mirror of
https://github.com/acamarata/pray-calc-ml.git
synced 2026-07-01 03:14:27 +00:00
Weighted least-squares calibration of Islamic prayer time depression angles from observed mosque announcement data. Uses golden-section search to minimize the sum of squared residuals independently for Fajr and Isha. Internal Jean Meeus solar ephemeris — zero runtime dependencies. API: calibrateAngles, scoreAngles, predictFajr, predictIsha. Full TypeScript, dual CJS/ESM via tsup. 32 ESM tests, 6 CJS tests, all passing on Node 20/22/24.
150 lines
4.4 KiB
Markdown
150 lines
4.4 KiB
Markdown
# API Reference
|
|
|
|
## `calibrateAngles(observations, options?)`
|
|
|
|
Finds the Fajr and Isha depression angles that minimize the weighted sum of squared residuals across all observations.
|
|
|
|
```ts
|
|
function calibrateAngles(
|
|
observations: Observation[],
|
|
options?: CalibrationOptions,
|
|
): CalibrationResult
|
|
```
|
|
|
|
### Parameters
|
|
|
|
**`observations: Observation[]`**
|
|
|
|
An array of recorded prayer times. Each entry may supply a Fajr time, an Isha time, or both. Entries missing a field are ignored for the corresponding angle.
|
|
|
|
At least 2 observations are required per prayer. If one prayer has fewer than 2 observations, that angle falls back to the initial guess (`fajrAngle0` or `ishaAngle0`). If both prayers have fewer than 2, the function throws.
|
|
|
|
**`options?: CalibrationOptions`**
|
|
|
|
| Field | Type | Default | Description |
|
|
| --- | --- | --- | --- |
|
|
| `fajrAngle0` | `number` | `15.0` | Initial Fajr angle guess in degrees |
|
|
| `ishaAngle0` | `number` | `15.0` | Initial Isha angle guess in degrees |
|
|
| `fajrMin` | `number` | `10.0` | Minimum allowed Fajr angle |
|
|
| `fajrMax` | `number` | `22.0` | Maximum allowed Fajr angle |
|
|
| `ishaMin` | `number` | `10.0` | Minimum allowed Isha angle |
|
|
| `ishaMax` | `number` | `22.0` | Maximum allowed Isha angle |
|
|
| `maxIter` | `number` | `200` | Maximum solver iterations |
|
|
| `tol` | `number` | `1e-5` | Convergence tolerance in degrees |
|
|
|
|
### Returns `CalibrationResult`
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| `angles` | `CalibratedAngles` | Best-fit `fajrAngle` and `ishaAngle` in degrees |
|
|
| `rmsMinutes` | `number` | Weighted RMS residual in minutes |
|
|
| `observationCount` | `number` | Effective count (dual=1, single=0.5) |
|
|
| `residuals` | `Array<{fajrMin, ishaMin}>` | Per-observation residuals in minutes |
|
|
|
|
Residuals are signed: positive means the model predicted later than the observation.
|
|
|
|
### Throws
|
|
|
|
- If neither Fajr nor Isha has at least 2 observations.
|
|
|
|
---
|
|
|
|
## `scoreAngles(observations, fajrAngle, ishaAngle)`
|
|
|
|
Evaluates a fixed pair of depression angles against observation data without fitting.
|
|
|
|
```ts
|
|
function scoreAngles(
|
|
observations: Observation[],
|
|
fajrAngle: number,
|
|
ishaAngle: number,
|
|
): ScoreResult
|
|
```
|
|
|
|
### Returns `ScoreResult`
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| `rmsMinutes` | `number` | Weighted RMS error in minutes |
|
|
| `fajrBiasMinutes` | `number` | Signed mean Fajr error (positive: angle predicts Fajr too late) |
|
|
| `ishaBiasMinutes` | `number` | Signed mean Isha error |
|
|
| `residuals` | `Array<{fajrMin, ishaMin}>` | Per-observation residuals |
|
|
|
|
Use this to compare multiple standard methods against your data:
|
|
|
|
```ts
|
|
import { scoreAngles } from 'pray-calc-ml';
|
|
|
|
const obs = [/* ... */];
|
|
|
|
const isna = scoreAngles(obs, 15, 15); // ISNA
|
|
const mwl = scoreAngles(obs, 18, 17); // MWL
|
|
const uoif = scoreAngles(obs, 12, 12); // UOIF
|
|
|
|
console.log('ISNA RMS:', isna.rmsMinutes.toFixed(2));
|
|
console.log('MWL RMS:', mwl.rmsMinutes.toFixed(2));
|
|
console.log('UOIF RMS:', uoif.rmsMinutes.toFixed(2));
|
|
```
|
|
|
|
---
|
|
|
|
## `predictFajr(date, lat, lng, tz, fajrAngle)`
|
|
|
|
Predict the Fajr time for a given date, location, and depression angle.
|
|
|
|
```ts
|
|
function predictFajr(
|
|
date: Date,
|
|
lat: number,
|
|
lng: number,
|
|
tz: number,
|
|
fajrAngle: number,
|
|
): number // fractional hours, local time; NaN at polar extremes
|
|
```
|
|
|
|
---
|
|
|
|
## `predictIsha(date, lat, lng, tz, ishaAngle)`
|
|
|
|
Predict the Isha time for a given date, location, and depression angle.
|
|
|
|
```ts
|
|
function predictIsha(
|
|
date: Date,
|
|
lat: number,
|
|
lng: number,
|
|
tz: number,
|
|
ishaAngle: number,
|
|
): number // fractional hours, local time; NaN at polar extremes
|
|
```
|
|
|
|
---
|
|
|
|
## `Observation` type
|
|
|
|
```ts
|
|
interface Observation {
|
|
date: Date; // local calendar date
|
|
lat: number; // decimal degrees, south = negative
|
|
lng: number; // decimal degrees, west = negative
|
|
tz: number; // UTC offset in hours (e.g. -5 for EST, +3 for AST)
|
|
fajr?: number; // fractional hours local time (e.g. 4.5 = 4:30 AM)
|
|
isha?: number; // fractional hours local time (e.g. 21.25 = 9:15 PM)
|
|
weight?: number; // relative weight, default 1.0
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## `CalibratedAngles` type
|
|
|
|
```ts
|
|
interface CalibratedAngles {
|
|
fajrAngle: number; // degrees below horizon (positive)
|
|
ishaAngle: number; // degrees below horizon (positive)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
*[Home](Home) | [Architecture](Architecture) | [Guide: Collecting Observations](Guide-Collecting-Observations) | [Guide: Integrating with pray-calc](Guide-Integrating-with-pray-calc)*
|