pray-calc/.github/wiki/Architecture.md

4.6 KiB
Raw Blame History

Architecture

Module Structure

src/
├── index.ts              Main exports
├── types.ts              All interfaces and type aliases
├── getSolarEphemeris.ts  Jean Meeus solar declination, r, ecliptic lon
├── getMSC.ts             MSC piecewise seasonal model
├── getAngles.ts          Dynamic Fajr/Isha angle computation
├── getAsr.ts             Asr from noon + declination
├── getQiyam.ts           Last-third-of-night calculation
├── getTimes.ts           Raw fractional-hour prayer times
├── calcTimes.ts          Formatted HH:MM:SS prayer times
├── getTimesAll.ts        All-methods comparison (raw)
└── calcTimesAll.ts       All-methods comparison (formatted)

Data Flow

Date + lat/lng/tz/elev/temp/pressure
        │
        ├─► getSolarEphemeris()  ──► decl, r, eclLon
        │
        ├─► getMscFajr/Isha()    ──► minutes offset
        │
        └─► getAngles()          ──► fajrAngle, ishaAngle
                │
                ▼
        nrel-spa getSpa()
        (batch zenith angles)
                │
                ├─► spaData.angles[0].sunrise  = Fajr
                ├─► spaData.sunrise             = Sunrise
                ├─► spaData.solarNoon           = Noon
                ├─► spaData.sunset              = Maghrib
                └─► spaData.angles[1].sunset    = Isha
                        │
                        ├─► getAsr(noon, lat, decl)
                        └─► getQiyam(fajr, isha)

For getTimesAll, the SPA call is extended to include all method zenith angles:

allZeniths = [
  fajrZenith,   // dynamic Fajr
  ishaZenith,   // dynamic Isha
  ...methodZeniths  // 14 × 2 = 28 more
]

One SPA call, 30 zenith angles. Methods with fixed-minute Isha (UAQ, Qatar) use a placeholder zenith for the SPA call but override the result with sunset + minutes. The MSC entry uses MSC minutes offsets relative to the SPA-computed sunrise/sunset.

External Dependency

The only runtime dependency is nrel-spa. It provides:

  • getSpa(date, lat, lng, tz, opts, zeniths): batch NREL SPA computation
  • formatTime(fractionalHours): converts 5.5 to "05:30:00", returns "N/A" for NaN

The SPA (Solar Position Algorithm) from the National Renewable Energy Laboratory is the reference algorithm for solar position. It is accurate to within ±0.0003° and covers dates from -2000 to +6000.

Solar Ephemeris

getSolarEphemeris implements Jean Meeus, Astronomical Algorithms (2nd ed.), Chapter 25. This provides:

  • Solar declination (δ): accurate to ~0.01°
  • Earth-Sun distance (r) in AU: accurate to ~0.0001 AU
  • Apparent ecliptic longitude (λ) in degrees

These values are used by getAngles to apply physics corrections to the MSC base angle. They are computed independently of the full SPA call using a faster, lower-precision path that is sufficient for the correction magnitudes involved.

Why not use the SPA declination? The nrel-spa public API does not expose solar declination. The SPA computes it internally, but the value is not part of the public interface. Rather than monkey-patching nrel-spa or making a second SPA call with a known reference point, the Meeus equations provide a clean solution at adequate accuracy.

Asr Computation

getAsr does not call the SPA. Instead, it solves the shadow-ratio equation analytically:

// Shadow ratio: s = 1 (Shafi'i) or s = 2 (Hanafi)
altitude = arccot(s + tan(|latitude - declination|))
cos(hourAngle) = (sin(altitude) - sin(lat)sin(decl)) / (cos(lat)cos(decl))
asrTime = solarNoon + hourAngle (converted to hours)

This is faster than an SPA call and avoids a dependency on internal ephemeris state.

Type System

All public types are in src/types.ts and re-exported from src/index.ts. The key distinction is:

  • FractionalHours (number): raw output from getTimes / getTimesAll
  • TimeString (string): formatted output from calcTimes / calcTimesAll

PrayerTimesAll extends PrayerTimes rather than duplicating fields. The Methods map uses string keys (method IDs) rather than a union type, to allow forward compatibility without breaking changes when methods are added.

Build

tsup builds dual CJS/ESM output:

dist/
├── index.cjs       CJS bundle
├── index.mjs       ESM bundle
├── index.d.ts      CJS type declarations
└── index.d.mts     ESM type declarations

Source maps are included. sideEffects: false allows tree-shaking.


Back to Home | API Reference | Dynamic Algorithm