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 8c4946b, 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.12, the version in the engine repository when the suite was released. This site's charts use version 0.1.1-rc.10.

@zodiacs/engine 0.1.1-rc.12

KindPassFailUnsupportedMedian / largest residual
Apparent positions, ecliptic of date471930lon 2.067″ / 18.846″; lat 1.187″ / 19.028″
Positions, all471930
Ascendant and midheaven6000asc 0.037″ / 0.182″; mc 0.043″ / 0.178″
Vertex and east point1000vertex 0.083″ / 0.285″; east_point 0.083″ / 0.103″
House cusps, thirteen systems8000cusps 0.051″ / 0.489″
Houses and angles, all15000
Time zone offsets20205—
Local mean time005—
TT − UTC091tt_minus_utc_s 0.388 s / 0.634 s
ΔT (TT − UT1)1500delta_t_s 0.00410 s / 0.0689 s
Calendar date to day number0025—
Day number to calendar date0010—
Time and calendars, all352946

Vectors tagged backzone-history: 0 pass, 20 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.07″ 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.9″. A precise backend is planned to close this gap.
  • Time zones (L3). The engine reads the host's Intl time zone data, which does not carry tzdb's backzone history. All 20 failures are the vectors tagged backzone-history, and on each the engine returns what tzdb's default build gives.
    • A repeated local time is resolved to its earlier instant only, and the engine has no public way to give the later one. Those vectors are unsupported.
  • TT − UTC (L3). The engine reads a UTC instant as UT1, so its TT − UTC is its ΔT. Each residual (engine minus expected) is −(UT1 − UTC) for that date, up to 0.63 s here. A leap-second table is planned. A leap second itself cannot be expressed as a JavaScript date, so that case is unsupported.
  • Unsupported (L3). Release 0.1.1-rc.12 has no public function for local mean time from a longitude or for calendar conversion. Both are planned for the next engine release.

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.