1#![forbid(unsafe_code)]
41
42use std::sync::OnceLock;
43
44use pleiades_backend::{CelestialBody, CustomBodyId};
45use pleiades_jpl::SnapshotEntry;
46
47mod accuracy_baseline;
48mod backend;
49mod coverage;
50mod data;
51mod lookup;
52mod regenerate;
53pub mod thresholds;
54
55pub use accuracy_baseline::{
56 accuracy_baseline_against, eros_self_consistency_max_longitude_arcsec,
57 packaged_artifact_accuracy_baseline, packaged_artifact_accuracy_baseline_summary_for_report,
58 BodyChannelError,
59};
60pub use backend::*;
61pub use coverage::*;
62pub use data::*;
63pub use lookup::*;
64pub use regenerate::*;
65pub use thresholds::packaged_artifact_thresholds_summary_for_report;
66
67#[cfg(test)]
70pub(crate) use coverage::{
71 channel_from_fit_samples_with_control_points, distance_channel_from_fit_samples,
72 distance_channel_from_samples, packaged_artifact_body_cadence,
73 packaged_artifact_fit_outlier_sample_fractions, packaged_artifact_fit_sample_fractions,
74 packaged_artifact_fit_sample_fractions_for_body, PackagedArtifactBodyCadence,
75};
76#[cfg(test)]
77pub(crate) use data::PACKAGED_ARTIFACT_FIXTURE;
78#[cfg(test)]
79pub(crate) use lookup::{
80 validate_packaged_artifact_access_summary_line, validate_packaged_artifact_storage_profile,
81 validate_packaged_artifact_storage_summary_line,
82 validate_packaged_frame_treatment_summary_line,
83};
84#[cfg(test)]
85pub(crate) use regenerate::{
86 best_residual_segment,
87 body_segment_span_limit,
89 chebyshev_lobatto_fractions,
90 coordinates,
91 evaluate_polynomial_channel,
92 packaged_artifact_fit_sample_counts_for_body,
93 packaged_artifact_residual_sample_fractions_for_channel,
94 packaged_artifact_segment_validation_fractions_for_body,
95 packaged_artifact_split_fraction_for_interval,
96 segment_channel_value,
97 segment_error_prefers_candidate,
98 segment_fit_candidate_is_better,
99 segment_from_pair,
100 segment_from_pair_fallback,
101 snapshot_entry_from_ecliptic_coordinates,
102 validate_packaged_artifact_phase1_source_inputs,
103 PackagedArtifactFitCandidateScore,
104 PackagedArtifactSegmentFitError,
105 PackagedArtifactSplitCurvature,
107 PACKAGED_ARTIFACT_DENSE_FIT_SAMPLE_COUNTS,
108 PACKAGED_ARTIFACT_DENSE_RESIDUAL_SAMPLE_FRACTIONS,
109 PACKAGED_ARTIFACT_DENSE_VALIDATION_SAMPLE_FRACTIONS,
110 PACKAGED_ARTIFACT_FOUR_FIFTHS_SPLIT_FRACTION,
111 PACKAGED_ARTIFACT_LEFT_BIASED_SPLIT_FRACTION,
113 PACKAGED_ARTIFACT_LEFT_EXTREME_SPLIT_FRACTION,
114 PACKAGED_ARTIFACT_MEDIUM_FIT_SAMPLE_COUNTS,
115 PACKAGED_ARTIFACT_MEDIUM_VALIDATION_SAMPLE_FRACTIONS,
116 PACKAGED_ARTIFACT_ONE_EIGHTH_SPLIT_FRACTION,
117 PACKAGED_ARTIFACT_ONE_FIFTH_SPLIT_FRACTION,
118 PACKAGED_ARTIFACT_ONE_NINTH_SPLIT_FRACTION,
119 PACKAGED_ARTIFACT_ONE_SEVENTH_SPLIT_FRACTION,
120 PACKAGED_ARTIFACT_ONE_THIRD_SPLIT_FRACTION,
121 PACKAGED_ARTIFACT_RESIDUAL_SAMPLE_FRACTIONS,
122 PACKAGED_ARTIFACT_RIGHT_BIASED_SPLIT_FRACTION,
123 PACKAGED_ARTIFACT_RIGHT_EXTREME_SPLIT_FRACTION,
124 PACKAGED_ARTIFACT_SEVEN_EIGHTHS_SPLIT_FRACTION,
125 PACKAGED_ARTIFACT_SIX_SEVENTHS_SPLIT_FRACTION,
126};
127#[cfg(test)]
129pub(crate) use pleiades_backend::{
130 Apparentness, BackendFamily, CoordinateFrame, EclipticCoordinates, EphemerisBackend,
131 EphemerisErrorKind, EphemerisRequest, Instant, JulianDay, QualityAnnotation, TimeRange,
132 TimeScale, ZodiacMode,
133};
134#[cfg(test)]
135pub(crate) use pleiades_compression::{
136 ArtifactOutput, ArtifactProfile, ChannelKind, CompressedArtifact, EndianPolicy,
137 PolynomialChannel, Segment, SpeedPolicy,
138};
139#[cfg(test)]
140pub(crate) use pleiades_jpl::{
141 production_generation_source_summary_for_report, production_holdout_corpus, reference_snapshot,
142 JplSnapshotBackend,
143};
144
145const PACKAGE_NAME: &str = "pleiades-data";
146const ARTIFACT_LABEL: &str = "stage-5 packaged-data draft";
147const ARTIFACT_PROFILE_ID: &str = "pleiades-packaged-artifact-profile/stage-5-draft";
148const 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";
149
150pub(crate) fn packaged_artifact_generation_policy_note_text() -> &'static str {
151 static NOTE: OnceLock<String> = OnceLock::new();
152 NOTE.get_or_init(|| {
153 format!(
154 "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, {}",
155 PACKAGED_ARTIFACT_GENERATION_STRATEGY_TAIL
156 )
157 })
158 .as_str()
159}
160
161pub(crate) fn packaged_artifact_source_text() -> &'static str {
162 static SOURCE: OnceLock<String> = OnceLock::new();
163 SOURCE.get_or_init(|| {
164 format!(
165 "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, {}.",
166 PACKAGED_ARTIFACT_GENERATION_STRATEGY_TAIL
167 )
168 })
169 .as_str()
170}
171
172const PACKAGED_BASE_BODIES: [CelestialBody; 10] = [
173 CelestialBody::Sun,
174 CelestialBody::Moon,
175 CelestialBody::Mercury,
176 CelestialBody::Venus,
177 CelestialBody::Mars,
178 CelestialBody::Jupiter,
179 CelestialBody::Saturn,
180 CelestialBody::Uranus,
181 CelestialBody::Neptune,
182 CelestialBody::Pluto,
183];
184
185const PACKAGED_REFERENCE_EPOCH_JD: f64 = 2_451_545.0;
186
187pub(crate) fn packaged_bodies() -> &'static [CelestialBody] {
188 static BODIES: OnceLock<Vec<CelestialBody>> = OnceLock::new();
189 BODIES.get_or_init(|| {
190 let mut bodies = PACKAGED_BASE_BODIES.to_vec();
191 bodies.push(CelestialBody::Custom(CustomBodyId::new(
192 "asteroid", "433-Eros",
193 )));
194 bodies
195 })
196}
197
198pub fn packaged_body_claims() -> Vec<pleiades_backend::BodyClaim> {
201 use pleiades_backend::{AccuracyClass, BodyClaim, ClaimEvidence};
202 packaged_bodies()
203 .iter()
204 .cloned()
205 .map(|body| {
206 BodyClaim::release_grade(body, AccuracyClass::High, ClaimEvidence::ArtifactValidated)
207 })
208 .collect()
209}
210
211pub fn apsis_body_claims() -> Vec<pleiades_backend::BodyClaim> {
216 use pleiades_backend::{AccuracyClass, BodyClaim, ClaimEvidence};
217 let source = "Swiss Ephemeris 2.10.03 SE_OSCU_APOG (validate-lilith)".to_string();
218 vec![
219 BodyClaim::release_grade(
220 CelestialBody::TrueApogee,
221 AccuracyClass::High,
222 ClaimEvidence::CorpusValidated {
223 source: source.clone(),
224 },
225 ),
226 BodyClaim::release_grade(
227 CelestialBody::TruePerigee,
228 AccuracyClass::High,
229 ClaimEvidence::CorpusValidated { source },
230 ),
231 ]
232}
233
234pub(crate) fn packaged_reference_entry_for_body(
235 snapshot: &[SnapshotEntry],
236 body: &CelestialBody,
237) -> Option<SnapshotEntry> {
238 snapshot
239 .iter()
240 .find(|entry| {
241 entry.body == *body
242 && (entry.epoch.julian_day.days() - PACKAGED_REFERENCE_EPOCH_JD).abs()
243 < f64::EPSILON
244 })
245 .cloned()
246 .or_else(|| snapshot.iter().find(|entry| entry.body == *body).cloned())
247}
248
249pub(crate) const AU_IN_KM: f64 = 149_597_870.7;
250
251pub const fn package_name() -> &'static str {
253 PACKAGE_NAME
254}
255
256#[cfg(test)]
257mod test_support;
258#[cfg(test)]
259mod tests;