Skip to content

Add real-data validation against JPL Horizons#330

Open
matthewholman wants to merge 1 commit into
mainfrom
feat/real-data-validation
Open

Add real-data validation against JPL Horizons#330
matthewholman wants to merge 1 commit into
mainfrom
feat/real-data-validation

Conversation

@matthewholman
Copy link
Copy Markdown
Collaborator

This branch was developed with Claude Code, on top of an initial
implementation I had started.

Adds an end-to-end validation test that runs orbitfit on real MPC
astrometry and asserts the recovered Cartesian state matches the
JPL Horizons reference at the same epoch. Every other test in the
layup suite uses synthetic observations (either generated via
predict_sequence or from the diagnostic-scan dataset built by
integrating known orbits with ASSIST) -- this is the first
real-world ground-truth check.

What

Three numbered asteroids from the existing 4_random_mpc_ADES_provIDs_no_sats.csv
test fixture, each compared to JPL Horizons:

provID name obs arc layup vs JPL drift
119839 2002 CX17 587 27 years ~150 km
742428 2007 TC75 117 shorter ~130 km
609631 2005 HE12 109 shorter ~20 km

All three objects are mainbelt at ~2-3 AU (i.e., ~3 × 10⁸ km), so
relative position agreement is 1×10⁻⁷ to 1×10⁻⁶. Velocity agreement
is 1×10⁻⁹ AU/day or better. Test tolerances (1×10⁻⁵ AU pos,
1×10⁻⁷ AU/day vel) sit comfortably above the observed accuracy so
benign numerical drift won't flag false positives.

How

tests/data/jpl_reference_states.json -- captured once via JPL
Horizons' public API on 2026-05-17 (see the _comment_ field for
regeneration instructions: query
https://ssd.jpl.nasa.gov/api/horizons.api with
CENTER='@0', REF_PLANE='FRAME', REF_SYSTEM='ICRF',
VEC_TABLE='2', OUT_UNITS='AU-D' at the layup-recovered epoch).

The test runs hermetically against this committed fixture -- no
network access during CI.

It also catches future drift in layup's epoch-selection logic: the
test asserts that layup's chosen epoch matches the fixture's
recorded epoch to 10⁻⁹ days. If layup changes how it picks the fit
epoch for a given observation set, the fixture needs regeneration
(the test message says so).

Independent

Doesn't depend on any of the other open BK-related PRs -- branches
off main, touches only tests/.

Review Checklist for Source Code Changes

  • Does pip install still work?
  • Have you written a unit test for any new functions? — tests/layup/test_real_data_validation.py
  • Do all the units tests run successfully? — 3 parametrized cases, all green
  • Does Layup run successfully on a test set of input files/databases? — uses the existing real-MPC fixture
  • Have you used black on the files you have updated?

End-to-end test that runs orbitfit on real MPC astrometry (from the
existing 4_random_mpc_ADES_provIDs_no_sats.csv test fixture, three
real numbered asteroids) and asserts the recovered Cartesian state
matches JPL Horizons' reference at the same epoch.

This is the most realistic check in the layup test suite -- every
other test uses synthetic observations (whether generated via
predict_sequence, or from the diagnostic_scan dataset built by
integrating known orbits with ASSIST).

Empirical agreement on the three tested mainbelt asteroids:
  - 119839 (2002 CX17, 587 obs, 27-year arc):  ~150 km in position
  - 742428 (2007 TC75, 117 obs):               ~130 km in position
  - 609631 (2005 HE12, 109 obs):                ~20 km in position

All against object distances of ~2-3 AU (~3 x 10^8 km), so relative
position agreement of 1e-7 to 1e-6.  Velocity agreement is 1e-9
AU/day or better.  Test tolerances (1e-5 AU pos, 1e-7 AU/day vel)
sit comfortably above the observed accuracy so benign numerical
drift won't flag false positives.

Hermetic by design: the JPL reference states are committed to
tests/data/jpl_reference_states.json (captured once via the public
Horizons API on 2026-05-17; the file's _comment_ field documents
how to regenerate).  The test runs in CI without any network
access.

Also catches any future drift in layup's epoch-selection logic: the
test asserts that the layup-chosen epoch matches the fixture's
recorded epoch to 1e-9 days.  If layup changes how it picks the fit
epoch for a given observation set, the fixture needs regeneration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant