Conformance suite

Check an engine against independent references.

The suite is 500 test vectors for the numbers an astrology program computes: positions, angles and house cusps, time zone offsets, time scales and calendar dates. Each vector names its arbiter, the independent source that decides the expected value, and a tolerance that was fixed before any engine was run against it. None uses Swiss Ephemeris as its arbiter; Swiss Ephemeris is measured like any other engine.

Any engine can be run against it through a small adapter that reads and writes one JSON object per line. The vectors, the harness and the adapters are dedicated to the public domain under CC0 1.0. Version 0.1.0 is at zodiacs-org/engine, commit 782b496, with its format and protocol and full results.

What it checks

LevelKindVectorsTolerance
L1Apparent positions, ecliptic of date240lon ±1″ (on the circle); lat ±1″
L2Ascendant and midheaven60asc ±1″ (on the circle); mc ±1″ (on the circle)
L2Vertex and east point10vertex ±1″ (on the circle); east_point ±1″ (on the circle)
L2House cusps, thirteen systems80cusps each ±1″ (on the circle), or status exact
L3Time zone offsets45status exact; utc_offset_s exact, or status exact, or status exact; utc_offsets_s exact
L3Local mean time5utc_offset_s ±0.001 s
L3TT − UTC10tt_minus_utc_s ±0.0005 s
L3ΔT (TT − UT1)15delta_t_s ±0.1 s
L3Calendar date to day number25jdn exact
L3Day number to calendar date10year exact; month exact; day exact

The arbiters

  • Positions. NASA JPL Horizons (DE441) apparent geocentric ecliptic positions, carried into the IAU 2006/2000A true ecliptic and equinox of date.
  • Houses and angles. ERFA sidereal time and obliquity; spherical geometry of the angles.
  • Houses and angles. Ecliptic divisions: equal, whole-sign, Vehlow, equal-MC, Porphyry.
  • Houses and angles. Fixed house circles: Regiomontanus, Campanus, meridian, Morinus.
  • Houses and angles. Semi-arc and pole systems: Placidus, Koch, Alcabitius, topocentric.
  • Houses and angles. Cusps that do not exist: the construction has no solution in zodiacal order.
  • Time and calendars. IANA tzdb 2025c with backzone, compiled by zic 2025c.
  • Time and calendars. Local mean time from longitude.
  • Time and calendars. IERS leap-second table (leap-seconds.list, tzdata 2025c).
  • Time and calendars. IERS UT1 - UTC (finals2000A, observed rows) with the leap-second table.
  • Time and calendars. Julian and Gregorian calendar arithmetic, two published algorithms.

Each vector file records how its arbiter was applied, the digest of every input, and the arbiter's own uncertainty. The generators rebuild every file byte for byte, and the engine repository's continuous integration checks that they still do.

Results

A vector an engine has no function for is counted as unsupported, not as a failure. Residuals are the median and largest difference from the expected value.

The @zodiacs/engine results are for version 0.1.1-rc.17, the version this site's charts use.

@zodiacs/engine 0.1.1-rc.17

KindPassFailUnsupportedMedian / largest residual
Apparent positions, ecliptic of date481920lon 2.083″ / 18.774″; lat 1.187″ / 19.028″
Positions, all481920
Ascendant and midheaven6000asc 0.003″ / 0.019″; mc 0.004″ / 0.008″
Vertex and east point1000vertex 0.003″ / 0.013″; east_point 0.003″ / 0.007″
House cusps, thirteen systems8000cusps 0.004″ / 0.071″
Houses and angles, all15000
Time zone offsets4500—
Local mean time005—
TT − UTC901tt_minus_utc_s 0.00 s / 2.03e-8 s
ΔT (TT − UT1)1500delta_t_s 0.000132 s / 0.000505 s
Calendar date to day number0025—
Day number to calendar date0010—
Time and calendars, all69041

Vectors tagged backzone-history: 20 pass, 0 fail, 0 unsupported, of 20.

  • Positions (L1). The engine computes positions with astronomy-engine 2.1.19's series. Against DE441 they differ by a median of 2.08″ in longitude. The largest differences are 18.8″ in longitude (Neptune) and 19.0″ in latitude (Saturn); the Sun is within 1.64″ and the Moon within 3.75″. Since the engine took the full IAU 2000B nutation in place of astronomy-engine's five terms, L1-POS-0041 (the Sun in 1908, 1.076″ off in longitude before) passes. A precise backend is planned to close this gap.
  • Time zones (L3). Before 1970 the engine reads the tzdb 2025c history with backzone that it ships, loaded with prepareLocalTime, so the 20 vectors tagged backzone-history pass; from 1970 it reads the host's Intl data. A repeated local time resolves to its earlier instant, and the transition the engine reports gives the offsets of both readings, so the five folds are answered. All 45 pass.
  • TT − UTC and ΔT (L3). From 1972 to 2027-10-02 the engine takes TAI − UTC from the IERS leap-second list updated 2026-07-06, and UT1 − UTC from IERS: the EOP 20 C04 series in 1972 and finals2000A.all of 2026-09-24 from 1973-01-02. The adapter reports TT − UTC as a chart's ΔT (TT − UT1) plus its UT1 − UTC. A leap second itself cannot be expressed as a JavaScript date, so that case is unsupported.
  • Unsupported (L3). Release 0.1.1-rc.17 converts dates between the Julian and Gregorian calendars and reads a birthplace's local mean time inside a zone's local mean time era, but has no public function for a Julian Day Number or for local mean time from a longitude alone, so the 35 calendar vectors and the 5 local mean time vectors are unsupported.

Swiss Ephemeris 2.10.03

KindPassFailUnsupportedMedian / largest residual
Apparent positions, ecliptic of date24000lon 0.001″ / 0.005″; lat 0.000″ / 0.002″
Positions, all24000
Ascendant and midheaven41190asc 0.002″ / 5.862″; mc 0.001″ / 2.028″
Vertex and east point640vertex 0.024″ / 2.952″; east_point 0.026″ / 2.040″
House cusps, thirteen systems56240cusps 0.001″ / 15.652″
Houses and angles, all103470
Time zone offsets0045—
Local mean time005—
TT − UTC1000tt_minus_utc_s 0.00000729 s / 0.0000196 s
ΔT (TT − UT1)1500delta_t_s 0.00166 s / 0.0513 s
Calendar date to day number2500—
Day number to calendar date1000—
Time and calendars, all60050

Vectors tagged backzone-history: 0 pass, 0 fail, 20 unsupported, of 20.

Published as verdicts, returned flags and summary statistics only.

  • Houses and angles (L2). Swiss Ephemeris 2.10.03 uses the IAU 2006/2000A sidereal time from 1850 to 2050, where its sidereal time agrees with the arbiter's to 0.001″ and its angles and cusps to within 0.007″. Outside that span it switches to its own long-term model. That model's sidereal time is 1.9″ from the IAU value just after 2050, falling to 0.7″ by 2199 (both measured with Swiss's own ΔT); the angles and cusps differ by up to 15.7″ here.
    • All 47 failures are dated after 2050.
    • The vectors before 1850 all pass.
    • Given the arbiter's own sidereal time and obliquity, its house geometry agrees to within 0.0011″ in every system (see arbiters/l2/README.md).
  • Flags. Every position request returned SEFLG_SWIEPH: Swiss read its .se1 files rather than falling back to its Moshier theory.
  • Unsupported. Swiss Ephemeris has no time zone database and no function for local mean time from a longitude.

Run it yourself

With Node.js 20 or later, from a clone of the engine repository:

npm ci && npm run build
node conformance/harness/validate.mjs --expect-total 500
node conformance/harness/run.mjs \
  --adapter "node conformance/adapters/zodiacs-engine.mjs" \
  --out my-results.json

To measure another engine, write an adapter that follows the protocol: one JSON request per line in, one JSON response per line out, and unsupportedfor anything the engine cannot answer.

Report an error in the suite

If you believe a vector's expected value, tolerance or arbiter is wrong, open a conformance discrepancy with your evidence. Every report gets a public disposition in the log. A vector is never changed in place: a wrong one is withdrawn and replaced under a new id.