Skip to content

fix: normalize ephemeris reference frames at backend boundaries - #127

Merged
VPRamon merged 7 commits into
mainfrom
fix/ephemeris-reference-frame-normalization
Oct 31, 2026
Merged

VPRamon merged 7 commits into
mainfrom
fix/ephemeris-reference-frame-normalization

Conversation

@VPRamon

@VPRamon VPRamon commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Summary

Prepare Siderust v0.18.0 with backend-independent physical frame semantics: the same Position<Center, Frame, Unit> describes the same axes and origin regardless of how its numbers were calculated. Fixes #126.

Scientific root cause

Raw VSOP87A/E and post-Laskar ELP82B positions were labeled as the IAU 2006 canonical ecliptic without completing their native-frame ties. Their published native→FK5 matrices differ. Modern DE SPICE J2000 data are ICRF-realized, not classical FK5/EME2000.

Preserve the verified chains: C * H * V for VSOP and C * H * L after the existing Laskar step for ELP. C is SOFA/ERFA ecm06 at J2000; H is the fixed J2000 fk5hip orientation, without catalogue spin. No matrices were refitted or inferred from DE residuals. Positions and analytical velocities use the same static rotation. ELP now evaluates its documented TDB argument from TT input.

References: IMCCE VSOP conventions, Bretagnon & Francou 1988, ELP82B notice §§8–9, ERFA ecm06/fk5hip, and NAIF frame conventions/lunar frames.

Implementation

  • Normalize once at public analytical output boundaries; retain the existing ICRF→canonical JPL path and shared bias matrix.
  • Make CenterShiftProvider::try_shift the implementation path. A narrow try_to_center_with entry point handles standard-center positions; reverse/composed shifts propagate CenterShiftError and its original cause. Existing infallible transforms delegate to that path. Explicit nutation selection is preserved and regression-tested.
  • Planetary and Pluto shifts request the selected backend's fallible capability. Missing data return errors without VSOP/catalogue substitutions. DynEphemeris and PlanetState keep their existing scope.
  • ICRF-only SPK loaders strictly reject any unsupported frame or type, including unrelated segments, consistently for bytes and parsed input. Failed loads do not mutate a kernel set. Later covering segments win; invalid selected records or unavailable center chains return errors instead of exposing stale segments. The general chain path replaces a shortcut that could bypass selected Earth/target data. Runtime DE's fixed fundamental chains reject alternate centers explicitly. Legacy parse_bsp rejects duplicate fundamental targets that its single-segment result cannot represent; use the stacked loaders for overlapping coverage.
  • Give analytical IAU_MOON its distinct MoonIauMeanEarth identity. MoonPrincipalAxes has no invented analytical orientation.

API and compatibility

Breaking changes for 0.18.0:

  • Custom center providers implement try_shift -> Result. shift, to_center* and combined to* conveniences remain documented panic-on-error APIs; use try_shift/try_to_center_with to handle capability and coverage failures.
  • Pluto backends implement try_pluto_barycentric; overriding only the infallible wrapper does not supply a transform. Unsupported Pluto is identified by physical center ID 999.
  • Migrate analytical lunar coordinates/transforms to MoonIauMeanEarth. Actual principal-axes data require appropriate binary PCK/FK data and must not be relabeled.
  • ICRF-only loaders reject mixed/unsupported kernels rather than skipping incompatible segments. Checked constructors are provided; retained infallible constructors document their panic behavior.

Analytical numeric outputs change; downstream snapshots need updating. Coordinate/FFI layouts remain unchanged, and the FFI version reports 1800. Serde still stores components/center parameters without a frame discriminator: callers must preserve provenance. Python companion #21 handles the string alias; it must adopt the released 0.18 core separately to inherit normalization. No external repository was modified in this finalization.

Scientific validation

Permanent Cargo tests retain independent IMCCE A/E states (four bodies, multiple epochs), original-coefficient span-endpoint evaluations, all five published ELP Table H vectors, independent ERFA basis/handedness checks, position/velocity tests and lunar compile-fail checks. Expected values were not regenerated from Siderust. Fixture metadata records authoritative sources, units, time conventions and precision.

Center-matched comparisons only: Earth is 399 relative to Sun 10; Moon is 301 relative to Earth 399. Earlier Mercury/Mars/Neptune residuals against system-barycenter IDs 1/4/8 are removed from the conclusions. No missing planet-center offset is silently replaced with a system barycenter.

Illustrative J2000 orientation-only effects, holding epoch arguments fixed:

Matched quantity Before residual vs DE440s Orientation displacement Residual after orientation
Earth position 49.74 km 78.01 km (0.10939″) 29.27 km
Earth velocity 0.009585 m/s 0.016275 m/s 0.006760 m/s
Moon position ≈234 m ≈216 m (0.11063″) ≈68 m

ELP's separate TT→TDB effect is about 0.08 m at J2000. These are model residuals, not accuracy guarantees. The empirical historical/FK5 tie retains uncertainty; it is not an exact recovered DE200→DE440 alignment. The coarse element catalogue/abbreviated Pluto series still lack a precision realization tie, and arbitrary frame-ID-1 provenance must come from the producer.

