astroceleste_engine/lib.rs
1//! astroceleste-engine: astrological chart calculation on JPL ephemerides.
2//!
3//! One implementation shared by the Astroceleste server (Python bindings), desktop and
4//! mobile apps (native) and the web app (WASM), so every platform computes identical charts.
5//!
6//! **Experimental (0.0.x):** the API may change in any release.
7//!
8//! ```no_run
9//! use astroceleste_engine::ephemeris::{Kernel, KernelSet, Spk};
10//! use astroceleste_engine::{calculate_chart, ChartRequest, UtcInstant};
11//!
12//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
13//! // Kernels in preference order: the first one covering a date is used.
14//! let mut kernels = KernelSet::new();
15//! kernels.push(Kernel::new("de440s.bsp", Spk::open("kernels/de440s.bsp")?)?);
16//!
17//! let mut request = ChartRequest::new(UtcInstant::parse("1987-05-17T14:30:00Z")?, 41.9, 12.5);
18//! request.house_system = "P";
19//! request.zodiac_type = "sidereal";
20//! request.ayanamsa = "lahiri";
21//!
22//! let chart = calculate_chart(&kernels, &request)?;
23//! println!("{}", serde_json::to_string_pretty(&chart)?);
24//! # Ok(())
25//! # }
26//! ```
27//!
28//! # Entry points
29//!
30//! | Function | Result |
31//! |---|---|
32//! | [`calculate_chart`] | a natal or event [`Chart`]: planets (the seven with their [`Condition`]), houses, aspects, fixed stars, lots, temperament, lunar status, sect, [`Reception`]s, antiscia, planetary hours |
33//! | [`calculate_horary_chart`] | the chart plus [`HoraryData`]: planetary hours, the Moon's aspects, strictures and the [`Judgment`] (significators, perfection, prohibition, translation, collection) |
34//! | [`calculate_transit_chart`] | the sky at a moment and place, with its [`CrossAspect`]s to natal planets |
35//! | [`calculate_synastry`] | cross-aspects between two charts' planets (no kernel needed) |
36//! | [`calculate_derived_chart`] | a stored chart turned to a new first house (no kernel needed) |
37//! | [`calculate_election_chart`] | the chart plus [`ElectionData`]: an electional score with the rules that apply |
38//! | [`search_elections`] | the best [`ElectionWindow`]s over a span of time at a place |
39//! | [`degree_qualities`] | Lilly's [`DegreeQualities`] of the degree a longitude falls in (no kernel needed) |
40//! | [`chart_dignities`] | a computed chart's [`Condition`]s and [`Reception`]s judged again under the Lilly or Dorothean scheme (no kernel needed) |
41//! | [`time_lords`] | the annual [`Profection`]s and [`Firdaria`] of a nativity over a span of time (no kernel needed) |
42//! | [`chinese_calendar`] | the [`ChineseCalendar`] around a moment: solar terms, lunar months, equation of time (also on a chart, with [`ChartRequest::chinese_calendar`]) |
43//! | [`bazi`] | the Four Pillars ([`Bazi`]) of a birth from its Chinese calendar: hidden stems, Ten Gods, NaYin, element balance, luck pillars (no kernel needed) |
44//! | [`zi_wei`] | the Zi Wei Dou Shu chart ([`ZiWei`]) of a birth from its Chinese calendar: palaces, bureau, the 14 major and 14 auxiliary stars with brightness, the Four Transformations, minor stars and cycles of gods, flying transformations, decade and small limits and a year's horoscope (no kernel needed) |
45//! | [`jyotish`] | the Vedic reading ([`Jyotish`]) of a chart: sidereal placements with their [`Nakshatra`]s, the divisional charts D1-D60 and the Vimshottari dasha (no kernel needed) |
46//! | [`vedic_calendar`] | the panchanga ([`VedicCalendar`]) of a moment and place: sunrise, vara, tithi, nakshatra, yoga, karana, lunar month, samvatsara (also on a chart, with [`ChartRequest::vedic_calendar`]) |
47//!
48//! Every result implements [`serde::Serialize`] and serializes to the JSON of the
49//! Astroceleste API, with the same key order and the same integer vs float types. The
50//! Python and WebAssembly bindings return exactly that JSON.
51//!
52//! # Loading kernels
53//!
54//! Positions come from NASA JPL SPK kernels (`de440s.bsp` covers 1849–2150; DE441 covers
55//! 13200 BC–17191). A [`KernelSet`](ephemeris::KernelSet) holds them in preference order,
56//! and each date is computed with the first kernel that covers it. [`Spk::open`] reads a
57//! file. Where there is no file system (WebAssembly) or the kernel is bundled with an app,
58//! load the bytes and use [`Spk::from_bytes`]. [`Spk::excerpt`] cuts a smaller kernel for a
59//! date range, with positions unchanged inside it (1950–2050 of DE440s is about 11 MB).
60//!
61//! ```no_run
62//! use astroceleste_engine::ephemeris::{Kernel, KernelSet, Spk};
63//!
64//! # fn download(_: &str) -> Vec<u8> { Vec::new() }
65//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
66//! let bytes: Vec<u8> = download("https://example.com/de440s-1950-2050.bsp");
67//! let mut kernels = KernelSet::new();
68//! kernels.push(Kernel::new("de440s-1950-2050.bsp", Spk::from_bytes(bytes)?)?);
69//! assert!(kernels.coverage().is_some());
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! # Errors
75//!
76//! Calculations return [`EngineError`], whose [`code`](EngineError::code) is the stable
77//! error code of the Astroceleste API. A date that no loaded kernel covers is
78//! [`EngineError::OutOfRange`]: the engine never extrapolates or approximates.
79//!
80//! ```no_run
81//! # use astroceleste_engine::ephemeris::KernelSet;
82//! # use astroceleste_engine::{calculate_chart, ChartRequest, EngineError, UtcInstant};
83//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
84//! # let kernels = KernelSet::new();
85//! let request = ChartRequest::new(UtcInstant::parse("1700-01-01T00:00:00Z")?, 41.9, 12.5);
86//! match calculate_chart(&kernels, &request) {
87//! Ok(chart) => println!("{} planets", chart.planets.len()),
88//! Err(EngineError::OutOfRange { jd, coverage }) => {
89//! eprintln!("JD {jd} is outside the loaded kernels ({coverage:?})")
90//! }
91//! Err(err) => eprintln!("{}: {err}", err.code()),
92//! }
93//! # Ok(())
94//! # }
95//! ```
96//!
97//! # Platforms
98//!
99//! The crate is pure Rust with no C code, and depends only on `serde` and `serde_json`.
100//! It builds for servers and desktops, `wasm32-unknown-unknown`, Android and iOS. Python
101//! (`pip install astroceleste-engine`) and JavaScript (`npm install astroceleste-engine`)
102//! bindings are published from the same repository.
103//!
104//! # Further reading
105//!
106//! - [API guide](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/api.md):
107//! request options (house systems, ayanamsas, orb settings) and the chart JSON
108//! - [Ephemerides](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/ephemerides.md):
109//! kernels, coverage and excerpts
110//! - [Accuracy](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/accuracy.md):
111//! how results are verified against the reference implementation
112//! - [Live demo](https://ffalcinelli.github.io/astroceleste-engine/): this crate compiled
113//! to WebAssembly, computing charts in your browser
114//!
115//! [`Spk::open`]: ephemeris::Spk::open
116//! [`Spk::from_bytes`]: ephemeris::Spk::from_bytes
117//! [`Spk::excerpt`]: ephemeris::Spk::excerpt
118
119mod almanac;
120mod aspects;
121mod catalog;
122mod chart;
123mod chinese;
124mod constants;
125mod degree_qualities;
126mod derived;
127mod dignities;
128mod election;
129pub mod ephemeris;
130mod error;
131mod fixed_stars;
132#[doc(hidden)] // exposed for the reduction tests; not part of the API
133pub mod frames;
134mod horary;
135mod houses;
136mod instant;
137mod judgment;
138mod lots;
139mod lunar;
140mod minor_bodies;
141mod planets;
142mod pyfloat;
143mod search;
144mod symbolic;
145mod temperament;
146#[cfg(test)]
147mod test_kernel;
148#[doc(hidden)] // exposed for the reduction tests; not part of the API
149pub mod time;
150mod time_lords;
151mod vedic;
152mod zodiac;
153
154pub use aspects::{Aspect, CrossAspect};
155pub use catalog::{DegreeRun, LunarMansion, SignDegrees};
156pub use chart::{calculate_chart, Chart, ChartRequest, HouseCusp, Placement};
157pub use chinese::bazi::{
158 bazi, Bazi, BaziFlow, BaziOptions, ElementBalance, HiddenStem, Luck, LuckPillar, LunarDate,
159 Pillar, TermMoment,
160};
161pub use chinese::calendar::{chinese_calendar, ChineseCalendar, LunarMonth, SolarTerm};
162pub use chinese::ziwei::{
163 zi_wei, AgeRange, Bureau, ZiWei, ZiWeiFlowStar, ZiWeiGod, ZiWeiGods, ZiWeiHoroscope,
164 ZiWeiOptions, ZiWeiPalace, ZiWeiPeriod, ZiWeiStar, ZiWeiTransformation,
165};
166pub use degree_qualities::{degree_qualities, degree_quality_table, DegreeQualities};
167pub use derived::{
168 calculate_derived_chart, calculate_synastry, calculate_transit_chart, Synastry, TransitChart,
169};
170pub use dignities::{
171 chart_dignities, AntisciaContact, ChartDignities, Condition, DignityLords, DignityScheme,
172 EssentialDignity, PlanetCondition, Reception,
173};
174pub use election::{
175 calculate_election_chart, search_elections, CriteriaSummary, ElectionChart, ElectionCriteria,
176 ElectionData, ElectionFactor, ElectionSearch, ElectionWindow, Exclusions, HourRange,
177 NatalPoint, Purpose, UtcOffset, MAX_SEARCH_DAYS,
178};
179pub use error::EngineError;
180pub use fixed_stars::FixedStarPosition;
181pub use horary::{
182 calculate_horary_chart, ApplyingAspect, HoraryChart, HoraryData, MoonStatus, PlanetaryHours,
183 SeparatingAspect, Stricture,
184};
185pub use instant::{ParseError, UtcInstant};
186pub use judgment::{Collection, Judgment, Perfection, Prohibition, Refranation, Translation};
187pub use lots::Lot;
188pub use lunar::LunarStatus;
189pub use minor_bodies::{is_minor_body, MINOR_BODIES};
190pub use temperament::{Factor, Qualities, Scores, Temperament};
191pub use time_lords::{time_lords, Firdaria, Period, Profection, TimeLords, MAX_TIME_LORDS_YEARS};
192pub use vedic::calendar::{vedic_calendar, Anga, Masa, Vara, VedicCalendar};
193pub use vedic::dasha::{DashaPeriod, Vimshottari, MAX_DASHA_DEPTH, MAX_DASHA_LEVELS};
194pub use vedic::varga::VARGAS;
195pub use vedic::{
196 jyotish, nakshatra, Graha, Jyotish, JyotishOptions, Nakshatra, Varga, VargaPlacement,
197 NAKSHATRAS,
198};