Calculation evidence / November 1, 2026
Birth chart calculation accuracy and evidence
LunarOtter calculates astronomical positions with astronomy-engine 2.1.19. We compare those positions with NASA JPL Horizons, check chart angles separately, and test repeatability. These checks describe numerical calculations. They do not measure the accuracy of astrological interpretations.
Engine, coordinates, and time
Our MIT-licensed engine is astronomy-engine 2.1.19, using conventions v2: Western tropical zodiac, geocentric apparent positions, and Whole Sign houses. The Ascendant and Midheaven are calculated separately. The Midheaven is not assumed to be the tenth-house boundary.
Historical civil-time conversion uses bundled IANA time-zone database 2026f. We resolve the local birth time with the rules for that date, rather than today’s offset. Repeated or nonexistent local times require an explicit choice. Pre-1970 records receive a historical-rules notice.
UTC approximates UT1; the engine’s Espenak-Meeus Delta T model supplies terrestrial time (TT). The comparisons below use shared explicit TT to isolate position and frame differences.
NASA JPL Horizons: fixed-epoch comparison
24 fixed instants from 1900 through 2025, with ten bodies at each instant, give 240 longitude/latitude pairs. Horizons queries use observer quantity 31, geocenter 500@399, AIRLESS, ICRF, and apparent ecliptic/equinox of date. No topocentric parallax or refraction is applied.
Absolute differences below are in degrees. Each body has 24 samples. Nearest-rank p99 equals the maximum with this sample size; it is not evidence that the error is constant. The acceptance bound is 0.01° for each coordinate, not a guarantee about an individual birth time.
| Body | Longitude max | Longitude p99 | Latitude max | Latitude p99 |
|---|---|---|---|---|
| Sun | 0.000418375 | 0.000418375 | 0.000327212 | 0.000327212 |
| Moon | 0.000620377 | 0.000620377 | 0.000201656 | 0.000201656 |
| Mercury | 0.003361890 | 0.003361890 | 0.001145681 | 0.001145681 |
| Venus | 0.001789722 | 0.001789722 | 0.001643026 | 0.001643026 |
| Mars | 0.001710470 | 0.001710470 | 0.001986073 | 0.001986073 |
| Jupiter | 0.001652427 | 0.001652427 | 0.002485299 | 0.002485299 |
| Saturn | 0.002141656 | 0.002141656 | 0.005366631 | 0.005366631 |
| Uranus | 0.003063173 | 0.003063173 | 0.004402615 | 0.004402615 |
| Neptune | 0.004780556 | 0.004780556 | 0.002662637 | 0.002662637 |
| Pluto | 0.001011297 | 0.001011297 | 0.000302495 | 0.000302495 |
Reference: NASA JPL Horizons manual and Horizons API. The small residuals include analytical orbital and lunar approximations, aberration and frame-model differences.
Independent release check: new dates and places
A separate audit used 50 newly seeded instants across 10 places, producing 500 charts. The earliest instant was 1901-05-29T08:56:00.000Z and the latest was 2022-08-04T19:16:00.000Z. These inputs do not overlap the frozen development charts. The city-center coordinates are approximate synthetic inputs, rather than verified Amazon Location results.
Places: Reykjanesbaer, Valparaiso, Dunedin, Antananarivo, Thimphu, Tashkent, Porto, Juneau, Hobart, Accra.
Fresh Horizons requests compared 100 longitude/latitude pairs at ten of these epochs, with zero sign disagreements. This is a separate sample from the fixed-epoch table above.
| Body | Longitude max | Latitude max |
|---|---|---|
| Sun | 0.000201735 | 0.000242314 |
| Moon | 0.000536351 | 0.000145901 |
| Mercury | 0.000966938 | 0.000572313 |
| Venus | 0.001104168 | 0.001386495 |
| Mars | 0.000914268 | 0.000514995 |
| Jupiter | 0.002208755 | 0.001907321 |
| Saturn | 0.002510580 | 0.005055561 |
| Uranus | 0.001464919 | 0.004927462 |
| Neptune | 0.004932893 | 0.002578242 |
| Pluto | 0.000921481 | 0.000274348 |
Ascendant and Midheaven checks
Horizons body positions do not validate a rising sign. We check angles with a separate Meeus GMST, equation-of-equinoxes, and vector-plane-intersection path. This shares nutation and obliquity with the engine, so it is an independent geometry check, not an independent astronomical model.
| Comparison | Max | p99 |
|---|---|---|
| Recorded development Ascendant, including time-model change | 0.005140328 | 0.003070275 |
| Recorded development MC, including time-model change | 0.002831678 | 0.002573351 |
| Recorded Ascendant, shared time | 0.000165729 | 0.000068868 |
| Recorded MC, shared time | 0.000061002 | 0.000051843 |
| Independent formula Ascendant | 0.000191626 | 0.000143578 |
| Independent formula MC | 0.000083817 | 0.000080181 |
The recorded development positions came from Swiss Ephemeris 2.10.03 and remain reference numbers only. Shared-time and independent checks use a 0.002° tolerance. The full migration allows 0.03° because it includes the time-model change. Five mean-obliquity textbook fixtures are checked separately within 0.03°.
Across the 500 new charts, the independent path’s maximum Ascendant difference was 0.0002411556766901413° and MC difference was 0.0000852884290907241°, both below 0.002°.
Repeatable inputs, repeatable output
Identical normalized inputs, settings, and versions produce identical canonical chart and reading content. Two fresh processes using different host time zones produced 3,638,200 identical canonical bytes for the 500-chart audit. Random identifiers, timestamps, and PDF metadata sit outside this content contract.
SHA-256: 405ff5334eea6c41648f24564bc5108865cad0f6c4c02d265afa38cf02451504
Three committed full-chart snapshots from 1900, 1984, and 2025 also run at startup. Missing, nonfinite, or changed output fails the engine self-test; numeric tolerance is 1e-9, with exact labels and structure.
Supported range and limits
Supported birth dates run from January 1, 1900 through today in the birth time zone. A complete chart requires latitude within 66° north or south. Beyond that range, we provide supported Sun and Moon information without rising or houses.
Known time uses the stated minute. Approximate time checks a chosen ±15-minute, ±30-minute, ±1-hour, or ±2-hour interval. Purchase requires stable signs, houses, retrograde flags, angles, and aspect memberships throughout that interval. Matching endpoints alone is insufficient.
Unknown time checks the entire local birth day, including short or long clock-change days. We show stable Sun and Moon signs, or possible signs when they change, without guessing rising or houses. A complete reading is unavailable for an unknown time.
Numerical agreement cannot fix an incorrect birth record or uncertain historical civil-time rules. Astrology provides a symbolic framework for reflection; these tests validate calculation behavior.
How to reproduce
The checked-in evidence sources are docs/reference/calculation-evidence.json and docs/reference/verify/results.json, described in docs/CALCULATION-EVIDENCE.md and docs/VERIFICATION.md. A test compares this page’s data with those files so the published numbers cannot silently drift.
npm ci
npm run verify:calculations
npx tsx scripts/verify-independent.ts
npx tsx scripts/verify-edge-cases.ts
npm testThese checks use committed references offline. To deliberately refresh the independent NASA reference, run npx tsx scripts/verify-independent.ts --prepare, then python3 scripts/verify-horizons.py, then npx tsx scripts/verify-independent.ts. Retain the raw responses, exact query URLs, and access timestamps, and review evidence changes before updating the public figures.
Versioned calculation conventions · Methodology · Find your Big Three free