## ADDED Requirements ### Requirement: SolarProvider implementation The system SHALL export `createSolarSystemProvider` returning a `SolarProvider` that computes real solar system body positions. #### Scenario: Sun altitude at noon - **WHEN** `getBody("Sun", observer, time)` is called for an observer at latitude `0` at local noon - **THEN** the returned `altitude` is approximately `90` minus the absolute declination offset ### Requirement: Astronomy.js engine injection The system SHALL accept an injectable `AstronomyEngine` in `createSolarSystemProvider`, defaulting to astronomy.js. #### Scenario: Deterministic engine injection - **WHEN** a deterministic mock `AstronomyEngine` is injected - **THEN** `getBodies` returns exactly the mocked positions ### Requirement: Keplerian propagator for custom planets The system SHALL propagate positions for planets with `OrbitalConfig` using a Keplerian solver from epoch. #### Scenario: Custom planet position - **WHEN** a `PlanetConfig` with valid `OrbitalConfig` is used as observer's planet or target body - **THEN** `getBodies` includes the custom planet with a propagated heliocentric position ### Requirement: Angular diameter computation The system SHALL compute `angularDiameterArcmin` as `2 * arctan(radius / distance) * (180/π * 60)`. #### Scenario: Sun angular diameter - **WHEN** `getBody("Sun", ...)` is called - **THEN** `angularDiameterArcmin` is approximately `32` arcminutes ### Requirement: Apparent magnitude for real bodies The system SHALL compute `apparentMagnitude` for real bodies using phase angle and distance via astronomy.js. #### Scenario: Venus magnitude range - **WHEN** `getBody("Venus", ...)` is called at multiple times - **THEN** `apparentMagnitude` varies between approximately `-4.9` and `-3.8` ### Requirement: Sunrise and sunset computation The system SHALL compute sunrise and sunset dates in UTC for a given observer and calendar date. #### Scenario: Equator sunrise consistency - **WHEN** `getSunrise` and `getSunset` are called for an equatorial observer on an equinox date - **THEN** sunrise is approximately 12 hours before sunset ### Requirement: Season computation The system SHALL export `getSeason` defaulting to Earth but accepting any `PlanetConfig` with `axialTilt` and `OrbitalConfig`. #### Scenario: Northern hemisphere season - **WHEN** `getSeason(new Date("2026-06-21"), EARTH)` is called - **THEN** the result is `summer` ### Requirement: Equinox and solstice prediction The system SHALL export `getNextEquinox` and `getNextSolstice` for any configured planet. #### Scenario: Next equinox after a date - **WHEN** `getNextEquinox(new Date("2026-01-01"), EARTH)` is called - **THEN** the result is a `Date` in March 2026 ### Requirement: Lunar phase The system SHALL export `getLunarPhase` returning phase (0–1), illumination percentage, and name for Earth/Moon. #### Scenario: Full moon phase - **WHEN** `getLunarPhase(fullMoonDate)` is called - **THEN** `phase` is approximately `0.5` - **AND** `illumination` is approximately `100%` ### Requirement: Eclipse prediction The system SHALL export `predictEclipse` for solar and lunar eclipses on Earth. #### Scenario: Known eclipse - **WHEN** `predictEclipse(new Date("2026-01-01"), "solar")` is called - **THEN** the returned `EclipseEvent` has a date after the input and a `totality` value ### Requirement: Constellation lookup The system SHALL export `getConstellation` mapping RA/Dec to IAU constellation name. #### Scenario: Orion lookup - **WHEN** `getConstellation(raOrion, decOrion)` is called with Orion's centre coordinates - **THEN** the result equals `"Orion"` ### Requirement: Atmosphere extinction for surface observers The system SHALL apply atmospheric extinction to apparent magnitudes when the observer has an atmosphere. #### Scenario: Horizon extinction - **WHEN** `getBody("Sun", surfaceObserver, time)` is called with the Sun near the horizon - **THEN** `apparentMagnitude` is dimmer than the vacuum value