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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! # empyrean
//!
//! High-precision Solar System dynamics — trajectory propagation,
//! ephemeris generation, orbit determination, and event analysis
//! (close approaches, occultations, eclipses, sphere-of-influence
//! crossings) for real Solar System bodies.
//!
//! Safe Rust wrapper over `empyrean-sys`. Use this crate to propagate
//! orbits, generate ephemerides, and determine orbits from observations
//! without writing `unsafe` code.
//!
//! ## Standard workflow
//!
//! Pull an orbit from JPL SBDB, propagate it forward, generate
//! ephemerides at an observatory, and inspect detected events:
//!
//! ```no_run
//! use empyrean::{Context, EphemerisConfig, Origin, PropagationConfig};
//!
//! let ctx = Context::from_data_dir(None)?;
//!
//! // 1. Pull Apophis from SBDB (CometaryCoordinates with covariance).
//! let batch = empyrean::query_sbdb(&["99942"], None)?;
//!
//! // 2. Propagate 10 years past the SBDB epoch.
//! let cfg = PropagationConfig::default();
//! let t0 = batch.orbits[0].state.epoch.mjd_tdb()?;
//! let epochs = vec![empyrean::Epoch::from_mjd_tdb(t0 + 10.0 * 365.25)];
//! let result = ctx.propagate(&batch.orbits, &epochs, &cfg)?;
//! println!("{} states, {} events", result.states.len(), result.events.len());
//!
//! // 3. Predict on-sky positions at Mauna Kea (MPC code 568).
//! let observers = ctx.get_observers(&["568"], &epochs)?;
//! let eph_cfg = EphemerisConfig::default();
//! let eph = ctx.generate_ephemeris(&batch.orbits, &observers, &eph_cfg)?;
//! # Ok::<(), empyrean::Error>(())
//! ```
//!
//! For close-approach analysis (impact probability, B-plane geometry),
//! see [`Context::compute_impact_probabilities`] and
//! [`Context::compute_b_planes`]. For OD from astrometric
//! observations, see [`Context::determine`] and the [`Session`] type
//! for interactive masking workflows.
//!
//! ## Coordinate transform
//!
//! ```no_run
//! use empyrean::{Context, CoordinateState, Frame, Origin, Representation};
//!
//! let ctx = Context::from_data_dir(None)?;
//! let input = CoordinateState::cometary(
//! empyrean::Epoch::from_mjd_tdb(60200.0),
//! [0.7461, 0.1914, 3.339, 204.446, 126.687, 60159.0],
//! Frame::EclipticJ2000,
//! Origin::SUN,
//! );
//! let cart = ctx.transform(&input, Representation::Cartesian, Frame::ICRF, Origin::SUN)?;
//! println!("x = {:.6} AU", cart.elements[0]);
//! # Ok::<(), empyrean::Error>(())
//! ```
//!
//! ## Quick reference
//!
//! | You want… | API |
//! |----------------------------------------|--------------------------------------------------|
//! | Propagate orbits to target epochs | [`Context::propagate`] |
//! | Predict observations at observatories | [`Context::generate_ephemeris`] |
//! | Fit an orbit to observations | [`Context::determine`] |
//! | Re-fit with a Bayesian prior | [`Context::refine`] |
//! | Residuals only — no fit | [`Context::evaluate`] |
//! | Stateful, mask-and-refit OD | [`Session`] |
//! | Impact probability | [`Context::compute_impact_probabilities`] |
//! | B-plane geometry | [`Context::compute_b_planes`] |
//! | Convert between coordinate types | [`Context::transform`] |
//! | Body / observer states | [`Context::get_states`] / [`Context::get_observers`] |
//! | Pull an orbit from JPL SBDB | [`query_sbdb`] |
//! | Pull predicted ephemeris from Horizons | [`query_horizons`] |
//! | Pull SSB state vectors from Horizons | [`query_horizons_vectors`] |
//! | Pull observations from MPC | [`query_observations`] |
//! | Pull radar astrometry from JPL | [`query_radar`] |
//! | Read ADES PSV observations | [`Context::read_ades`] |
//! | Default data directory | [`default_data_dir`] |
//!
//! ## Conventions
//!
//! - **Distances** in AU; **velocities** in AU/day; **angles** in
//! **degrees** at the API boundary (radians internally).
//! - **Epochs** are MJD on the **TDB** scale unless otherwise stated.
//! See [`time::Epoch`] for time-scale-aware values and
//! [`time::iso_to_mjd`] / [`time::mjd_to_iso`] for ISO 8601 interop.
//! - **Default integrator** is
//! [`IntegratorChoice::GR15`] (median Horizons error ≈ 35 m).
//! Switch to [`IntegratorChoice::DOP853`] via
//! [`AdvancedIntegratorConfig::integrator`] for ~1.4× speed at the
//! cost of ~10× position error.
//! - **Default frame** for propagation output is
//! [`Frame::EclipticJ2000`] (the integration frame); set
//! [`PropagationConfig::frame`] to [`Frame::ICRF`] for ICRF output.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use Observer;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use State;
pub use ;
pub use ;
pub use ;
// Compile the Rust examples in both READMEs as doctests under
// `cargo test --doc` so they cannot rot against the public API. The
// `cfg(doctest)` gate keeps these synthetic structs out of the public
// rustdoc — they exist only during doc-test compilation. `../README.md`
// is the crate (crates.io) README; `../../README.md` is the top-level
// workspace README.
;
;