1#![forbid(unsafe_code)]
40
41use std::sync::OnceLock;
42
43use pleiades_backend::{CelestialBody, CustomBodyId};
44use pleiades_jpl::SnapshotEntry;
45
46mod accuracy_baseline;
47mod backend;
48mod coverage;
49mod data;
50mod lookup;
51mod regenerate;
52pub mod thresholds;
53
54pub use accuracy_baseline::{
55 accuracy_baseline_against, packaged_artifact_accuracy_baseline, BodyChannelError,
56};
57pub use backend::*;
58pub use coverage::*;
59pub use data::*;
60pub use lookup::*;
61pub use regenerate::*;
62
63#[cfg(test)]
66pub(crate) use coverage::{
67 channel_from_fit_samples_with_control_points, distance_channel_from_fit_samples,
68 distance_channel_from_samples, packaged_artifact_body_cadence,
69 packaged_artifact_fit_outlier_sample_fractions, packaged_artifact_fit_sample_fractions,
70 packaged_artifact_fit_sample_fractions_for_body, PackagedArtifactBodyCadence,
71};
72#[cfg(test)]
73pub(crate) use data::PACKAGED_ARTIFACT_FIXTURE;
74#[cfg(test)]
75pub(crate) use lookup::{
76 validate_packaged_artifact_access_summary_line, validate_packaged_artifact_storage_profile,
77 validate_packaged_artifact_storage_summary_line,
78 validate_packaged_frame_treatment_summary_line,
79};
80#[cfg(test)]
81pub(crate) use regenerate::{
82 best_residual_segment,
83 body_segment_span_limit,
85 chebyshev_lobatto_fractions,
86 coordinates,
87 evaluate_polynomial_channel,
88 packaged_artifact_fit_sample_counts_for_body,
89 packaged_artifact_residual_sample_fractions_for_channel,
90 packaged_artifact_segment_validation_fractions_for_body,
91 packaged_artifact_split_fraction_for_interval,
92 segment_channel_value,
93 segment_error_prefers_candidate,
94 segment_fit_candidate_is_better,
95 segment_from_pair,
96 segment_from_pair_fallback,
97 snapshot_entry_from_ecliptic_coordinates,
98 validate_packaged_artifact_phase1_source_inputs,
99 PackagedArtifactFitCandidateScore,
100 PackagedArtifactSegmentFitError,
101 PackagedArtifactSplitCurvature,
103 PACKAGED_ARTIFACT_DENSE_FIT_SAMPLE_COUNTS,
104 PACKAGED_ARTIFACT_DENSE_RESIDUAL_SAMPLE_FRACTIONS,
105 PACKAGED_ARTIFACT_DENSE_VALIDATION_SAMPLE_FRACTIONS,
106 PACKAGED_ARTIFACT_FOUR_FIFTHS_SPLIT_FRACTION,
107 PACKAGED_ARTIFACT_LEFT_BIASED_SPLIT_FRACTION,
109 PACKAGED_ARTIFACT_LEFT_EXTREME_SPLIT_FRACTION,
110 PACKAGED_ARTIFACT_MEDIUM_FIT_SAMPLE_COUNTS,
111 PACKAGED_ARTIFACT_MEDIUM_VALIDATION_SAMPLE_FRACTIONS,
112 PACKAGED_ARTIFACT_ONE_EIGHTH_SPLIT_FRACTION,
113 PACKAGED_ARTIFACT_ONE_FIFTH_SPLIT_FRACTION,
114 PACKAGED_ARTIFACT_ONE_NINTH_SPLIT_FRACTION,
115 PACKAGED_ARTIFACT_ONE_SEVENTH_SPLIT_FRACTION,
116 PACKAGED_ARTIFACT_ONE_THIRD_SPLIT_FRACTION,
117 PACKAGED_ARTIFACT_RESIDUAL_SAMPLE_FRACTIONS,
118 PACKAGED_ARTIFACT_RIGHT_BIASED_SPLIT_FRACTION,
119 PACKAGED_ARTIFACT_RIGHT_EXTREME_SPLIT_FRACTION,
120 PACKAGED_ARTIFACT_SEVEN_EIGHTHS_SPLIT_FRACTION,
121 PACKAGED_ARTIFACT_SIX_SEVENTHS_SPLIT_FRACTION,
122};
123#[cfg(test)]
125pub(crate) use pleiades_backend::{
126 Apparentness, BackendFamily, CoordinateFrame, EclipticCoordinates, EphemerisBackend,
127 EphemerisErrorKind, EphemerisRequest, Instant, JulianDay, QualityAnnotation, TimeRange,
128 TimeScale, ZodiacMode,
129};
130#[cfg(test)]
131pub(crate) use pleiades_compression::{
132 ArtifactOutput, ArtifactProfile, ChannelKind, CompressedArtifact, EndianPolicy,
133 PolynomialChannel, Segment, SpeedPolicy,
134};
135#[cfg(test)]
136pub(crate) use pleiades_jpl::{
137 production_generation_source_summary_for_report, production_holdout_corpus, reference_snapshot,
138 JplSnapshotBackend,
139};
140
141const PACKAGE_NAME: &str = "pleiades-data";
142const ARTIFACT_LABEL: &str = "stage-5 packaged-data draft";
143const ARTIFACT_PROFILE_ID: &str = "pleiades-packaged-artifact-profile/stage-5-draft";
144const PACKAGED_ARTIFACT_GENERATION_STRATEGY_TAIL: &str = "with 8-point and 10-point Chebyshev-Lobatto baseline candidates before the dense body-specific ladders and 12-point and 14-point candidates for inner and outer planets before fallback, with 10-point, 12-point, 14-point, 16-point, 18-point, and 20-point options for luminaries, lunar points, Pluto, selected asteroids, and custom bodies, and the best dense candidate wins before fallback, with equal-error, equal-sample-count ties preferring the simpler segment, residual correction channels on high-curvature spans when they improve the fit, residual-channel combinations and remaining channel-order permutations when composing those channels, preferring the smaller residual footprint on equal-error ties, higher-order reconstruction from fit samples when it quantizes cleanly, shared four-point control-point fallback across longitude, latitude, and distance channels when the higher-order fit does not quantize cleanly, quarter-biased splits on very long dense-body spans when quarter-point curvature is strongly asymmetric, a dense quarter-point control-point lattice before exact-third fallback on irregular spans, one-sixth and five-sixth probe fractions on very long dense-body spans when quarter-point curvature stays balanced, one-third and two-thirds probe fractions on long dense-body spans when quarter-point curvature stays balanced, a dense five-point fallback on the longest dense-body spans when one-fifth through four-fifth samples fit cleanly, a dense seven-point fallback on super-extreme dense-body spans when one-seventh through six-sevenths samples fit cleanly, one-ninth and eight-ninths probe fractions on super-extreme dense-body spans when the finer probes stay balanced, one-eighth and seven-eighths probe fractions on super-extreme dense-body spans when the ninth-point probes stay balanced, one-seventh and six-sevenths probe fractions on extreme dense-body spans when the super-extreme probes stay balanced, one-fifth and four-fifth probe fractions on the longest dense-body spans when the coarser probes stay balanced, and quadratic fallback otherwise";
145
146pub(crate) fn packaged_artifact_generation_policy_note_text() -> &'static str {
147 static NOTE: OnceLock<String> = OnceLock::new();
148 NOTE.get_or_init(|| {
149 format!(
150 "bodies with a single sampled epoch use point segments; bodies with two or more sampled epochs are recursively subdivided into quadratic windows using body-class span caps and measured-fit comparison against the fallback, {}",
151 PACKAGED_ARTIFACT_GENERATION_STRATEGY_TAIL
152 )
153 })
154 .as_str()
155}
156
157pub(crate) fn packaged_artifact_source_text() -> &'static str {
158 static SOURCE: OnceLock<String> = OnceLock::new();
159 SOURCE.get_or_init(|| {
160 format!(
161 "Quantized adjacent same-body quadratic windows with longitude-unwrapped planetary fits, with the comparison-body planetary set densely fit from the JPL de440 kernel over the default 1900-2100 coverage window and the constrained asteroid:433-Eros sourced from its committed reference corpus, with point segments only for single-epoch bodies and recursively subdivided quadratic spans for multi-epoch bodies using body-class span caps and measured-fit comparison against the fallback, {}.",
162 PACKAGED_ARTIFACT_GENERATION_STRATEGY_TAIL
163 )
164 })
165 .as_str()
166}
167
168const PACKAGED_BASE_BODIES: [CelestialBody; 10] = [
169 CelestialBody::Sun,
170 CelestialBody::Moon,
171 CelestialBody::Mercury,
172 CelestialBody::Venus,
173 CelestialBody::Mars,
174 CelestialBody::Jupiter,
175 CelestialBody::Saturn,
176 CelestialBody::Uranus,
177 CelestialBody::Neptune,
178 CelestialBody::Pluto,
179];
180
181const PACKAGED_REFERENCE_EPOCH_JD: f64 = 2_451_545.0;
182
183pub(crate) fn packaged_bodies() -> &'static [CelestialBody] {
184 static BODIES: OnceLock<Vec<CelestialBody>> = OnceLock::new();
185 BODIES.get_or_init(|| {
186 let mut bodies = PACKAGED_BASE_BODIES.to_vec();
187 bodies.push(CelestialBody::Custom(CustomBodyId::new(
188 "asteroid", "433-Eros",
189 )));
190 bodies
191 })
192}
193
194pub fn packaged_body_claims() -> Vec<pleiades_backend::BodyClaim> {
197 use pleiades_backend::{AccuracyClass, BodyClaim, ClaimEvidence};
198 packaged_bodies()
199 .iter()
200 .cloned()
201 .map(|body| {
202 BodyClaim::release_grade(body, AccuracyClass::High, ClaimEvidence::ArtifactValidated)
203 })
204 .collect()
205}
206
207pub fn apsis_body_claims() -> Vec<pleiades_backend::BodyClaim> {
212 use pleiades_backend::{AccuracyClass, BodyClaim, ClaimEvidence};
213 let source = "Swiss Ephemeris 2.10.03 SE_OSCU_APOG (validate-lilith)".to_string();
214 vec![
215 BodyClaim::release_grade(
216 CelestialBody::TrueApogee,
217 AccuracyClass::High,
218 ClaimEvidence::CorpusValidated {
219 source: source.clone(),
220 },
221 ),
222 BodyClaim::release_grade(
223 CelestialBody::TruePerigee,
224 AccuracyClass::High,
225 ClaimEvidence::CorpusValidated { source },
226 ),
227 ]
228}
229
230pub fn true_node_body_claims() -> Vec<pleiades_backend::BodyClaim> {
237 use pleiades_backend::{AccuracyClass, BodyClaim, ClaimEvidence};
238 vec![BodyClaim::release_grade(
239 CelestialBody::TrueNode,
240 AccuracyClass::High,
241 ClaimEvidence::CorpusValidated {
242 source: "Swiss Ephemeris 2.10.03 SE_TRUE_NODE (validate-true-node)".to_string(),
243 },
244 )]
245}
246
247pub(crate) fn packaged_reference_entry_for_body(
248 snapshot: &[SnapshotEntry],
249 body: &CelestialBody,
250) -> Option<SnapshotEntry> {
251 snapshot
252 .iter()
253 .find(|entry| {
254 entry.body == *body
255 && (entry.epoch.julian_day.days() - PACKAGED_REFERENCE_EPOCH_JD).abs()
256 < f64::EPSILON
257 })
258 .cloned()
259 .or_else(|| snapshot.iter().find(|entry| entry.body == *body).cloned())
260}
261
262pub(crate) const AU_IN_KM: f64 = 149_597_870.7;
263
264pub const fn package_name() -> &'static str {
266 PACKAGE_NAME
267}
268
269#[cfg(test)]
270mod test_support;
271#[cfg(test)]
272mod tests;