From b032fb596f950f52d53552d3eb0a7d35d18555a0 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Sun, 8 Mar 2026 17:30:54 -0400 Subject: [PATCH] style: fix prettier table formatting in wiki --- .wiki/API-Reference.md | 193 +++++++++++++++++------------------ .wiki/Architecture.md | 18 ++-- .wiki/Crescent-Visibility.md | 30 +++--- .wiki/Ephemeris.md | 4 +- .wiki/Getting-Started.md | 39 +++---- .wiki/Home.md | 6 +- .wiki/Observer-Model.md | 18 ++-- .wiki/Reference-Frames.md | 20 ++-- .wiki/Time-Scales.md | 2 +- .wiki/Validation.md | 24 +++-- 10 files changed, 184 insertions(+), 170 deletions(-) diff --git a/.wiki/API-Reference.md b/.wiki/API-Reference.md index a94b5e1..87c6b3f 100644 --- a/.wiki/API-Reference.md +++ b/.wiki/API-Reference.md @@ -14,10 +14,10 @@ async function initKernels(config?: KernelConfig): Promise ```ts interface KernelConfig { - planetary?: KernelSource // DE442S source. Default: auto-download - leapSeconds?: KernelSource // LSK source. Default: auto-download - cacheDir?: string // Cache dir. Default: ~/.cache/moon-sighting - checksumOverride?: string // SHA-256 override for de442s.bsp + planetary?: KernelSource // DE442S source. Default: auto-download + leapSeconds?: KernelSource // LSK source. Default: auto-download + cacheDir?: string // Cache dir. Default: ~/.cache/moon-sighting + checksumOverride?: string // SHA-256 override for de442s.bsp } type KernelSource = @@ -45,13 +45,13 @@ async function getMoonSightingReport( ```ts interface Observer { - lat: number // Geodetic latitude, degrees (north positive) - lon: number // Longitude, degrees (east positive) - elevation: number // Height above WGS84 ellipsoid, meters - name?: string // Optional label - deltaT?: number // Override TT - UT1, seconds - ut1utc?: number // Override UT1 - UTC, seconds (takes precedence over deltaT) - pressure?: number // Atmospheric pressure, mbar (default 1013.25) + lat: number // Geodetic latitude, degrees (north positive) + lon: number // Longitude, degrees (east positive) + elevation: number // Height above WGS84 ellipsoid, meters + name?: string // Optional label + deltaT?: number // Override TT - UT1, seconds + ut1utc?: number // Override UT1 - UTC, seconds (takes precedence over deltaT) + pressure?: number // Atmospheric pressure, mbar (default 1013.25) temperature?: number // Temperature, Celsius (default 15) } ``` @@ -61,7 +61,7 @@ interface Observer { ```ts interface SightingOptions { kernels?: KernelConfig - bestTimeMethod?: 'heuristic' | 'optimized' // default: 'heuristic' + bestTimeMethod?: 'heuristic' | 'optimized' // default: 'heuristic' } ``` @@ -75,15 +75,15 @@ interface MoonSightingReport { // Event times sunsetUTC: Date | null moonsetUTC: Date | null - lagMinutes: number | null // moonset - sunset, minutes - bestTimeUTC: Date | null // T_sunset + 4/9 × lag - bestTimeWindowUTC: [Date, Date] | null // ±20 min around best time + lagMinutes: number | null // moonset - sunset, minutes + bestTimeUTC: Date | null // T_sunset + 4/9 × lag + bestTimeWindowUTC: [Date, Date] | null // ±20 min around best time // Body positions at best time - moonPosition: AzAlt | null // { azimuth, altitude } + moonPosition: AzAlt | null // { azimuth, altitude } sunPosition: AzAlt | null - illumination: number | null // percent, 0–100 - moonAge: number | null // hours since conjunction + illumination: number | null // percent, 0–100 + moonAge: number | null // hours since conjunction // Crescent geometry at best time geometry: CrescentGeometry | null @@ -119,20 +119,20 @@ function getMoonPosition( **Parameters:** -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `date` | `Date?` | Date to evaluate. Defaults to now | -| `lat` | `number` | Geodetic latitude, degrees (north positive) | -| `lon` | `number` | Longitude, degrees (east positive) | -| `elevation` | `number?` | Height above ellipsoid, meters. Default: 0 | +| Parameter | Type | Description | +| ----------- | --------- | ------------------------------------------- | +| `date` | `Date?` | Date to evaluate. Defaults to now | +| `lat` | `number` | Geodetic latitude, degrees (north positive) | +| `lon` | `number` | Longitude, degrees (east positive) | +| `elevation` | `number?` | Height above ellipsoid, meters. Default: 0 | **MoonPosition:** ```ts interface MoonPosition { - azimuth: number // Degrees from North, clockwise (0–360) - altitude: number // Apparent altitude, degrees (refraction applied) - distance: number // Earth center to Moon center, km + azimuth: number // Degrees from North, clockwise (0–360) + altitude: number // Apparent altitude, degrees (refraction applied) + distance: number // Earth center to Moon center, km parallacticAngle: number // Angle between zenith and north pole as seen from Moon, radians } ``` @@ -143,9 +143,9 @@ interface MoonPosition { import { getMoonPosition } from 'moon-sighting' const pos = getMoonPosition(new Date(), 51.5074, -0.1278, 10) -console.log(pos.azimuth) // 214.7 -console.log(pos.altitude) // 38.2 -console.log(pos.distance) // 384400 +console.log(pos.azimuth) // 214.7 +console.log(pos.altitude) // 38.2 +console.log(pos.distance) // 384400 ``` --- @@ -160,18 +160,18 @@ function getMoonIllumination(date?: Date): MoonIlluminationResult **Parameters:** -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `date` | `Date?` | Date to evaluate. Defaults to now | +| Parameter | Type | Description | +| --------- | ------- | --------------------------------- | +| `date` | `Date?` | Date to evaluate. Defaults to now | **MoonIlluminationResult:** ```ts interface MoonIlluminationResult { - fraction: number // Illuminated fraction, 0 (new moon) to 1 (full moon) - phase: number // Position in 0–1 cycle: 0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter - angle: number // Position angle of bright limb midpoint, eastward from north, radians - isWaxing: boolean // True when elongation is increasing (new moon toward full moon) + fraction: number // Illuminated fraction, 0 (new moon) to 1 (full moon) + phase: number // Position in 0–1 cycle: 0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter + angle: number // Position angle of bright limb midpoint, eastward from north, radians + isWaxing: boolean // True when elongation is increasing (new moon toward full moon) } ``` @@ -181,9 +181,9 @@ interface MoonIlluminationResult { import { getMoonIllumination } from 'moon-sighting' const illum = getMoonIllumination() -console.log(illum.fraction) // 0.143 -console.log(illum.phase) // 0.09 -console.log(illum.isWaxing) // true +console.log(illum.fraction) // 0.143 +console.log(illum.phase) // 0.09 +console.log(illum.isWaxing) // true ``` --- @@ -200,12 +200,12 @@ function getMoonPhase(date?: Date): MoonPhaseResult ```ts interface MoonPhaseResult { - phase: MoonPhaseName // 'new-moon' | 'waxing-crescent' | ... | 'waning-crescent' - phaseName: string // Display name, e.g. 'Waxing Crescent' - phaseSymbol: string // Moon emoji, e.g. '🌒' - illumination: number // 0–100 percent - age: number // hours since last new moon - elongationDeg: number // Moon - Sun ecliptic longitude, [0, 360) + phase: MoonPhaseName // 'new-moon' | 'waxing-crescent' | ... | 'waning-crescent' + phaseName: string // Display name, e.g. 'Waxing Crescent' + phaseSymbol: string // Moon emoji, e.g. '🌒' + illumination: number // 0–100 percent + age: number // hours since last new moon + elongationDeg: number // Moon - Sun ecliptic longitude, [0, 360) isWaxing: boolean nextNewMoon: Date nextFullMoon: Date @@ -232,27 +232,27 @@ function getMoonVisibilityEstimate( **Parameters:** -| Parameter | Type | Description | -| --------- | ---- | ----------- | -| `date` | `Date?` | Observation time. Defaults to now. Use a post-sunset time for meaningful results | -| `lat` | `number` | Geodetic latitude, degrees (north positive) | -| `lon` | `number` | Longitude, degrees (east positive) | -| `elevation` | `number?` | Height above ellipsoid, meters. Default: 0 | +| Parameter | Type | Description | +| ----------- | --------- | -------------------------------------------------------------------------------- | +| `date` | `Date?` | Observation time. Defaults to now. Use a post-sunset time for meaningful results | +| `lat` | `number` | Geodetic latitude, degrees (north positive) | +| `lon` | `number` | Longitude, degrees (east positive) | +| `elevation` | `number?` | Height above ellipsoid, meters. Default: 0 | **MoonVisibilityEstimate:** ```ts interface MoonVisibilityEstimate { - V: number // Odeh V parameter: V = ARCV - f(W). Positive = crescent exceeds threshold - zone: OdehZone // 'A' | 'B' | 'C' | 'D' - description: string // Human-readable zone description - isVisibleNakedEye: boolean // True for zone A + V: number // Odeh V parameter: V = ARCV - f(W). Positive = crescent exceeds threshold + zone: OdehZone // 'A' | 'B' | 'C' | 'D' + description: string // Human-readable zone description + isVisibleNakedEye: boolean // True for zone A isVisibleWithOpticalAid: boolean // True for zones A and B - ARCL: number // Arc of light (elongation), degrees - ARCV: number // Arc of vision (Moon alt - Sun alt, airless), degrees - W: number // Topocentric crescent width, arc minutes - moonAboveHorizon: boolean // True when Moon is above the horizon at the given time - isApproximate: true // Always true: Meeus approximation, not DE442S + ARCL: number // Arc of light (elongation), degrees + ARCV: number // Arc of vision (Moon alt - Sun alt, airless), degrees + W: number // Topocentric crescent width, arc minutes + moonAboveHorizon: boolean // True when Moon is above the horizon at the given time + isApproximate: true // Always true: Meeus approximation, not DE442S } ``` @@ -263,9 +263,9 @@ import { getMoonVisibilityEstimate } from 'moon-sighting' // ~40 min after sunset in Mecca, day after new moon const est = getMoonVisibilityEstimate(new Date('2025-03-02T15:30:00Z'), 21.42, 39.83) -console.log(est.zone) // 'A' through 'D' -console.log(est.V) // Odeh V parameter -console.log(est.isVisibleNakedEye) // true/false +console.log(est.zone) // 'A' through 'D' +console.log(est.V) // Odeh V parameter +console.log(est.isVisibleNakedEye) // true/false ``` --- @@ -275,22 +275,17 @@ console.log(est.isVisibleNakedEye) // true/false Combined kernel-free snapshot: phase, position, illumination, and visibility estimate in one call. ```ts -function getMoon( - date?: Date, - lat: number, - lon: number, - elevation?: number, -): MoonSnapshot +function getMoon(date?: Date, lat: number, lon: number, elevation?: number): MoonSnapshot ``` **MoonSnapshot:** ```ts interface MoonSnapshot { - phase: MoonPhaseResult // getMoonPhase() result - position: MoonPosition // getMoonPosition() result + phase: MoonPhaseResult // getMoonPhase() result + position: MoonPosition // getMoonPosition() result illumination: MoonIlluminationResult // getMoonIllumination() result - visibility: MoonVisibilityEstimate // getMoonVisibilityEstimate() result + visibility: MoonVisibilityEstimate // getMoonVisibilityEstimate() result } ``` @@ -300,11 +295,11 @@ interface MoonSnapshot { import { getMoon } from 'moon-sighting' const moon = getMoon(new Date(), 51.5074, -0.1278, 10) -console.log(moon.phase.phaseName) // 'Waxing Crescent' -console.log(moon.phase.phaseSymbol) // '🌒' -console.log(moon.position.altitude) // degrees above horizon +console.log(moon.phase.phaseName) // 'Waxing Crescent' +console.log(moon.phase.phaseSymbol) // '🌒' +console.log(moon.position.altitude) // degrees above horizon console.log(moon.illumination.fraction) // 0.0 to 1.0 -console.log(moon.visibility.zone) // 'A' through 'D' +console.log(moon.visibility.zone) // 'A' through 'D' ``` --- @@ -329,8 +324,8 @@ interface SunMoonEvents { moonsetUTC: Date | null sunriseUTC: Date | null moonriseUTC: Date | null - civilTwilightEndUTC: Date | null // Sun at -6° - nauticalTwilightEndUTC: Date | null // Sun at -12° + civilTwilightEndUTC: Date | null // Sun at -6° + nauticalTwilightEndUTC: Date | null // Sun at -12° astronomicalTwilightEndUTC: Date | null // Sun at -18° } ``` @@ -369,11 +364,11 @@ async function verifyKernels(config?: KernelConfig): Promise<{ ```ts interface CrescentGeometry { - ARCL: number // Elongation (Sun-Moon angular separation), degrees - ARCV: number // Moon altitude - Sun altitude (airless), degrees - DAZ: number // Sun azimuth - Moon azimuth, [-180, 180], degrees - W: number // Topocentric crescent width, arc minutes - lag: number // Moonset - sunset, minutes + ARCL: number // Elongation (Sun-Moon angular separation), degrees + ARCV: number // Moon altitude - Sun altitude (airless), degrees + DAZ: number // Sun azimuth - Moon azimuth, [-180, 180], degrees + W: number // Topocentric crescent width, arc minutes + lag: number // Moonset - sunset, minutes } ``` @@ -381,13 +376,13 @@ interface CrescentGeometry { ```ts interface YallopResult { - q: number // Continuous q parameter + q: number // Continuous q parameter category: YallopCategory // 'A' | 'B' | 'C' | 'D' | 'E' | 'F' description: string - isVisibleNakedEye: boolean // A or B - requiresOpticalAid: boolean // C or D - isBelowDanjonLimit: boolean // F - Wprime: number // W' used in q formula, arc minutes + isVisibleNakedEye: boolean // A or B + requiresOpticalAid: boolean // C or D + isBelowDanjonLimit: boolean // F + Wprime: number // W' used in q formula, arc minutes } ``` @@ -395,10 +390,10 @@ interface YallopResult { ```ts interface OdehResult { - V: number // Continuous V parameter - zone: OdehZone // 'A' | 'B' | 'C' | 'D' + V: number // Continuous V parameter + zone: OdehZone // 'A' | 'B' | 'C' | 'D' description: string - isVisibleNakedEye: boolean // A + isVisibleNakedEye: boolean // A isVisibleWithOpticalAid: boolean // A or B } ``` @@ -415,8 +410,8 @@ See the `getMoon` section above for the full definition. ```ts interface AzAlt { - azimuth: number // Degrees from North, clockwise (0–360) - altitude: number // Degrees above horizon (negative = below) + azimuth: number // Degrees from North, clockwise (0–360) + altitude: number // Degrees above horizon (negative = below) } ``` @@ -425,13 +420,13 @@ interface AzAlt { ## Constants ```ts -YALLOP_THRESHOLDS // { A: 0.216, B: -0.014, C: -0.160, D: -0.232, E: -0.293 } -ODEH_THRESHOLDS // { A: 5.65, B: 2.00, C: -0.96 } -WGS84 // { a: 6378137.0, invF: 298.257223563, f, b, e2 } +YALLOP_THRESHOLDS // { A: 0.216, B: -0.014, C: -0.160, D: -0.232, E: -0.293 } +ODEH_THRESHOLDS // { A: 5.65, B: 2.00, C: -0.96 } +WGS84 // { a: 6378137.0, invF: 298.257223563, f, b, e2 } YALLOP_DESCRIPTIONS // Record -ODEH_DESCRIPTIONS // Record +ODEH_DESCRIPTIONS // Record ``` --- -*Previous: [Home](Home) | Next: [Architecture](Architecture)* +_Previous: [Home](Home) | Next: [Architecture](Architecture)_ diff --git a/.wiki/Architecture.md b/.wiki/Architecture.md index dab0781..b7b3fa4 100644 --- a/.wiki/Architecture.md +++ b/.wiki/Architecture.md @@ -123,17 +123,17 @@ Target: a full sighting report (sunset + moonset + best-time geometry + Yallop + A crescent sighting report's accuracy is limited by the worst source in the chain: -| Source | Contribution | -| ------ | ------------ | -| DE442S position error | < 1 km (~0.001 arcsec at Moon distance) | -| IERS Q·R·W transform (with user-supplied EOP) | < 1 mas | -| IERS Q·R·W transform (polynomial ΔT approximation) | < 5 arcsec | -| WGS84 observer position | < 1 m (negligible in angle) | -| Bennett refraction (standard atmosphere) | < 1 arcmin for alt > 5° | -| Bennett refraction (non-standard conditions) | up to 15 arcmin near horizon | +| Source | Contribution | +| -------------------------------------------------- | --------------------------------------- | +| DE442S position error | < 1 km (~0.001 arcsec at Moon distance) | +| IERS Q·R·W transform (with user-supplied EOP) | < 1 mas | +| IERS Q·R·W transform (polynomial ΔT approximation) | < 5 arcsec | +| WGS84 observer position | < 1 m (negligible in angle) | +| Bennett refraction (standard atmosphere) | < 1 arcmin for alt > 5° | +| Bennett refraction (non-standard conditions) | up to 15 arcmin near horizon | In practice, refraction uncertainty dominates all other error sources for crescent sighting near the horizon. --- -*Previous: [API Reference](API-Reference) | Next: [Crescent Visibility](Crescent-Visibility)* +_Previous: [API Reference](API-Reference) | Next: [Crescent Visibility](Crescent-Visibility)_ diff --git a/.wiki/Crescent-Visibility.md b/.wiki/Crescent-Visibility.md index f9537b0..8d4b2e9 100644 --- a/.wiki/Crescent-Visibility.md +++ b/.wiki/Crescent-Visibility.md @@ -48,14 +48,14 @@ This polynomial represents the minimum ARCV observed in historical crescent sigh ### Categories -| Category | q range | Meaning | -| -------- | ------- | ------- | -| A | q > +0.216 | Easily visible to the naked eye | -| B | q > −0.014 | Visible under perfect conditions | -| C | q > −0.160 | May need optical aid to locate; naked eye possible | -| D | q > −0.232 | Optical aid necessary; naked eye not possible | -| E | q > −0.293 | Not visible even with telescope | -| F | q ≤ −0.293 | Below Danjon limit; crescent cannot form | +| Category | q range | Meaning | +| -------- | ---------- | -------------------------------------------------- | +| A | q > +0.216 | Easily visible to the naked eye | +| B | q > −0.014 | Visible under perfect conditions | +| C | q > −0.160 | May need optical aid to locate; naked eye possible | +| D | q > −0.232 | Optical aid necessary; naked eye not possible | +| E | q > −0.293 | Not visible even with telescope | +| F | q ≤ −0.293 | Below Danjon limit; crescent cannot form | Category F corresponds to ARCL below roughly 7° (the Danjon limit), where the Moon is geometrically too close to the Sun for the crescent arc to sustain itself. @@ -87,12 +87,12 @@ V = ARCV - (11.8371 - 6.3226·W + 0.7319·W² - 0.1018·W³) ### Zones -| Zone | V range | Meaning | -| ---- | ------- | ------- | -| A | V ≥ 5.65 | Visible with naked eye | -| B | V ≥ 2.00 | Visible with optical aid; may be naked eye under excellent conditions | -| C | V ≥ −0.96 | Visible with optical aid only | -| D | V < −0.96 | Not visible even with optical aid | +| Zone | V range | Meaning | +| ---- | --------- | --------------------------------------------------------------------- | +| A | V ≥ 5.65 | Visible with naked eye | +| B | V ≥ 2.00 | Visible with optical aid; may be naked eye under excellent conditions | +| C | V ≥ −0.96 | Visible with optical aid only | +| D | V < −0.96 | Not visible even with optical aid | ### Key differences from Yallop @@ -146,4 +146,4 @@ This approach requires additional atmospheric inputs (aerosol optical depth, hum --- -*Previous: [Architecture](Architecture) | Next: [Ephemeris](Ephemeris)* +_Previous: [Architecture](Architecture) | Next: [Ephemeris](Ephemeris)_ diff --git a/.wiki/Ephemeris.md b/.wiki/Ephemeris.md index c4dd739..562fc16 100644 --- a/.wiki/Ephemeris.md +++ b/.wiki/Ephemeris.md @@ -60,6 +60,7 @@ Each record covers a fixed time interval and stores coefficients for X, Y, Z: ``` The polynomial degree n is derived from RSIZE (record size in doubles): + ``` n = (RSIZE - 2) / 3 - 1 ``` @@ -93,6 +94,7 @@ result = c_0 + x·b_1 - b_2 This produces the position. Velocity requires the derivative d(result)/dt, computed via the Chebyshev derivative recurrence, not by finite differencing, which would lose accuracy. Transforming from normalized domain back to physical time: + ``` x = (et - MID) / RADIUS dx/dt = 1/RADIUS @@ -152,4 +154,4 @@ See [Validation](Validation) for the test methodology. --- -*Previous: [Crescent Visibility](Crescent-Visibility) | Next: [Time Scales](Time-Scales)* +_Previous: [Crescent Visibility](Crescent-Visibility) | Next: [Time Scales](Time-Scales)_ diff --git a/.wiki/Getting-Started.md b/.wiki/Getting-Started.md index 2182a08..909dc22 100644 --- a/.wiki/Getting-Started.md +++ b/.wiki/Getting-Started.md @@ -55,17 +55,17 @@ import { initKernels, getMoonSightingReport } from 'moon-sighting' await initKernels() const observer = { - lat: 51.5074, // London + lat: 51.5074, // London lon: -0.1278, - elevation: 10, // meters above WGS84 ellipsoid + elevation: 10, // meters above WGS84 ellipsoid name: 'London, UK', } const report = await getMoonSightingReport(new Date('2025-03-29'), observer) // Summary -console.log(report.yallop.category) // 'A' through 'F' -console.log(report.odeh.zone) // 'A' through 'D' +console.log(report.yallop.category) // 'A' through 'F' +console.log(report.odeh.zone) // 'A' through 'D' console.log(report.guidance) // Event times @@ -92,26 +92,26 @@ import { getMoonPhase, getMoonPosition, getMoonIllumination } from 'moon-sightin // Phase name, illumination percent, and next new/full moon dates const phase = getMoonPhase() -console.log(phase.phase) // 'waxing-crescent' -console.log(phase.illumination) // 23.4 -console.log(phase.age) // 4.2 (hours since last new moon) -console.log(phase.nextFullMoon) // Date +console.log(phase.phase) // 'waxing-crescent' +console.log(phase.illumination) // 23.4 +console.log(phase.age) // 4.2 (hours since last new moon) +console.log(phase.nextFullMoon) // Date // Topocentric position: azimuth, altitude (refraction applied), distance // Accuracy: ~0.3° const pos = getMoonPosition(new Date(), 51.5074, -0.1278, 10) -console.log(pos.azimuth) // degrees from North, clockwise -console.log(pos.altitude) // degrees above horizon -console.log(pos.distance) // km from Earth center to Moon center +console.log(pos.azimuth) // degrees from North, clockwise +console.log(pos.altitude) // degrees above horizon +console.log(pos.distance) // km from Earth center to Moon center console.log(pos.parallacticAngle) // radians // Illumination fraction and phase cycle position // Accuracy: ~0.5% on fraction const illum = getMoonIllumination() -console.log(illum.fraction) // 0–1 (0=new, 1=full) -console.log(illum.phase) // 0–1 cycle position (0=new, 0.5=full) -console.log(illum.angle) // bright limb position angle, radians -console.log(illum.isWaxing) // true when moving toward full moon +console.log(illum.fraction) // 0–1 (0=new, 1=full) +console.log(illum.phase) // 0–1 cycle position (0=new, 0.5=full) +console.log(illum.angle) // bright limb position angle, radians +console.log(illum.isWaxing) // true when moving toward full moon // All three accept an optional Date for historical or future queries const past = getMoonPhase(new Date('2024-01-01')) @@ -127,7 +127,10 @@ import { initKernels, getSunMoonEvents } from 'moon-sighting' await initKernels() const events = await getSunMoonEvents(new Date('2025-03-29'), { - lat: 21.4225, lon: 39.8262, elevation: 300, name: 'Mecca' + lat: 21.4225, + lon: 39.8262, + elevation: 300, + name: 'Mecca', }) console.log(events.sunsetUTC) @@ -173,7 +176,7 @@ await initKernels({ // IERS Bulletin A value for UT1-UTC (current, as of 2025-03) await getMoonSightingReport(date, { ...observer, - ut1utc: 0.0341, // seconds, from IERS Bulletin A + ut1utc: 0.0341, // seconds, from IERS Bulletin A }) ``` @@ -202,4 +205,4 @@ npx moon-sighting benchmark --- -*Previous: [Home](Home) | Next: [API Reference](API-Reference)* +_Previous: [Home](Home) | Next: [API Reference](API-Reference)_ diff --git a/.wiki/Home.md b/.wiki/Home.md index d1ae198..f684673 100644 --- a/.wiki/Home.md +++ b/.wiki/Home.md @@ -38,10 +38,12 @@ import { initKernels, getMoonSightingReport } from 'moon-sighting' await initKernels() const report = await getMoonSightingReport(new Date('2025-03-29'), { - lat: 51.5074, lon: -0.1278, elevation: 10 + lat: 51.5074, + lon: -0.1278, + elevation: 10, }) -console.log(report.yallop.category) // 'A' +console.log(report.yallop.category) // 'A' console.log(report.guidance) ``` diff --git a/.wiki/Observer-Model.md b/.wiki/Observer-Model.md index 6bdff41..b949672 100644 --- a/.wiki/Observer-Model.md +++ b/.wiki/Observer-Model.md @@ -5,6 +5,7 @@ The library uses the WGS84 (World Geodetic System 1984) reference ellipsoid, which is the standard for GPS coordinates, Google Maps, and most modern mapping systems. Key constants: + ``` a = 6378137.0 m (semi-major axis, equatorial radius) 1/f = 298.257223563 (inverse flattening) @@ -45,6 +46,7 @@ Up = (cos φ cos λ, cos φ sin λ, sin φ) ``` These are unit vectors. To convert a topocentric ECEF displacement Δ (in meters) to ENU: + ``` e = East · Δ n = North · Δ @@ -67,6 +69,7 @@ Altitude is the angle above the horizontal plane: 0° = horizon, 90° = zenith, The Moon's geocentric position (from the ephemeris) differs from its topocentric position (as seen by a surface observer) because the Moon is close enough that the baseline between Earth's center and the observer's surface position is significant. This is the diurnal parallax. The correction is simply: + ``` topocentric_direction = moon_ITRS − observer_ITRS ``` @@ -103,6 +106,7 @@ Standard conditions: P = 1013.25 mbar, T = 15°C. The correction factors adjust ### Accuracy limits The Bennett formula is accurate to: + - ~0.1 arcminute for h > 5° - ~0.5 arcminute for h = 2°–5° - ~1–2 arcminutes for h < 2° @@ -114,16 +118,16 @@ This is why crescent sighting criteria use "airless" (refraction-free) altitudes ### When to apply refraction -| Use case | Mode | -|----------|------| -| Yallop ARCV input | Airless | -| Odeh ARCV input | Airless | -| Sunset/moonset threshold | Standard refraction | -| "Where to look" altitude output | Standard refraction | +| Use case | Mode | +| ------------------------------------ | ------------------- | +| Yallop ARCV input | Airless | +| Odeh ARCV input | Airless | +| Sunset/moonset threshold | Standard refraction | +| "Where to look" altitude output | Standard refraction | | Civil/nautical/astronomical twilight | Standard refraction | moon-sighting computes both airless and apparent altitudes for each body position and uses the appropriate one for each purpose. --- -*Previous: [Reference Frames](Reference-Frames) | Next: [Validation](Validation)* +_Previous: [Reference Frames](Reference-Frames) | Next: [Validation](Validation)_ diff --git a/.wiki/Reference-Frames.md b/.wiki/Reference-Frames.md index a89f351..3c7adc1 100644 --- a/.wiki/Reference-Frames.md +++ b/.wiki/Reference-Frames.md @@ -13,6 +13,7 @@ The IERS Conventions (2010) define the standard transformation: ``` Where: + - **GCRS** = Geocentric Celestial Reference System (essentially the inertial J2000 frame at Earth's center) - **ITRS** = International Terrestrial Reference System (Earth-fixed frame, rotates with the solid Earth) - **Q(t)** = celestial motion matrix (precession + nutation) @@ -31,6 +32,7 @@ The IAU 2006 precession model and IAU 2000A nutation model together parameterize - **s:** CIO locator, a small angle that ensures continuity of the CIO position The CIP X,Y series has: + - A polynomial part (degree 5 in T = Julian centuries from J2000.0) - 1,306 luni-solar nutation terms - 687 planetary nutation terms @@ -71,13 +73,13 @@ moon-sighting defaults to xp = yp = 0. Supply current values from IERS Bulletin ## IAU 2000A vs 2000B -| Feature | 2000A | 2000B | -|---------|-------|-------| -| Luni-solar terms | 1,306 | 77 | -| Planetary terms | 687 | 0 | -| Max error | < 0.1 mas | < 1 mas | -| Computation | ~2× slower | fast | -| Suitable for | moon sighting | approximate work | +| Feature | 2000A | 2000B | +| ---------------- | ------------- | ---------------- | +| Luni-solar terms | 1,306 | 77 | +| Planetary terms | 687 | 0 | +| Max error | < 0.1 mas | < 1 mas | +| Computation | ~2× slower | fast | +| Suitable for | moon sighting | approximate work | For crescent sighting at the horizon where refraction dominates, 2000B is more than sufficient. moon-sighting defaults to 2000A for correctness; 2000B will be available as a compile-time option for size-sensitive builds. @@ -96,13 +98,15 @@ See [Observer Model](Observer-Model) for the WGS84 and ENU computation details. ## Accuracy With user-supplied EOP (Earth orientation parameters from IERS Bulletin A): + - Azimuth/altitude accuracy: < 0.1 arcsecond (dominated by nutation model error) With polynomial ΔT approximation (no user EOP): + - Azimuth/altitude accuracy: typically < 5 arcseconds, occasionally up to ~30 arcseconds in pathological ΔT errors For comparison, the Moon's angular diameter is ~1800 arcseconds, and refraction uncertainty near the horizon is 600–900 arcseconds. The frame transform is not the limiting factor for crescent sighting. --- -*Previous: [Time Scales](Time-Scales) | Next: [Observer Model](Observer-Model)* +_Previous: [Time Scales](Time-Scales) | Next: [Observer Model](Observer-Model)_ diff --git a/.wiki/Time-Scales.md b/.wiki/Time-Scales.md index 49aaa3e..bab5948 100644 --- a/.wiki/Time-Scales.md +++ b/.wiki/Time-Scales.md @@ -106,4 +106,4 @@ The NAIF LSK (`naif0012.tls`) is a plain-text file in NAIF text kernel format. I --- -*Previous: [Ephemeris](Ephemeris) | Next: [Reference Frames](Reference-Frames)* +_Previous: [Ephemeris](Ephemeris) | Next: [Reference Frames](Reference-Frames)_ diff --git a/.wiki/Validation.md b/.wiki/Validation.md index 5b1553f..8338abe 100644 --- a/.wiki/Validation.md +++ b/.wiki/Validation.md @@ -21,6 +21,7 @@ NASA NAIF's SPICE toolkit is the authoritative reference for reading JPL ephemer Any deviation in the SPK Chebyshev evaluation from SPICE indicates a parsing or algorithm error in moon-sighting. **How to compare:** + ```python import spiceypy as spice spice.furnsh('de442s.bsp') @@ -33,6 +34,7 @@ print(state[:3]) # position in km ``` The moon-sighting equivalent: + ```ts const kernel = SpkKernel.fromFile('de442s.bsp') const ts = computeTimeScales(new Date('2025-03-29T20:00:00Z')) @@ -44,6 +46,7 @@ Expected agreement: < 1 meter (floating-point evaluation precision). ### JPL Horizons JPL Horizons is the online solar system ephemeris service. It uses the same JPL ephemerides and provides tabular output for: + - Apparent RA/Dec and az/alt for any observer and time - Observer-centered quantities (elongation, illumination, phase angle) - Rise/transit/set times @@ -52,6 +55,7 @@ Horizons uses SPICE internally, so it represents an independent end-to-end valid **How to use for validation:** Go to https://ssd.jpl.nasa.gov/horizons/, select: + - Target body: Moon (or Sun) - Observer location: user-defined geodetic lat/lon/elevation - Time span: the date of interest @@ -61,15 +65,15 @@ Compare Horizons' output with moon-sighting's topocentric az/alt. Differences of ## Acceptance thresholds -| Quantity | Expected error vs SPICE | Notes | -|----------|------------------------|-------| -| Geocentric position | < 1 m (< 0.001 arcsec) | SPK parsing precision | -| Topocentric az/alt (with EOP) | < 0.1 arcsec | Frame transform precision | -| Topocentric az/alt (polynomial ΔT) | < 30 arcsec | ΔT polynomial error | -| ARCL | < 1 arcsec | Derived from positions | -| ARCV | < 30 arcsec | Dominated by ΔT uncertainty | -| Yallop q | < 0.005 | q is dimensionless; <0.005 difference = same category in most cases | -| Sunset/moonset | < 10 seconds | Root-finding convergence | +| Quantity | Expected error vs SPICE | Notes | +| ---------------------------------- | ----------------------- | ------------------------------------------------------------------- | +| Geocentric position | < 1 m (< 0.001 arcsec) | SPK parsing precision | +| Topocentric az/alt (with EOP) | < 0.1 arcsec | Frame transform precision | +| Topocentric az/alt (polynomial ΔT) | < 30 arcsec | ΔT polynomial error | +| ARCL | < 1 arcsec | Derived from positions | +| ARCV | < 30 arcsec | Dominated by ΔT uncertainty | +| Yallop q | < 0.005 | q is dimensionless; <0.005 difference = same category in most cases | +| Sunset/moonset | < 10 seconds | Root-finding convergence | ## Validation suite @@ -98,4 +102,4 @@ Be cautious: ICOP records include weather and observer acuity information that t --- -*Previous: [Observer Model](Observer-Model) | Next: [API Reference](API-Reference)* +_Previous: [Observer Model](Observer-Model) | Next: [API Reference](API-Reference)_