Validation
Accuracy here is measured in two stages. The Python reference engine is compared against Swiss Ephemeris, and the TypeScript port is compared against golden fixtures written by that reference. Both suites run in CI on every commit, and the tables below report what they measure.
Reference vs Swiss Ephemeris 2.10
The Python reference was compared against Swiss Ephemeris at hundreds of random instants across 1000–3000. The quantity compared is apparent geocentric ecliptic longitude at the true equinox of date, for every body, together with the angles and cusps at six latitudes that include polar Iceland. The table gives the maximum and root-mean-square disagreement in arcseconds.
| Body | Max | RMS | Note |
|---|---|---|---|
| Sun | 0.5″ | 0.2″ | |
| Moon (precise tier) | 2.5″ | 0.9″ | JPL DE423 fit (2010); DE423 vs DE440 is <0.1″ over this span |
| Moon (embedded series) | 9.6″ | 2.8″ | 60-term ELP abridged |
| Mercury | 0.6″ | 0.2″ | |
| Venus | 0.8″ | 0.2″ | |
| Mars | 0.7″ | 0.2″ | |
| Jupiter | 0.9″ | 0.3″ | |
| Saturn | 1.0″ | 0.4″ | |
| Uranus | 1.9″ | 0.7″ | series truncation; complete VSOP87 holds ≤1″ |
| Neptune | 4.6″ | 2.2″ | series truncation; complete VSOP87 holds ≤1″ |
| Pluto | 3.4″ | 1.0″ | JPL Horizons barycenter Chebyshev pack (1000-3000, true error 8.3e-7 AU ≈ 0.005″); supersedes the Meeus ch.37 series. Bound is vs SE's own Moshier Pluto over the modern era |
| Chiron | 1.0″ | 0.3″ | JPL Horizons fit, 1600-2484 (Horizons serves small bodies only over ~1600-2500) |
| Mean node | 0.1″ | 0.1″ | |
| True node | 0.8″ | 0.4″ | vs full JPL DE431 files; vs Swiss Ephemeris's BUILT-IN (Moshier) ephemeris expect up to ~1′ (measured: 50″ max, 8″ median, 300 epochs) — that is the built-in lunar theory's own node error, not ours |
| Ascendant / MC | 3.2″ | — | |
| Placidus cusps (all 12) | 3.2″ | — | |
| Mean Lilith | 1.6″ | 0.5″ | mean lunar apogee on the inclined orbit; latitude ≤0.1″ |
| Sidereal longitudes | — | — | ayanamsa model vs SE ≤0.005″ (Vondrák 2011, same model both sides); sidereal longitudes inherit each body's tropical bound |
| RA / Dec | 2.6″ | — | rotation is exact; bound tracks each body's ecliptic accuracy (Moon worst) |
| Topocentric Moon | 2.7″ | — | parallax model adds ≤0.1″ over the geocentric bound |
| House cusps, 8 new systems | 0.0″ | 0.0″ | Koch, Regiomontanus, Campanus, Alcabitius, Morinus, Meridian, Polich-Page, Vehlow: exact vs swe_houses_armc (200 polar-inclusive cases each) |
| Vertex / east point | 0.0″ | 0.0″ | exact vs swe_houses_armc |
| Magnitudes | 0.045 mag″ | — | Mallama 2018; Moon (Allen law) valid to phase angle 140° |
| Rise/set/meridian transit | 0.5 s″ | — | vs swe_rise_trans, 48 polar-inclusive cases per body; Moon bound tracks its position accuracy |
| Crossings / lunar phases | 4 s″ | — | vs swe_solcross/swe_mooncross and elongation root-finds |
| Stations | 55 s″ | — | ill-conditioned by nature: speed-zero slope ~0.01°/day² turns sub-arcsecond model differences into minutes |
| True Lilith (osc. apogee) | 214″ | — | hypersensitive to the lunar theory (~1/e amplification); SE's own Moshier-vs-DE difference dominates. 'True Lilith' values disagree across software at this scale |
| Ceres, Pallas, Juno, Vesta, Pholus | 1.0″ | 0.3″ | JPL Horizons fits 1600-2484 (true error <5e-6 AU); same geocentric pipeline as Chiron. No independent SE oracle here: SE's asteroid files are unavailable in Moshier mode |
| Uranian bodies (Cupido…Poseidon) | 2.3″ | — | Hamburg-school constant-element Kepler orbits, elements fitted to SE 2.10's built-in definitions (fit_uranian.py prints per-body figures; Zeus is fit-noise-limited at ~3″ heliocentric). Uranian practice works in arcminutes |
| Fixed stars (318-star catalog) | 0.3″ | — | HYG-derived catalog (ICRS J2000 + proper motions, full 3D space motion, Vondrák 2011 precession); vs swe_fixstar fed the same rows |
| Star-anchored ayanamsas | 0.5″ | — | galcent_0sag and true_citra computed from the apparent star; bound tracks the fixed-star chain (≤0.3″) plus the body's tropical accuracy |
| Gauquelin sectors | 0.0001 sectors″ | — | rise/set of disc center with refraction (SE method 3): exact to the rise/set bound |
| Eclipses (solar + lunar) | 9 s″ | — | times of maximum vs swe; types exact over 1990-2030 (92 lunar + 89 solar, zero mismatches); lunar magnitudes ≤0.0013 (Danjon parallax enlargement, recovered empirically). Contact times typically ≤15 s, minutes for grazing geometries. Global circumstances only — no ground paths |
For scale, chart software usually displays positions to the arcminute (60″), and uncertainty in a recorded birth time moves a chart by more than any of the deltas above. Instants after 2025 also depend on how each engine extrapolates ΔT, which no model can pin down; the reasoning is in the Build Notes.
TypeScript port vs reference
The port is replayed against 3,397 golden checks, covering bodies, timescales, nutation, the twelve house systems, fixed stars, Gauquelin sectors, eclipses, speeds, retrograde flags, and the polar Placidus fallback. The worst deviation recorded across that suite is 0.61 nano-arcsecond. Because both implementations run the same algorithms in IEEE doubles, a tolerance this far below astronomical relevance is achievable, and a porting bug fails the build rather than reaching a release.
Cross-checks
The Moon fit to JPL DE423 leaves a residual of 0.19 km, or about 0.1″. The Chiron fit from Horizons agrees with the Swiss Ephemeris asteroid file to 0.85″ in the worst case. The MCP aspect-date search was checked hit for hit against an independent scan, which found the same nine Mars sextiles to the minute, including a retrograde triple pass.
Reproduce
Clone the repo and run npm install && npm run build && npm test to reproduce every figure on this page. If you find a discrepancy against any professional ephemeris, please open an issue with the UTC instant and the coordinates you used.
The validated range runs from 1000 to 3000 for the planets, Pluto, and the Moon, all checked against JPL Horizons. The small bodies span 1600 to 2484, which is the widest window Horizons serves for them, and the hosted edge API covers a narrower window still because of its embedded Moon tier. Placidus is undefined above the polar circles, so the engine falls back to whole-sign there and reports that fallback in the response. Methods → Build notes →