An explicitly ignored external-kernel test checks Sun/Earth/Moon states and Earth velocity at 1900/J2000/2026/2100 against CSPICE N0067 plus independent ERFA: 1 mm position and 1e-7 km/day velocity tolerances. It has been executed successfully with the official DE440s kernel. Normal scientific reference tests need only Cargo.

Tests and CI

  • cargo fmt --all -- --check: passed.
  • cargo clippy --workspace --all-features --all-targets -- -D warnings: passed.
  • cargo test --workspace --all-features: 2,055 unit/integration tests + 322 doctests passed, zero failures; two external-kernel tests, one hash-printing developer helper and 33 documentation examples ignored as declared. The DE440s test passed separately; the unchanged lunar external parity test requires its five original NAIF kernels.
  • cargo test --workspace --doc --all-features: 322 passed, zero failures, 33 declared ignores.
  • Host no-default build; embedded thumbv7em-none-eabihf no-default and serde,atmosphere checks: passed.
  • Strict all-feature rustdoc links/HTML: passed.
  • Focused capability, explicit-nutation, reference-vector and SPK policy/precedence tests: passed.
  • Explicit external DE440s CSPICE/ERFA test: passed.
  • Release preflight: passed; both release crates and changelog agree on 0.18.0.
  • GitHub Actions on cdae832: 14 successful checks, including the full feature matrix, doctests, embedded no_std checks and coverage gates. Siderust CI and release metadata validation passed; publication and GitHub Release jobs were correctly skipped on this PR.

Repository cleanup

Remove the validation-only Rust example, auxiliary Python generator, comparison CSV and standalone report. Consolidate permanent conventions in Frames and centers; retain the Rust tests and independent fixtures with their provenance. Correct outdated Keplerian/backend, IAU 1976/1980, GCRS and lunar-alias descriptions. No new dependencies or academic-paper changes.

Release preparation

Root and FFI manifests, internal dependency, lockfile, ABI version, citation metadata, installation snippets and the undated changelog entry are prepared for v0.18.0. No tag, merge, publication or release has been created.

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Code Coverage

Package Line Rate Complexity Health
src.targets 99% 0 ✔
src.bodies.aircraft 100% 0 ✔
src.formats.ilrs 91% 0 ✔
src.healpix 86% 0 ✔
src.event.solar 95% 0 ✔
src.ephemeris.jpl 95% 0 ✔
src.coordinates 97% 0 ✔
src.astro.dynamics.forces 87% 0 ✔
src.coordinates.transform.providers 90% 0 ✔
src.formats.vlbi 100% 0 ✔
src.pod.propagation 94% 0 ✔
src.mission.geometry 91% 0 ✔
src.astro.sgp4 99% 0 ✔
src.spice 84% 0 ✔
src.pod.qc 93% 0 ✔
src.coordinates.transform 93% 0 ✔
src.pod.product 94% 0 ✔
src.event.search 95% 0 ✔
src.pod.force 94% 0 ✔
src.catalogs 92% 0 ✔
src.formats.tle.omm 94% 0 ✔
src.formats.ccsds 89% 0 ✔
src.pod.process 93% 0 ✔
src.astro 91% 0 ✔
src.coordinates.observation 97% 0 ✔
src.ephemeris.vsop87 100% 0 ✔
src.event.lunar 95% 0 ✔
src.atmosphere 99% 0 ✔
src.pod.run 100% 0 ✔
src.astro.nutation 94% 0 ✔
src.bodies 94% 0 ✔
src.formats.tle 93% 0 ✔
src.astro.dynamics 95% 0 ✔
src.coordinates.transform.centers.position 77% 0 ✔
src.event.stellar 97% 0 ✔
src.coordinates.transform.centers 100% 0 ✔
src.formats.igs 90% 0 ✔
src.starlight 100% 0 ✔
src.event.planetary 93% 0 ✔
src.event 93% 0 ✔
src 86% 0 ✔
src.ephemeris 89% 0 ✔
src.formats.iers 94% 0 ✔
src.astro.dynamics.propagation 74% 0 ➖
src.coordinates.transform.frames 97% 0 ✔
src.mission 89% 0 ✔
src.pod.observation 86% 0 ✔
src.pod.estimation 89% 0 ✔
src.ephemeris.lagrange 85% 0 ✔
src.photometry.passbands 100% 0 ✔
src.astro.dynamics.gravity 72% 0 ➖
src.event.azimuth 94% 0 ✔
src.formats.spice 83% 0 ✔
src.ephemeris.elp2000 100% 0 ✔
src.formats.rinex 79% 0 ✔
src.formats 91% 0 ✔
src.pod 100% 0 ✔
src.coordinates.surface 97% 0 ✔
src.pod.spice 88% 0 ✔
src.pod.problem 95% 0 ✔
src.catalogs.gaia 90% 0 ✔
src.event.altitude 90% 0 ✔
src.formats.adsb 85% 0 ✔
Summary 92% (36570 / 39911) 0 ✔

@VPRamon
VPRamon merged commit f110c81 into main Oct 31, 2026
16 checks passed
@VPRamon
VPRamon deleted the fix/ephemeris-reference-frame-normalization branch October 31, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.16.0 analytical ephemerides share EclipticMeanJ2000 without a native realization tie

1 participant