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.
4.4 KiB
API Reference
calibrateAngles(observations, options?)
Finds the Fajr and Isha depression angles that minimize the weighted sum of squared residuals across all observations.
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.
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:
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.
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.
function predictIsha(
date: Date,
lat: number,
lng: number,
tz: number,
ishaAngle: number,
): number // fractional hours, local time; NaN at polar extremes
Observation type
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
interface CalibratedAngles {
fajrAngle: number; // degrees below horizon (positive)
ishaAngle: number; // degrees below horizon (positive)
}
Home | Architecture | Guide: Collecting Observations | Guide: Integrating with pray-calc