pray-calc-ml/.wiki/API-Reference.md
Aric Camarata bbe1bf5cbc v1.0.0 — initial release
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.
2026-02-25 18:48:07 -05:00

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