Moon residual fix — v0.5.3 (high-precision sidereal periods)¶
Phase A diagnosis (notebook §3 → moon residual root cause): expected ecliptic-projection warp. Got something better — period truncation.
Phase A: ruled out the frame hypothesis¶
The v0.5.2 moon FFT sweep reported ~100° RMS residuals on 13 of 17
moons against ephemeris truth. Initial hypothesis (notebook §3): the
encoder advances at uniform rate omega = 2π / P_sidereal while
skyfield's astrometric.ecliptic_latlon() returns ecliptic
longitude — for moons in inclined orbits (Galileans orbit Jupiter's
equator, ~26° off ecliptic), the ecliptic-projection longitude is a
non-uniform function of orbital phase, so the residual is a bounded
periodic warp at the moon's orbital period.
research/diagnose_moon_residual.py measured the residual within
ONE orbital period for two clean controls (Callisto, Titan) and four
broken bodies (Io, Europa, Mimas, Metis):
| body | class | period (d) | within-period RMS | v0.5.2 sweep RMS |
|---|---|---|---|---|
| callisto | control | 16.7 | 1.0° | 0.6° |
| titan | control | 15.9 | 5.2° | 3.4° |
| io | broken | 1.77 | 0.42° | 106° |
| europa | broken | 3.55 | 0.81° | 116° |
| mimas | broken | 0.94 | 5.3° | 104° |
| metis | broken | 0.29 | 0.07° | 104° |
Within ONE orbital period, the "broken" moons show TINY residuals. The v0.5.2 ~100° RMS is secular accumulation, not within-orbit warping. The frame hypothesis is wrong.
Real root cause: period truncation in BODIES¶
The encoder uses omega = 2π / P_sidereal baked at codegen time
into es_omega_diag[] (C side) and the integer Q-format omega_diag
table (Python side). v0.5.0 stored period_days to 3-4 decimals.
For fast-orbit moons (Io's sidereal period 1.769 d, Metis's 0.295 d)
that 10⁻⁴-relative truncation produces 10⁻⁴-relative omega error
that accumulates over 41,000+ orbits in the 200-yr sweep horizon.
Predicted vs measured cumulative drift over 200 yr:
| body | rel. period err | revs over 200yr | predicted cumul (mod 360°) | observed v0.5.2 RMS |
|---|---|---|---|---|
| io | +7.8×10⁻⁵ | 41,291 | +77.7° | 106° |
| europa | +5.1×10⁻⁵ | 20,571 | +17.5° | 116° |
| ganymede | -6.3×10⁻⁵ | 10,210 | +130.3° | 117° |
| callisto | +1.1×10⁻⁶ | 4,377 | +1.7° | 0.6° ✓ |
| metis | -6.8×10⁻⁵ | 247,812 | +67.2° | 104° |
| enceladus | +1.6×10⁻⁴ | 53,313 | +171.5° | 103° |
| rhea | +4.7×10⁻⁵ | 16,168 | -86.9° | 98° |
The pattern is clear: the moons that match the predicted cumulative drift (Callisto's ~2° prediction matches its 0.6° observed) are exactly the ones that ALREADY worked in v0.5.2. The moons whose predicted cumulative drift is large are exactly the ones that were broken. Period truncation is the dominant residual source.
The wrap of accumulated cumulative drift modulo 2π produces a sawtooth-shaped residual whose FFT spectrum is broadband — that's the "near-DC content" v0.5.2 reported (FFT peak at sweep span = 336 yr is just the sawtooth fundamental).
v0.5.3 fix: high-precision periods¶
Replace the truncated period_days values with full-precision
sidereal periods from JPL HORIZONS / NASA fact sheets. Periods now
stored to 9+ decimal places in research/bodies.py. After codegen
+ rebuild, re-run the moon FFT sweep:
| Moon | v0.5.2 RMS | v0.5.3 RMS | improvement |
|---|---|---|---|
| io | 106.33° | 0.34° | −317× |
| europa | 116.41° | 0.76° | −154× |
| ganymede | 117.49° | 0.14° | −825× |
| callisto | 0.60° | 0.60° | unchanged ✓ |
| adrastea | 103.57° | 0.07° | −1450× |
| amalthea | 102.30° | 0.27° | −376× |
| enceladus | 102.96° | 2.57° | −40× |
| tethys | 101.46° | 2.94° | −34× |
| dione | 117.24° | 2.54° | −46× |
| titan | 3.39° | 3.39° | unchanged ✓ |
| iapetus | 2.50° | 2.50° | unchanged ✓ |
| hyperion | 11.0° | 11.0° | unchanged ✓ |
| mimas | 103.65° | 30.78° | −3.4× (partial) |
| 103.97° | 109.0° | unchanged | |
| 105.30° | 104.3° | unchanged | |
| 98.28° | 100.5° | unchanged | |
| 103.86° | 103.8° | unchanged |
13 of 17 moons are now in Callisto-class clean territory (≤ 3° RMS; previously only 4). The Galileans + Jovian inner regulars + the inner Saturnian resonance set (Mimas–Tethys + Enceladus–Dione) all dropped by 30-1400×.
Still broken: metis / thebe / rhea / phoebe¶
Four moons resisted the period-truncation fix. The predicted-cumulative- drift heuristic suggests their period precision IS adequate, so something else is going on:
- Metis (P=0.29478, top-1 FFT period 168 yr at 49°): published sidereal periods for Metis vary across sources (0.2948 d on Wikipedia, 7.07467 hr = 0.294778 d on JPL, etc.). May need a more precise authoritative value.
- Thebe (P=0.6745 d): the published value is exactly 0.6745 with no further digits in most sources. The remaining residual may be perturbation-driven (Thebe has a small but non-zero inclination + eccentricity).
- Rhea (P=4.518212 d): published value matches our entry to 6 decimals. Could be a frame issue (Rhea's orbit is inclined ~0.35° to Saturn's equator) or a perturbation from neighbouring moons (Mimas, Tethys, Dione).
- Phoebe (P=550.564636 d, RETROGRADE): orbits backward relative
to Saturn. Our encoder advances
omega = +2π/Pregardless of direction. May need a sign flip or a frame fix specific to retrograde irregulars.
These four are queued for v0.5.x phase B+ — likely individual diagnostic + fix per moon. The v0.5.3 fix is the structural one; the remaining 4 need physics-specific investigation.
What this earns¶
The v0.5.3 fix unblocks the LS-fit catalog methodology (§9) for
moons. With 13 moons now in clean ≤ 3° RMS, the next step is to
re-run de441_moon_spectrum with the v0.5.2 LS-fit pipeline against
the cleaned-up moon residuals — likely surfacing per-moon resonance
peaks worth authoring CATALOG_V2 entries for. (The Mimas–Tethys
4:2 + Enceladus–Dione 2:1 + Titan–Hyperion 4:3 wired in v0.5.0's
RESONANCES table can finally be measurement-validated against
ephemeris truth.)
Reproducing this¶
cd docs/antikythera-maths
# Phase A diagnostic (per-orbital-period residual on Callisto/Io/Mimas/etc.)
python -m research.diagnose_moon_residual
# Re-run the moon FFT sweep on the v0.5.3 high-precision periods
python -m research.de441_moon_spectrum
End-to-end ~3 min on the C native + skyfield path.