1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! astroceleste-engine: astrological chart calculation on JPL ephemerides.
//!
//! One implementation shared by the Astroceleste server (Python bindings), desktop and
//! mobile apps (native) and the web app (WASM), so every platform computes identical charts.
//!
//! **Experimental (0.0.x):** the API may change in any release.
//!
//! ```no_run
//! use astroceleste_engine::ephemeris::{Kernel, KernelSet, Spk};
//! use astroceleste_engine::{calculate_chart, ChartRequest, UtcInstant};
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Kernels in preference order: the first one covering a date is used.
//! let mut kernels = KernelSet::new();
//! kernels.push(Kernel::new("de440s.bsp", Spk::open("kernels/de440s.bsp")?)?);
//!
//! let mut request = ChartRequest::new(UtcInstant::parse("1987-05-17T14:30:00Z")?, 41.9, 12.5);
//! request.house_system = "P";
//! request.zodiac_type = "sidereal";
//! request.ayanamsa = "lahiri";
//!
//! let chart = calculate_chart(&kernels, &request)?;
//! println!("{}", serde_json::to_string_pretty(&chart)?);
//! # Ok(())
//! # }
//! ```
//!
//! # Entry points
//!
//! | Function | Result |
//! |---|---|
//! | [`calculate_chart`] | a natal or event [`Chart`]: planets, houses, aspects, fixed stars, lots, temperament, lunar status |
//! | [`calculate_horary_chart`] | the chart plus [`HoraryData`]: planetary hours, significators, the Moon's aspects, strictures |
//! | [`calculate_transit_chart`] | the sky at a moment and place, with its [`CrossAspect`]s to natal planets |
//! | [`calculate_synastry`] | cross-aspects between two charts' planets (no kernel needed) |
//! | [`calculate_derived_chart`] | a stored chart turned to a new first house (no kernel needed) |
//! | [`calculate_election_chart`] | the chart plus [`ElectionData`]: an electional score with the rules that apply |
//! | [`search_elections`] | the best [`ElectionWindow`]s over a span of time at a place |
//!
//! Every result implements [`serde::Serialize`] and serializes to the JSON of the
//! Astroceleste API, with the same key order and the same integer vs float types. The
//! Python and WebAssembly bindings return exactly that JSON.
//!
//! # Loading kernels
//!
//! Positions come from NASA JPL SPK kernels (`de440s.bsp` covers 1849–2150; DE441 covers
//! 13200 BC–17191). A [`KernelSet`](ephemeris::KernelSet) holds them in preference order,
//! and each date is computed with the first kernel that covers it. [`Spk::open`] reads a
//! file. Where there is no file system (WebAssembly) or the kernel is bundled with an app,
//! load the bytes and use [`Spk::from_bytes`]. [`Spk::excerpt`] cuts a smaller kernel for a
//! date range, with positions unchanged inside it (1950–2050 of DE440s is about 11 MB).
//!
//! ```no_run
//! use astroceleste_engine::ephemeris::{Kernel, KernelSet, Spk};
//!
//! # fn download(_: &str) -> Vec<u8> { Vec::new() }
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let bytes: Vec<u8> = download("https://example.com/de440s-1950-2050.bsp");
//! let mut kernels = KernelSet::new();
//! kernels.push(Kernel::new("de440s-1950-2050.bsp", Spk::from_bytes(bytes)?)?);
//! assert!(kernels.coverage().is_some());
//! # Ok(())
//! # }
//! ```
//!
//! # Errors
//!
//! Calculations return [`EngineError`], whose [`code`](EngineError::code) is the stable
//! error code of the Astroceleste API. A date that no loaded kernel covers is
//! [`EngineError::OutOfRange`]: the engine never extrapolates or approximates.
//!
//! ```no_run
//! # use astroceleste_engine::ephemeris::KernelSet;
//! # use astroceleste_engine::{calculate_chart, ChartRequest, EngineError, UtcInstant};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let kernels = KernelSet::new();
//! let request = ChartRequest::new(UtcInstant::parse("1700-01-01T00:00:00Z")?, 41.9, 12.5);
//! match calculate_chart(&kernels, &request) {
//! Ok(chart) => println!("{} planets", chart.planets.len()),
//! Err(EngineError::OutOfRange { jd, coverage }) => {
//! eprintln!("JD {jd} is outside the loaded kernels ({coverage:?})")
//! }
//! Err(err) => eprintln!("{}: {err}", err.code()),
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Platforms
//!
//! The crate is pure Rust with no C code, and depends only on `serde` and `serde_json`.
//! It builds for servers and desktops, `wasm32-unknown-unknown`, Android and iOS. Python
//! (`pip install astroceleste-engine`) and JavaScript (`npm install astroceleste-engine`)
//! bindings are published from the same repository.
//!
//! # Further reading
//!
//! - [API guide](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/api.md):
//! request options (house systems, ayanamsas, orb settings) and the chart JSON
//! - [Ephemerides](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/ephemerides.md):
//! kernels, coverage and excerpts
//! - [Accuracy](https://github.com/ffalcinelli/astroceleste-engine/blob/main/docs/accuracy.md):
//! how results are verified against the reference implementation
//! - [Live demo](https://ffalcinelli.github.io/astroceleste-engine/): this crate compiled
//! to WebAssembly, computing charts in your browser
//!
//! [`Spk::open`]: ephemeris::Spk::open
//! [`Spk::from_bytes`]: ephemeris::Spk::from_bytes
//! [`Spk::excerpt`]: ephemeris::Spk::excerpt
// exposed for the reduction tests; not part of the API
// exposed for the reduction tests; not part of the API
pub use ;
pub use LunarMansion;
pub use ;
pub use ;
pub use ;
pub use EngineError;
pub use FixedStarPosition;
pub use ;
pub use ;
pub use Lot;
pub use LunarStatus;
pub use ;