Astronomical ephemeris API inspired by the ancient Greek Antikythera mechanism, providing NASA-validated celestial body positions for educational displays and physical mechanism control.
Validation: NASA JPL HORIZONS-compared, 1.4 arcsec typical error
Method: astronomy-engine library (VSOP87/ELP2000)
Use Cases: Educational demonstrations, planetarium displays, physical orrery control
The Antikythera Engine calculates real-time positions of celestial bodies (Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn) with validated sub-10-arcsecond precision. The system provides structured outputs for both digital visualization and physical mechanism control, recreating the computational capabilities of the ancient Antikythera mechanism with contemporary astronomical accuracy.
- Validated against: NASA JPL HORIZONS ephemeris system
- Typical error: 1.4 arcseconds (median across 7 bodies)
- Maximum error: 8.6 arcseconds (Saturn longitude)
- Validation date: 2025-10-26
- Method: Topocentric ecliptic coordinate comparison
- Observer location: 37.751°N, 97.822°W (Kansas, United States)
Complete validation methodology and results documented in docs/VALIDATION.md.
Suitable for:
- Educational astronomy demonstrations
- Planetarium displays and museum exhibits
- Physical orrery and mechanism control
- Amateur astronomy planning
- Visual observation applications
- Interactive astronomy learning
Not suitable for:
- Satellite tracking or orbital mechanics
- Occultation prediction requiring sub-arcsecond precision
- Astrometry or precise coordinate measurements
- Navigation or timing applications
- Research requiring peer-reviewed ephemeris
npm install
npm link
npm startAPI available at http://localhost:3000
Test the system:
# Get current astronomical state
curl http://localhost:3000/api/state
# Get physical device control data
curl http://localhost:3000/api/display
# Get system metadata and validation info
curl http://localhost:3000/api/systemThe system uses a validated JSON configuration architecture for application settings. Configuration is optional - the system works out of the box with sensible defaults.
config/
├── settings.default.json # Committed defaults
├── settings.local.json # Local overrides (gitignored)
└── schema.js # Zod validation
The system determines observer location using this priority order:
-
Control mode location (highest priority)
- Set via
antikythera control locationCLI command - Active until
control stopis called - See Control Mode section below
- Set via
-
Configuration file (when
observer.mode === 'manual')- Specified in
config/settings.local.json - Requires latitude, longitude, and timezone (IANA format)
- Specified in
-
Query parameters (per-request override)
?lat=X&lon=Y&elev=Zin API requests
-
IP geolocation (when
observer.mode === 'auto'or no config)- Automatic detection via ipapi.co
- City-level accuracy
- 24-hour cache
-
Fallback (lowest priority)
- Memphis, Tennessee (35.1184°N, 90.0489°W)
The system works automatically using IP geolocation by default. For fixed observatory locations or when you need precise coordinates, edit config/settings.local.json:
{
"observer": {
"mode": "manual",
"location": {
"latitude": 37.9838,
"longitude": 23.7275,
"timezone": "Europe/Athens",
"elevation": 0,
"name": "Athens, Greece"
}
}
}Configure display language and layout behavior. Defaults are suitable for most setups; per-device overrides go in config/settings.local.json (hot‑reloaded).
{
"display": {
"language": "english",
"theme": "ancient-bronze",
"layout": "gallery", // "hero" | "gallery" | "focus"
"mount": "landscape", // "landscape" | "portrait-right" | "portrait-left"
"rotate": "none", // advanced: "none" | "cw90" | "ccw90"
"showSunriseSunset": true
}
}mountcontrols the page grid: landscape = 3×1, portrait = 1×3.rotateis an advanced override that rotates the canvas drawing in software. Most users do not need it. If a monitor is physically rotated and the OS does not rotate the output, set:portrait-righthardware →rotate: "ccw90"portrait-lefthardware →rotate: "cw90"
Supported languages: English, Greek, Spanish, French, Russian, Arabic, Chinese, Japanese
All display text in the visualization (front face, back faces, zodiac signs, months) is internationalized.
Settings merge with priority: custom > local > default
# Use custom config path
export ANTIKYTHERA_CONFIG=/path/to/custom.json
npm start
# Use loose validation for development
export ANTIKYTHERA_CONFIG_LOOSE=1
npm startsettings.local.jsonchanges reload automatically- Custom config path changes reload automatically
settings.default.jsonrequires server restart
Complete configuration documentation: config/README.md
- Geocentric and topocentric celestial body positions
- Ecliptic, equatorial, and horizontal coordinate systems
- Eclipse predictions (solar and lunar)
- Planetary oppositions and conjunctions
- Visibility calculations with twilight modeling
- Ancient astronomical cycles (Metonic, Saros, Callippic)
- Lunar nodes and planetary retrograde motion
- Mechanical: Stepper motor positions and velocities, servo angles for retrograde indicators
- Digital: OLED/LCD display text, LED brightness/color values
- Astronomical: Detailed celestial coordinates and phenomena
- System: Precision metadata, reproducibility information, validation statistics
Write operations are under /api/control/*. For local development, the server auto-generates a token at .antikythera/control-token and the CLI reads it automatically.
Examples:
# Start server (generates token on first run)
npm start
# Set time (UTC, CE) — no env needed locally
antikythera control time 2025-10-29T12:00:00Z
# Set an ancient BCE time using signed astronomical years (year 0 = 1 BCE)
antikythera control time -490-09-12T06:00:00Z
# Set location (explicit, timezone required; elevation optional)
antikythera control location 37.9838,23.7275 --timezone "Europe/Athens" --name "Athens, Greece"
# Animate a range
antikythera control animate --from 2025-10-29T00:00:00Z --to 2025-10-30T00:00:00Z --speed 2
# Run/pause time flow
antikythera control run --speed 10
antikythera control pause
# Scene preset
antikythera control scene --preset planets --bodies mercury,venus,mars
# Status / Stop
antikythera control status
antikythera control stopThe example display at http://localhost:3000/ renders three canvases (front, back‑upper, back‑lower):
- Mean/Apprent solar time indicators
- Planetary positions and zodiac
- Metonic and Saros cycles
- Observer location and sunrise/sunset (when enabled)
Update model:
- The browser polls
/api/stateroughly once per second and re‑renders. - All control logic is server‑side (CLI →
/api/control/*). The browser never manipulates time or state locally. - High‑speed animations (
control run --speed …) are computed on the server; the display just renders the returned state. - No client‑side interpolation — positions are consistent per timestamp (UTC‑safe).
Use case:
- Synchronized classroom or exhibit: operators change time/location via CLI/API; all displays follow.
Shared classroom token (optional): set ANTIKYTHERA_CONTROL_TOKEN on server and clients.
BCE date support (astronomical year numbering):
- The engine and CLI accept BCE timestamps using signed astronomical years with a year zero.
- Historical year
n BCEmaps to astronomical year1 - n(e.g.,1 BCE → 0,2 BCE → -1,491 BCE → -490). - BCE dates are supported across
control time,/api/control/time,/api/state, CLIposition, andcompare.
Examples:
# Control time (CLI, BCE)
antikythera control time -490-09-12T06:00:00Z
# State API (absolute BCE timestamp via path parameter)
curl "http://localhost:3000/api/state/-490-09-12T06:00:00Z"
# Position CLI (BCE date for Mars)
antikythera position mars --date -490-09-12T06:00:00Z --format tableInternally, BCE timestamps are stored and echoed using astronomical year formatting, e.g.:
- Input:
-490-09-12T06:00:00Z - Stored:
-000490-09-12T06:00:00.000Z
The Antikythera Engine uses astronomical year numbering, which differs from historical BCE notation:
| Historical | Astronomical | Why? |
|---|---|---|
| 1 BCE | Year 0 | Astronomers need year zero |
| 2 BCE | Year -1 | for mathematical calculations |
| 100 BCE | Year -99 | |
| 101 BCE | Year -100 | One year offset |
When you set --year -100, the display shows 101 BCE.
This is correct! Astronomical year -100 = historical year 101 BCE.
Want to view Battle of Thermopylae (480 BCE)?
antikythera control time -0479-08-11T06:00:00Z # Note: -479, not -480Want to view Julius Caesar assassination (44 BCE)?
antikythera control time -0043-03-15T12:00:00Z # Note: -43, not -44Formula: Astronomical Year = 1 - BCE Year
This offset confuses students initially but teaches an important lesson:
- Historians use "1 BCE → 1 CE" (no year zero)
- Astronomers use "Year -1 → Year 0 → Year 1" (with year zero)
The Antikythera Engine is astronomically accurate, so we use astronomical numbering.
Use POST /api/control/location to pin the observer location for all reads until control stop.
CLI:
antikythera control location 40.7128,-74.0060 --timezone "America/New_York" --elevation 10 --name "New York, NY"API:
curl -X POST http://localhost:3000/api/control/location \
-H "Authorization: Bearer {{CONTROL_TOKEN}}" -H "Content-Type: application/json" \
-d '{"latitude":40.7128,"longitude":-74.0060,"timezone":"America/New_York","name":"New York, NY","elevation":10}'Notes:
- When a control location is set,
/api/stateand/api/displayignore?lat/lonoverrides and use the control location (observer.source = "control"). - FrontFace lower-right shows the control location name and coordinates; sunrise/sunset/time use the provided timezone.
The REPL shares the same unified control command implementation as the CLI, so you can drive the display directly from antikythera repl:
antikythera repl
set location 37.9838,23.7275,100
set tz Europe/Athens
control location here # push REPL context into control mode
control location status # inspect control status (alias for control status)control time nowuses the current REPL context date (set viagotoand relative steps):
goto 2025-01-02T03:04:05Z
control time now # sets control displayTime to the REPL date
control status # displayTime now ~ 2025-01-02T03:04:05Zsync controlpulls the current control location/timezone back into the REPL context:
sync control
context # location source=control, tz from control statusSee docs/CLI-REPL.md for the full REPL command reference and docs/CONTROL_MODE.md for detailed control mode behavior.
See also: docs/CONTROL_MODE.md
The server generates a persistent control token on first startup and reuses it across restarts.
- Location:
.antikythera/control-token(gitignored) - Override with env:
export ANTIKYTHERA_CONTROL_TOKEN=custom-token - Regenerate: delete the file and restart server
Normal usage (no setup):
npm run dev # Uses existing token or generates new one
antikythera control ... # CLI reads the same token automaticallyAPI endpoints:
GET /api/control(discovery)GET /api/control/statusPOST /api/control/time|run|pause|animate|scene|location|stop
GET /api/display
Complete state for physical mechanism implementation, including stepper motor control, servo positions, display text, and LED indicators.
curl http://localhost:3000/api/displayQuery parameters (optional):
date: ISO 8601 timestamplat,lon,elev: Manual observer location overrideprecision=full: Include per-body validation errorsinclude=astronomical: Include raw astronomical datadt: Interval in seconds to computestepsForIntervalfor steppersstepsPerDegree: Stepper resolution (steps/degree) used withdt
Important: The stepsForInterval field appears in stepper responses only when both dt and stepsPerDegree query parameters are provided. This field contains the exact number of motor steps to execute for the specified interval. Without these parameters, only position and velocity are returned.
Example with hardware control parameters:
curl "http://localhost:3000/api/display?dt=5&stepsPerDegree=200"Response structure:
{
"timestamp": "2025-10-28T23:01:45.595Z",
"mechanical": {
"steppers": {
"sun": {
"position": 215.781796586325,
"velocity": 0.998847784120528,
"velocityDegPerSec": 0.00001156,
"direction": "CW",
"stepsForInterval": 12,
"altitude": 1.03761647984169,
"azimuth": 253.005425613381
},
"moon": {
"position": 297.396513944196,
"velocity": 12.6810427226211,
"velocityDegPerSec": 0.00014688,
"direction": "CW",
"stepsForInterval": 147,
"altitude": 29.1465404202494,
"azimuth": 172.636428072449
},
"mercury": {
"position": 239.484014822145,
"velocity": 1.01726211710803,
"velocityDegPerSec": 0.00001178,
"direction": "CW",
"stepsForInterval": 12,
"altitude": 11.1210731625624,
"azimuth": 231.789630987122
},
"venus": {
"position": 198.765298995648,
"velocity": 1.24892443025954,
"velocityDegPerSec": 0.00001445,
"direction": "CW",
"stepsForInterval": 14,
"altitude": -7.17447701477178,
"azimuth": 268.329661272134
},
"mars": {
"position": 235.305780166959,
"velocity": 0.710306073897129,
"velocityDegPerSec": 0.00000822,
"direction": "CW",
"stepsForInterval": 8,
"altitude": 11.0088598845841,
"azimuth": 236.729874161838
},
"jupiter": {
"position": 114.843230101557,
"velocity": 0.0429114248708373,
"velocityDegPerSec": 0.00000050,
"direction": "CW",
"stepsForInterval": 1,
"altitude": -33.058690320916,
"azimuth": 356.159190320464
},
"saturn": {
"position": 355.938734706034,
"velocity": -0.0490251765785956,
"velocityDegPerSec": -0.00000057,
"direction": "CCW",
"stepsForInterval": -1,
"altitude": 18.5780781054604,
"azimuth": 109.053947533835
},
"lunar_nodes_ascending": {
"position": 345.549624261153,
"velocity": -0.0529544147843943,
"velocityDegPerSec": -0.00000061,
"direction": "CCW",
"stepsForInterval": -1
},
"lunar_nodes_descending": {
"position": 165.549624261153,
"velocity": -0.0529544147843943,
"velocityDegPerSec": -0.00000061,
"direction": "CCW",
"stepsForInterval": -1
}
},
"servos": {
"mercury_retrograde": { "angle": 0, "state": "prograde" },
"venus_retrograde": { "angle": 0, "state": "prograde" },
"mars_retrograde": { "angle": 0, "state": "prograde" },
"jupiter_retrograde": { "angle": 0, "state": "prograde" },
"saturn_retrograde": { "angle": 180, "state": "retrograde" }
}
},
"digital": {
"displays": {
"oled_main": {
"line1": "Next Eclipse: solar",
"line2": "112 days",
"line3": "2026-02-17"
},
"oled_secondary": {
"line1": "Next Opposition: Mars",
"line2": "73 days",
"line3": "2026-01-09"
},
"lcd_cycles": {
"line1": "Metonic: Year 7/19",
"line2": "Saros: 43.2% complete"
}
},
"leds": {
"visibility": {
"mercury": { "color": "red", "brightness": 0 },
"venus": { "color": "red", "brightness": 0 },
"mars": { "color": "red", "brightness": 0 },
"jupiter": { "color": "red", "brightness": 0 },
"saturn": { "color": "red", "brightness": 0 }
}
}
},
"next_eclipse": {
"type": "solar",
"kind": "annular",
"date": "2026-02-17T12:11:53.939Z",
"daysUntil": 111.548707685185,
"local": {
"kind": "partial",
"obscuration": 0.0619434435634973,
"partialBegin": null,
"totalBegin": null,
"peak": "2028-01-26T14:13:37.002Z",
"totalEnd": null,
"partialEnd": null
},
"details": null
},
"next_opposition": {
"planet": "Mars",
"date": "2026-01-09T11:41:19.629Z",
"daysUntil": 72.5274772453704
},
"update_hints": {
"mechanical": 10000,
"digital": 1000
},
"system": {
"healthy": true,
"cached": false,
"computation_time_ms": 22,
"precision": {
"validated_against": "NASA JPL HORIZONS",
"validation_date": "2025-10-28",
"coordinate_frame": "J2000 ecliptic",
"calculation_method": "astronomy-engine (VSOP87/ELP2000)",
"typical_error_arcsec": 1.61,
"max_error_arcsec": 8.62,
"validation_url": "https://github.com/mojoatomic/antikythera-engine-2/blob/main/docs/VALIDATION.md",
"error_quantiles_arcsec": { "p50": 1.61, "p95": 8.3, "max": 8.62 }
},
"reproducibility": {
"api_version": "1.1.0",
"engine_version": "astronomy-engine v2.1.19",
"git_sha": "f35c46e",
"validation_span": { "start": "2025-10-26", "end": "2025-11-25" },
"sample_count": 336,
"conventions": {
"angle_units": "degrees",
"error_units": "arcsec",
"longitude_wrap": "[0,360)",
"apparent": true,
"frame": "J2000 ecliptic"
}
},
"debug": {
"sun_altitude": 1.03761647984169,
"twilight_stage": "day",
"visibility_threshold": -6,
"planets_above_horizon": 3,
"sunrise": "2025-10-28T12:17:53.373Z",
"sunset": "2025-10-28T23:09:15.655Z",
"next_visibility_window": "2025-10-28T23:39:15.655Z",
"ecliptic_coordinates": {
"sun": { "lon": 215.781796586325, "lat": -0.00198583299563959 },
"moon": { "lon": 297.396513944196, "lat": -4.71419906148846 },
"mercury": { "lon": 239.484014822145, "lat": -2.83192440229897 },
"venus": { "lon": 198.765298995648, "lat": 1.50313271290408 },
"mars": { "lon": 235.305780166959, "lat": -0.363904907936811 },
"jupiter": { "lon": 114.843230101557, "lat": 0.0778131485971996 },
"saturn": { "lon": 355.938734706034, "lat": -2.47241398027667 }
}
},
"observer": {
"latitude": 35.1387,
"longitude": -90.0095,
"elevation": 0,
"city": "Memphis",
"country": "United States",
"source": "ip_geolocation",
"time_scale": "UTC"
}
}
}Units and conventions:
- position: Ecliptic longitude in degrees [0, 360)
- velocity: Degrees per day (negative values indicate retrograde motion)
- velocityDegPerSec: Degrees per second (derived from
velocity) - direction: 'CW' when
velocityDegPerSec ≥ 0, else 'CCW' - stepsForInterval: Integer motor steps for window
dtusingstepsPerDegree(present only when both query params provided) - altitude: Degrees above horizon [-90, 90] (negative = below horizon)
- azimuth: Compass direction in degrees [0, 360) (0=North, 90=East, 180=South, 270=West)
- angle: Servo position in degrees (0=prograde, 180=retrograde)
- brightness: LED brightness [0, 255]
- daysUntil: Fractional days until event
- update_hints: Suggested update intervals in milliseconds
GET /api/state
Comprehensive astronomical calculations for visualization and analysis.
curl http://localhost:3000/api/state
# Historical or future date
curl http://localhost:3000/api/state?date=2024-06-21T12:00:00Z
# Custom observer location
curl "http://localhost:3000/api/state?lon=-122.42&lat=37.77&elev=50"Response structure:
{
"date": "2025-10-26T15:27:31.358Z",
"location": {
"latitude": 37.5,
"longitude": 23.0
},
"sun": {
"longitude": 213.47,
"latitude": -0.002,
"rightAscension": 14.06,
"declination": -12.55,
"altitude": 1.26,
"azimuth": 253.23,
"velocity": 0.998,
"angularVelocity": 0.042
},
"moon": {
"longitude": 269.03,
"latitude": -5.82,
"phase": 55.81,
"illumination": 0.22,
"age": 4.51,
"altitude": 20.28,
"azimuth": 200.07,
"velocity": 12.12
},
"planets": {
"mercury": {
"longitude": 236.97,
"latitude": -2.72,
"altitude": 10.30,
"velocity": 1.11,
"isRetrograde": false,
"motionState": "prograde"
}
// ... venus, mars, jupiter, saturn
},
"nextEclipse": {
"type": "solar",
"date": "2026-02-17T12:11:53.939Z",
"daysUntil": 113.86
},
"nextOpposition": {
"planet": "Mars",
"date": "2026-01-09T11:41:19.176Z",
"daysUntil": 74.84
},
"metonicCycle": {
"year": 7,
"progress": 0.359,
"anglePosition": 129.22
},
"sarosCycle": {
"cycle": 1,
"progress": 0.431,
"anglePosition": 155.27,
"daysUntilNext": 3745.00
},
"lunarNodes": {
"ascendingNode": 345.67,
"descendingNode": 165.67,
"motionRate": -0.053,
"nextNodePassage": {
"daysUntil": 5.79,
"type": "ascending"
}
}
}GET /api/system
Precision metadata, validation statistics, and reproducibility information.
curl http://localhost:3000/api/systemResponse structure:
{
"timestamp": "2025-10-26T15:29:25.976Z",
"system": {
"healthy": true,
"computation_time_ms": 29,
"precision": {
"validated_against": "NASA JPL HORIZONS",
"validation_date": "2025-10-26",
"coordinate_frame": "J2000 ecliptic",
"calculation_method": "astronomy-engine (VSOP87/ELP2000)",
"typical_error_arcsec": 1.4,
"max_error_arcsec": 8.6,
"validation_url": "https://github.com/mojoatomic/antikythera-engine-2/blob/main/docs/VALIDATION.md"
},
"reproducibility": {
"api_version": "1.1.0",
"engine_version": "astronomy-engine v2.1.19",
"git_sha": "fb764bd",
"sample_count": 7,
"conventions": {
"angle_units": "degrees",
"error_units": "arcsec",
"longitude_wrap": "[0,360)",
"apparent": true,
"frame": "J2000 ecliptic"
}
},
"observer": {
"latitude": 37.751,
"longitude": -97.822,
"elevation": 0,
"country": "US",
"source": "ip_geolocation",
"time_scale": "UTC"
}
}
}All endpoints support the following query parameters:
- date - ISO 8601 timestamp for historical/future calculations
- Example:
?date=2024-06-21T12:00:00Z
- Example:
- lon, lat, elev - Manual observer location override
- Example:
?lon=-122.42&lat=37.77&elev=50 - Units: longitude/latitude in degrees, elevation in meters
- Example:
- precision=full - Include per-body validation errors (debugging)
Verify Moon position against current conditions:
node scripts/validate-simple.jsComplete validation against NASA JPL HORIZONS ephemeris:
node scripts/validate-all-bodies.jsExpected output: All bodies pass with errors under 10 arcseconds.
End-to-end testing of the JSON configuration system:
./scripts/test-config-integration.shTests 10 scenarios including:
- Default configuration and auto mode
- Manual observer location from config
- Config layering and hot reload
- Query parameter overrides
- Validation modes (strict and loose)
- Control mode interaction
- Unknown key handling
- Custom config paths
Expected output: 40/40 tests passing. Runtime approximately 2-3 minutes.
Comparison against NASA JPL HORIZONS System performed on 2025-10-26:
| Body | Longitude Error | Latitude Error |
|---|---|---|
| Sun | 0.4" | 0.7" |
| Moon | 1.9" | 0.3" |
| Mercury | 1.4" | 2.1" |
| Venus | 1.1" | 0.3" |
| Mars | 0.5" | 1.6" |
| Jupiter | 2.8" | 1.1" |
| Saturn | 8.6" | 0.5" |
- Median error: 1.4 arcseconds
- Maximum error: 8.6 arcseconds (Saturn longitude)
- Method: Topocentric ecliptic coordinate comparison
- Observer: 37.751°N, 97.822°W
For context, the human eye has a resolution of approximately 60 arcseconds, and typical planetarium software operates at 10-30 arcsecond precision. The maximum error of 8.6 arcseconds represents approximately 0.5% of the Moon's apparent diameter.
Complete validation methodology, coordinate frame specifications, and reproducibility instructions are documented in docs/VALIDATION.md.
- Runtime: Node.js with Express
- Calculations: astronomy-engine library (VSOP87/ELP2000 planetary theories)
- Design: Stateless REST API
- Deployment: Single process, horizontal scaling supported
- Planetary Theory: VSOP87 (Variations Séculaires des Orbites Planétaires)
- Lunar Theory: ELP2000 (Ephéméride Lunaire Parisienne)
- Corrections: Light-time delay, stellar aberration, parallax, atmospheric refraction
- Coordinate Systems: Ecliptic (J2000), Equatorial (J2000), Horizontal (topocentric)
Observer location is resolved using the following priority order:
- Control location (highest) - Set via
POST /api/control/locationor control CLI commands - Config manual mode - Fixed location from
config/settings.local.jsonwhenobserver.mode: "manual" - Query parameters - Temporary override via
?lat=X&lon=Y&elev=Z - IP geolocation - Automatic detection when
observer.mode: "auto"(default) - Fallback (lowest) - Memphis, Tennessee (35.1184°N, 90.0489°W) if all else fails
See config/README.md for complete configuration documentation.
- Computation time: 25-75ms typical per request
- Update rate: Suitable for 1-10 Hz display refresh
- Scalability: Stateless design enables horizontal scaling
- Caching: No built-in caching (stateless design principle)
The Antikythera mechanism (circa 100 BCE) was discovered in 1901 in a Roman-era shipwreck off the Greek island of Antikythera. The device represents one of the most sophisticated technological artifacts from the ancient world, using approximately 30 meshing bronze gears to predict:
- Solar and lunar positions
- Moon phases
- Eclipse timing (Saros cycle)
- Planetary positions (Mercury through Saturn)
- Olympic game cycles
- Ancient Greek calendar cycles (Metonic, Callippic)
This software recreates the mechanism's computational capabilities using modern astronomical calculations via the astronomy-engine library, while maintaining similar output formats for compatibility with physical implementations. The /api/display endpoint specifically provides data formatted for stepper motors, servos, and display elements, enabling construction of physical replicas with accurate astronomical behavior.
Interactive astronomy learning tools demonstrating celestial mechanics, coordinate systems, and historical astronomical knowledge. The system provides real-time data suitable for classroom demonstrations, museum exhibits, and public outreach.
The /api/display endpoint provides motor control data suitable for:
- Motorized orreries and planetariums
- Physical Antikythera mechanism replicas
- Robotic telescope pointing systems (within precision limits)
- Interactive museum exhibits with mechanical components
Update hints in the response suggest appropriate refresh intervals for mechanical (10 seconds) and digital (1 second) components.
Planetarium visualization, observatory displays, public astronomy installations, and educational kiosks. The comprehensive coordinate data supports various display formats and projection systems.
Astronomical algorithm testing, historical astronomy research, and coordinate system demonstrations. The validation against HORIZONS and reproducibility metadata make the system suitable for educational research contexts.
antikythera-engine-2/
├── server.js # Main API server
├── src/
│ ├── constants/
│ │ └── validation.js # Validation statistics
│ └── utils/
│ └── metadata.js # Version and git SHA extraction
├── scripts/
│ ├── validate-simple.js # Quick validation (Moon only)
│ ├── validate-all-bodies.js # Comprehensive HORIZONS comparison
│ ├── test-config-integration.sh # End-to-end config system tests (40 scenarios)
│ └── dump-horizons.js # HORIZONS API diagnostic tool
├── docs/
│ └── VALIDATION.md # Complete validation methodology
└── public/
└── index.html # Example visualization client
- Language: JavaScript (Node.js)
- Framework: Express
- Astronomy: astronomy-engine v2.1.19
- Testing: Jest unit tests (78 tests), HORIZONS validation scripts, configuration integration tests (40 scenarios)
- Documentation: Markdown with validation reports
- Maximum validated error: 8.6 arcseconds (suitable for display applications)
- Not validated for occultation predictions requiring sub-arcsecond precision
- Not validated for satellite tracking or orbital mechanics
- Single validation point (2025-10-26); extended temporal validation pending
- Primary output: J2000 ecliptic coordinates (apparent positions)
- Atmospheric refraction applied for horizontal coordinates
- No proper motion corrections (suitable for solar system bodies only)
- IP geolocation provides city-level accuracy (may vary by several degrees)
- Manual configuration via
config/settings.local.jsonrecommended for fixed installations - Query parameter override available for per-request location specification
- All internal calculations use UTC; timezone handling for display purposes only
- No caching (every request performs full calculation)
- No batch computation endpoints
- Computation time scales linearly with number of bodies
This software is currently in active development. While validation demonstrates suitable precision for intended use cases, the API and response formats may evolve. Production deployment should account for potential changes.
Contributions are welcome in the following areas:
- Multiple time periods (historical and future dates)
- Multiple observer locations (latitude/longitude diversity)
- Statistical analysis of error distribution
- Additional use case examples
- Physical mechanism integration guides
- Coordinate system transformation documentation
- Additional astronomical phenomena (transits, planetary configurations)
- Performance optimizations
- Batch computation endpoints
- Example Arduino/Raspberry Pi integration code
- Stepper motor control libraries
- Display formatting utilities
- API Documentation: Complete endpoint descriptions in this README
- Configuration:
config/README.md(settings, validation, hot reload) - Control Mode:
docs/CONTROL_MODE.md(classroom control, authentication) - CLI/REPL:
docs/CLI-REPL.md(interactive commands, data export) - Validation Results:
docs/VALIDATION.md(methodology, coordinate frames, error analysis) - Technical Operations:
docs/TECHNICAL_OPERATIONS_MANUAL.md(architecture, performance) - Precision Metadata: Included in all
/api/systemresponses - Coordinate Systems: Documented in validation file
If using this software in research, education, or publication, please cite:
Fennell, D. (2025). Antikythera Engine: Astronomical Ephemeris API.
GitHub: https://github.com/mojoatomic/antikythera-engine-2
For academic citation, see CITATION.cff in repository.
Copyright 2025 Doug Fennell
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- Astronomy calculations: Don Cross (astronomy-engine library)
- Validation authority: NASA JPL HORIZONS System
- Historical inspiration: Ancient Greek Antikythera mechanism (circa 100 BCE)
- Planetary theories: VSOP87 (Bureau des Longitudes), ELP2000 (Paris Observatory)