Add radar (delay/Doppler) Python ingest + end-to-end driver test (#146)#355
Open
matthewholman wants to merge 3 commits into
Open
Add radar (delay/Doppler) Python ingest + end-to-end driver test (#146)#355matthewholman wants to merge 3 commits into
matthewholman wants to merge 3 commits into
Conversation
Wires the C++ RadarObservation type into the orbitfit Python pipeline. - _radar_observation(): builds Observation.from_radar from a row, converting JPL units to the fitter's internal units -- delay us -> days, Doppler Hz -> round-trip range-rate au/day via the per-observation transmit frequency freqTx (doppler[au/day] = -c * doppler[Hz] / freqTx). Uncertainties from rmsDelay/rmsDoppler when present, else ~1 us / ~1 Hz defaults. A Doppler row without a usable freqTx is a ValueError, not a silent NaN. - _is_radar(): per-row dispatch (a row is radar when delay or doppler is populated); added a radar branch ahead of the streak/astrometry branches. - REQUIRED_INPUT_OBSERVATIONS_COLUMN_NAMES: delay/doppler added as one-of observable options alongside ra/dec and raRate/decRate. - _is_valid_data(): radar-aware -- a radar-only file (no ra/dec columns) now validates, while each row must still carry a usable observable. - The occultation / debias / Veres-weighting steps (all astrometric) skip radar rows; radar is forced onto engine="cartesian" (BK-native can't do the variable-row packing yet). Units conversion, dispatch, and validation are locked by tests/layup/test_radar_ingest.py (6 tests, no ASSIST needed). Full optical + streak + radar suites: 23 passed, no regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exercises the full orbitfit() Python driver path for radar observations, which the existing radar tests skip: test_radar_validation.py calls run_from_vector directly with observer states baked into a fixture, and test_radar_ingest.py covers only the unit conversion in isolation. Neither runs a radar arc through orbitfit() -> spice.str2et + obscodes_to_barycentric station states -> _radar_observation dispatch -> variable-row Cartesian fit. The truth delay/Doppler are generated in-test against the driver's own obscodes_to_barycentric station states and convert_tdb_date_to_julian_date epochs, so the observation cannot drift from the observer model the fitter uses -- the consistency gap left when the C++ and Python cores were validated separately. Orbit is the same ~2.6 AU MBA near opposition as the streak fixture; truth observables are an independent ASSIST propagation at the C++ light-time convention. Radar refines a prior orbit (perturbed initial guess, bypassing IOD), matching real radar astrometry and giving a zero-residual fixed point the noise-free fit must return to. Two tests: full delay+Doppler recovery (state to <1e-6, csq<1e-6, ndof=2N-6) and a delay-only arc (has_delay/has_doppler dispatch, ndof=N-6). Verified non-vacuous: a 10 us delay corruption raises csq from <1e-6 to ~7.6. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the two-leg radar light-time model on the Python side and locks it against real measurements. - orbitfit() finite-differences the barycentric observer velocity from obscodes_to_barycentric (only when radar columns are present) to get the observer acceleration, appended as ax/ay/az and passed to Observation.from_radar_with_id. The C++ model uses it to extrapolate the station state to the signal transmit time. - test_radar_end_to_end.py's in-test truth now mirrors the C++ two-leg model exactly (down + up leg, station Taylor-extrapolated with the same finite-difference acceleration), so the noise-free fit still reaches csq < 1e-6. - New tests/layup/test_radar_realdata_validation.py: fits the real 2013 JPL radar arc for (99942) Apophis (36 monostatic delay/Doppler rows from Goldstone DSS-14 + Arecibo, tests/data/apophis_2013_radar.json) seeded from a stored JPL Horizons reference, and recovers the barycentric state to < 1e-6 vs Horizons. Offline/deterministic, like test_tno_validation. A regression to the single-position model misses by orders of magnitude. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5566b9f to
b813773
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Radar (delay/Doppler) Python ingest + end-to-end and real-data validation (issue #146, P2 + P3). Stacks on the C++ two-leg core PR.
Ingest —
_radar_observation+_is_radarinorbitfit.py: per-row dispatch; µs→days and Hz→au/day via per-obsfreqTx; uncertainties fromrmsDelay/rmsDoppleror defaults; Doppler withoutfreqTxis aValueError.orbitfit()finite-differences the barycentric observer velocity to supply the observer acceleration the two-leg model needs (only when radar columns are present). Radar forced toengine='cartesian';_is_valid_dataradar-aware; occultation/debias/Véres-weighting skip radar rows.Validation
test_radar_ingest.py(6, no ASSIST): unit conventions + dispatch.test_radar_end_to_end.py(2): a radar arc through the fullorbitfit()driver; in-test truth mirrors the C++ two-leg model, so the noise-free fit reaches csq<1e-6 (ndof=2N−6; delay-only ndof=N−6).test_radar_realdata_validation.py(1): fits the real 2013 JPL radar arc for (99942) Apophis (36 monostatic delay/Doppler rows from Goldstone DSS-14 + Arecibo) seeded from a stored JPL Horizons reference, and recovers the barycentric state to <1e-6 vs Horizons. Offline/deterministic, liketest_tno_validation. A regression to the single-position model misses by orders of magnitude.Stacking
Base is the C++ two-leg core PR. Merge order: chi2_final fix → C++ core → this.
🤖 Generated with Claude Code