pray-calc-ml/.wiki/Guide-Integrating-with-pray-calc.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

113 lines
3.7 KiB
Markdown

# Guide: Integrating with pray-calc
`pray-calc-ml` fits angles from data. `pray-calc` uses those angles to generate prayer times. This guide shows how to connect them.
## Installation
```bash
npm install pray-calc pray-calc-ml
```
## Step 1: Collect observations
Build a dataset of recorded mosque times. See [Guide: Collecting Observations](Guide-Collecting-Observations) for details.
```ts
const observations = [
{ date: new Date('2024-06-01'), lat: 40.71, lng: -74.01, tz: -4, fajr: 3.75, isha: 21.58 },
{ date: new Date('2024-07-01'), lat: 40.71, lng: -74.01, tz: -4, fajr: 3.82, isha: 21.52 },
{ date: new Date('2024-08-01'), lat: 40.71, lng: -74.01, tz: -4, fajr: 4.15, isha: 21.12 },
{ date: new Date('2024-09-01'), lat: 40.71, lng: -74.01, tz: -4, fajr: 4.58, isha: 20.58 },
];
```
## Step 2: Calibrate
```ts
import { calibrateAngles } from 'pray-calc-ml';
const result = calibrateAngles(observations);
const { fajrAngle, ishaAngle } = result.angles;
console.log(`Fajr: ${fajrAngle.toFixed(2)}°, Isha: ${ishaAngle.toFixed(2)}°`);
console.log(`RMS error: ${result.rmsMinutes.toFixed(2)} min`);
```
## Step 3: Generate prayer times with pray-calc
Pass the calibrated angles to `pray-calc`'s `getTimes()` via the `angles` option:
```ts
import { getTimes } from 'pray-calc';
const today = new Date();
const lat = 40.71, lng = -74.01, tz = -4;
const times = getTimes(today, lat, lng, tz, { angles: { fajrAngle, ishaAngle } });
console.log(times.Fajr, times.Sunrise, times.Dhuhr, times.Asr, times.Maghrib, times.Isha);
```
## Step 4: Verify the fit
Before deploying, check the RMS and residuals to confirm the calibration quality:
```ts
import { scoreAngles } from 'pray-calc-ml';
// Compare ISNA standard against your observations
const isnaScore = scoreAngles(observations, 15, 15);
console.log(`ISNA RMS: ${isnaScore.rmsMinutes.toFixed(2)} min`);
console.log(`ISNA Fajr bias: ${isnaScore.fajrBiasMinutes.toFixed(1)} min`);
// Compare calibrated angles
const calibScore = scoreAngles(observations, fajrAngle, ishaAngle);
console.log(`Calibrated RMS: ${calibScore.rmsMinutes.toFixed(2)} min`);
```
If the calibrated RMS is more than 3 minutes, something is off — either the observations are inconsistent, the location coordinates are wrong, or the UTC offset changed mid-dataset.
## Caching the calibration
Run calibration once offline and store the resulting angles. There is no need to calibrate at runtime.
```ts
// Store in your config or database
const config = {
fajrAngle: 15.2,
ishaAngle: 14.8,
};
// At runtime, just use the stored angles
const times = getTimes(new Date(), lat, lng, tz, { angles: config });
```
## Handling seasonal drift
Depression angles are constants — they do not change with season. The calibration finds a single angle that minimizes error across your entire dataset. If you find that the calibrated angle gives a large error in one season (e.g. winter), you may need more observations from that season to stabilize the fit.
You can also run separate calibrations per season and check consistency:
```ts
const winterObs = observations.filter(o => {
const m = o.date.getMonth();
return m === 11 || m <= 1; // Dec, Jan, Feb
});
const summerObs = observations.filter(o => {
const m = o.date.getMonth();
return m >= 5 && m <= 7; // Jun, Jul, Aug
});
const winter = calibrateAngles(winterObs);
const summer = calibrateAngles(summerObs);
const diff = Math.abs(winter.angles.fajrAngle - summer.angles.fajrAngle);
if (diff > 0.5) {
console.warn(`Seasonal inconsistency: ${diff.toFixed(2)}° spread. Check data quality.`);
}
```
---
*[Home](Home) | [API Reference](API-Reference) | [Architecture](Architecture) | [Guide: Collecting Observations](Guide-Collecting-Observations)*