All computational correctness in goeph is validated against Skyfield via golden tests. The JSON files in this directory are the source of truth.
cd testdata
/opt/anaconda3/bin/python generate_golden.pyRequires Python 3 with skyfield installed. The script loads ../data/de440s.bsp and writes all golden_*.json files. These are checked into git so CI runs without Python or Skyfield.
Primary grid: 3,653 dates at 30-day increments across the full DE440s range (1850-2149).
This is dense enough to catch systematic drift from approximations (nutation, delta-T, precession) and error growth over centuries away from J2000. Fine-grained sampling is unnecessary because the underlying physics is smooth (Chebyshev polynomials, continuous rotations).
Bodies tested: 10 bodies for position/velocity/apparent (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto barycenters). 3 bodies for altaz (Sun, Moon, Mars) across 6 observer locations.
All tolerances are measured max error vs Skyfield across the full date range.
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_spk.json |
SPK positions | 0.01 | ~0.002 | km | Mercury barycenter chain (with TDB-TT correction) |
golden_velocity.json |
Velocity | 0.005 | ~0.0002 | km/day | Chebyshev derivative (with TDB-TT correction) |
golden_apparent.json |
Apparent positions | max(50, 1.5e-5 * dist) | ~27,000 km abs | km | Light-time correction in Skyfield's observe() (~20 arcsec, scales with distance) |
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_timescale.json |
UTC to TT | 1e-9 | ~1e-10 | days | Identical leap second table |
golden_timescale.json |
TT to UT1 | 1e-6 | 7.5e-7 | days (~65 ms) | Cubic spline vs Skyfield's spline (different source knots) |
golden_tdbtt.json |
TDB-TT | 1e-9 | ~1e-10 | seconds | Identical Fairhead & Bretagnon formula |
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_era.json |
Earth Rotation Angle | 1e-8 | ~1e-9 | degrees | Identical IAU 2000 formula |
golden_sidereal.json |
GMST | 1e-3 | ~5e-4 | degrees | IAU 1982 Meeus vs Skyfield's IERS 2000 ERA-based |
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_locations.json |
Geodetic to ecliptic | 0.025 | ~0.022 | degrees | Light-time correction in Skyfield's observe() for surface locations |
golden_altaz.json |
Altitude | 0.005 | ~0.0005 | degrees | GMST formula difference (IAU 1982 vs IERS 2000) |
golden_altaz.json |
Azimuth | 0.02 | ~0.010 | degrees | GMST formula + minor near-horizon geometry effects |
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_separation.json |
Separation angle | 1e-8 | ~1e-9 | degrees | Same position vectors, same formula |
golden_phase.json |
Phase angle | 1e-10 | ~6e-14 | degrees | Exact input vectors from Skyfield; formula-level agreement |
golden_elongation.json |
Elongation | 1e-10 | ~1e-11 | degrees | Pure modular arithmetic |
golden_refraction.json |
Refraction | 1e-10 | ~1e-11 | degrees | Identical Bennett 1982 formula |
| Golden file | Test | Tolerance | Match rate | Units | Error source |
|---|---|---|---|---|---|
golden_seasons.json |
Seasons (equinox/solstice) | 1 day | 100% | days | J2000 ecliptic vs Skyfield's ecliptic of date (~18 hours) |
golden_moon_phases.json |
Moon phases | 3 min | ≥99% | days | Relative longitude cancels frame difference (measured max ~4 sec) |
golden_sunrise_sunset.json |
Sunrise/sunset (NYC 2024) | 3 min | ≥99% | days | Altaz chain; no ecliptic frame dependency (measured max ~1 sec) |
golden_twilight.json |
Twilight (NYC Jan 2024) | 3 min | ≥95% | days | Altaz chain; extra events from finer step size (measured max ~1 sec) |
golden_oppositions.json |
Oppositions/conjunctions (Mars) | 3 min | 100% | days | Relative longitude cancels frame difference (measured max ~46 sec) |
Almanac golden data generated by generate_golden_almanac.py using Skyfield's almanac functions. Seasons use a 1-day tolerance because goeph uses J2000 ecliptic while Skyfield uses ecliptic of date, causing up to ~18 hours systematic offset. All other almanac tests use relative longitudes or altaz coordinates where the frame difference cancels, achieving sub-minute agreement with Skyfield.
| Golden file | Test | Tolerance | Measured max | Units | Error source |
|---|---|---|---|---|---|
golden_lunarnodes.json |
Lunar node longitude | 1e-8 | ~1e-9 | degrees | Identical Meeus formula |
Geodetic→ecliptic and apparent positions (~20 arcsec): Skyfield's golden data generator uses observe() which applies iterative light-time correction even for geographic locations on Earth's surface. During the ~0.02 seconds of light travel (6400 km), Earth's orbital motion displaces the position by ~0.6 km (~20 arcsec). goeph computes the surface position at the observation time directly, without this light-time iteration.
GMST formula (~0.3 arcsec at T=1): goeph uses the IAU 1982 Meeus GMST formula; Skyfield uses the IERS 2000 ERA-based formula. These differ by up to ~0.3 arcsec per century from J2000.
Equation of equinoxes complementary terms (~0.003 arcsec): Skyfield includes additional complementary terms in the equation of equinoxes that goeph omits. This is negligible.
Golden tests that compare nutation-dependent computations (geodetic→ecliptic, altaz) use coord.NutationFull mode to match Skyfield's full IAU 2000A model (678 luni-solar + 687 planetary terms). The tolerances listed above reflect this mode. With the default NutationStandard (30 terms), results are ~0.001 arcsec less precise — negligible compared to the dominant error sources listed above.
Skyfield defaults to the full IAU 2000A series but also provides an iau2000b() variant (77 luni-solar, 0 planetary terms) for faster computation at ~1 milliarcsecond precision. goeph's NutationStandard (30 terms) is a different truncation than Skyfield's iau2000b() but serves a similar purpose.
All files are JSON with a top-level tests array. Each test entry includes the input parameters and expected output values from Skyfield. See generate_golden.py for the exact schema of each file.