From 7b6b4139084f32b5ced6199472f10e18f0794260 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Sun, 8 Mar 2026 17:30:53 -0400 Subject: [PATCH] style: fix prettier table formatting in wiki --- .wiki/API-Reference.md | 83 +++++++++++++------------- .wiki/Architecture.md | 2 + .wiki/Home.md | 10 ++-- .wiki/Implementation-Comparison.md | 93 +++++++++++++++--------------- .wiki/NREL-SPA-Algorithm.md | 25 ++++---- .wiki/Twilight-Calculations.md | 43 +++++++------- 6 files changed, 130 insertions(+), 126 deletions(-) diff --git a/.wiki/API-Reference.md b/.wiki/API-Reference.md index 74d0f4d..292d6a8 100644 --- a/.wiki/API-Reference.md +++ b/.wiki/API-Reference.md @@ -8,24 +8,24 @@ Computes solar position for the given date and location. Returns raw numerical v **Parameters:** -| Parameter | Type | Required | Description | -| --- | --- | --- | --- | -| `date` | `Date` | Yes | UTC date and time. Uses UTC components internally. | -| `latitude` | `number` | Yes | Observer latitude, -90 to 90. Negative = south. | -| `longitude` | `number` | Yes | Observer longitude, -180 to 180. Negative = west. | -| `timezone` | `number \| null` | No | Hours from UTC (e.g., -4 for EDT). Default: `0`. | -| `options` | `SpaOptions \| null` | No | Atmospheric and calculation parameters. | -| `angles` | `[number, ...number[]]` | No | One or more custom zenith angles in degrees. See [Twilight Calculations](Twilight-Calculations). | +| Parameter | Type | Required | Description | +| ----------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------ | +| `date` | `Date` | Yes | UTC date and time. Uses UTC components internally. | +| `latitude` | `number` | Yes | Observer latitude, -90 to 90. Negative = south. | +| `longitude` | `number` | Yes | Observer longitude, -180 to 180. Negative = west. | +| `timezone` | `number \| null` | No | Hours from UTC (e.g., -4 for EDT). Default: `0`. | +| `options` | `SpaOptions \| null` | No | Atmospheric and calculation parameters. | +| `angles` | `[number, ...number[]]` | No | One or more custom zenith angles in degrees. See [Twilight Calculations](Twilight-Calculations). | **Returns:** `SpaResult` ```typescript interface SpaResult { - zenith: number; // topocentric zenith angle (degrees) - azimuth: number; // topocentric azimuth, eastward from north (degrees) - sunrise: number; // local sunrise (fractional hours, e.g. 5.417 = 05:25) + zenith: number; // topocentric zenith angle (degrees) + azimuth: number; // topocentric azimuth, eastward from north (degrees) + sunrise: number; // local sunrise (fractional hours, e.g. 5.417 = 05:25) solarNoon: number; // local solar noon (fractional hours) - sunset: number; // local sunset (fractional hours) + sunset: number; // local sunset (fractional hours) } ``` @@ -40,11 +40,12 @@ interface SpaResultWithAngles extends SpaResult { interface SpaAnglesResult { sunrise: number; // rise time for this zenith angle (fractional hours) - sunset: number; // set time for this zenith angle (fractional hours) + sunset: number; // set time for this zenith angle (fractional hours) } ``` **Throws:** + - `TypeError` if `date` is not a valid Date, or `latitude`/`longitude` are not finite numbers - `RangeError` if `latitude` is outside [-90, 90] or `longitude` outside [-180, 180] - `RangeError` if `options.function` is not 0, 1, 2, or 3 @@ -61,11 +62,11 @@ Same parameters as `getSpa()`. Formats `sunrise`, `solarNoon`, and `sunset` as ` ```typescript interface SpaFormattedResult { - zenith: number; // same as SpaResult - azimuth: number; // same as SpaResult - sunrise: string; // "HH:MM:SS" or "N/A" during polar day/night, or when using SPA_ZA/SPA_ZA_INC + zenith: number; // same as SpaResult + azimuth: number; // same as SpaResult + sunrise: string; // "HH:MM:SS" or "N/A" during polar day/night, or when using SPA_ZA/SPA_ZA_INC solarNoon: string; // "HH:MM:SS" or "N/A" - sunset: string; // "HH:MM:SS" or "N/A" + sunset: string; // "HH:MM:SS" or "N/A" } ``` @@ -78,7 +79,7 @@ interface SpaFormattedResultWithAngles extends SpaFormattedResult { interface SpaFormattedAnglesResult { sunrise: string; // "HH:MM:SS" or "N/A" - sunset: string; // "HH:MM:SS" or "N/A" + sunset: string; // "HH:MM:SS" or "N/A" } ``` @@ -89,12 +90,12 @@ interface SpaFormattedAnglesResult { Converts a fractional hour value to `HH:MM:SS` format. ```javascript -formatTime(5.417489) // "05:25:03" -formatTime(12) // "12:00:00" -formatTime(23.9997) // "23:59:59" -formatTime(-1) // "N/A" -formatTime(NaN) // "N/A" -formatTime(Infinity) // "N/A" +formatTime(5.417489); // "05:25:03" +formatTime(12); // "12:00:00" +formatTime(23.9997); // "23:59:59" +formatTime(-1); // "N/A" +formatTime(NaN); // "N/A" +formatTime(Infinity); // "N/A" ``` --- @@ -103,17 +104,17 @@ formatTime(Infinity) // "N/A" All options are optional. Defaults match the NREL C reference implementation. -| Option | Type | Default | Description | -| --- | --- | --- | --- | -| `elevation` | `number` | `0` | Observer elevation above sea level in meters. | -| `pressure` | `number` | `1013` | Annual average atmospheric pressure in millibars. | -| `temperature` | `number` | `15` | Annual average temperature in degrees Celsius. | -| `delta_ut1` | `number` | `0` | Fractional second difference between UTC and UT1. Valid range: (-1, 1). | -| `delta_t` | `number` | `67` | Difference between Terrestrial Time and UTC in seconds. | -| `slope` | `number` | `0` | Surface slope from horizontal in degrees. Used for incidence angle (SPA_ZA_INC, SPA_ALL). | -| `azm_rotation` | `number` | `0` | Surface azimuth rotation from south in degrees. | -| `atmos_refract` | `number` | `0.5667` | Atmospheric refraction at sunrise/sunset in degrees. | -| `function` | `SpaFunctionCode` | `SPA_ZA_RTS` | Which outputs to compute. | +| Option | Type | Default | Description | +| --------------- | ----------------- | ------------ | ----------------------------------------------------------------------------------------- | +| `elevation` | `number` | `0` | Observer elevation above sea level in meters. | +| `pressure` | `number` | `1013` | Annual average atmospheric pressure in millibars. | +| `temperature` | `number` | `15` | Annual average temperature in degrees Celsius. | +| `delta_ut1` | `number` | `0` | Fractional second difference between UTC and UT1. Valid range: (-1, 1). | +| `delta_t` | `number` | `67` | Difference between Terrestrial Time and UTC in seconds. | +| `slope` | `number` | `0` | Surface slope from horizontal in degrees. Used for incidence angle (SPA_ZA_INC, SPA_ALL). | +| `azm_rotation` | `number` | `0` | Surface azimuth rotation from south in degrees. | +| `atmos_refract` | `number` | `0.5667` | Atmospheric refraction at sunrise/sunset in degrees. | +| `function` | `SpaFunctionCode` | `SPA_ZA_RTS` | Which outputs to compute. | --- @@ -123,12 +124,12 @@ All options are optional. Defaults match the NREL C reference implementation. import { SPA_ZA, SPA_ZA_INC, SPA_ZA_RTS, SPA_ALL } from 'nrel-spa'; ``` -| Constant | Value | Outputs Computed | -| --- | --- | --- | -| `SPA_ZA` | `0` | `zenith`, `azimuth` | -| `SPA_ZA_INC` | `1` | `zenith`, `azimuth`, incidence angle (for solar panels on sloped surfaces) | -| `SPA_ZA_RTS` | `2` | `zenith`, `azimuth`, `sunrise`, `solarNoon`, `sunset` | -| `SPA_ALL` | `3` | All outputs from SPA_ZA_INC and SPA_ZA_RTS combined | +| Constant | Value | Outputs Computed | +| ------------ | ----- | -------------------------------------------------------------------------- | +| `SPA_ZA` | `0` | `zenith`, `azimuth` | +| `SPA_ZA_INC` | `1` | `zenith`, `azimuth`, incidence angle (for solar panels on sloped surfaces) | +| `SPA_ZA_RTS` | `2` | `zenith`, `azimuth`, `sunrise`, `solarNoon`, `sunset` | +| `SPA_ALL` | `3` | All outputs from SPA_ZA_INC and SPA_ZA_RTS combined | The default is `SPA_ZA_RTS`. Use `SPA_ZA` for zenith/azimuth-only calculations if you do not need rise/set times; it skips the three-day calculation that rise/set requires and is slightly faster. Custom zenith `angles` require `SPA_ZA_RTS` or `SPA_ALL`. diff --git a/.wiki/Architecture.md b/.wiki/Architecture.md index 984a58c..2eec906 100644 --- a/.wiki/Architecture.md +++ b/.wiki/Architecture.md @@ -23,6 +23,7 @@ nrel-spa/ **lib/spa.js** is the core mathematical engine. It is a direct JavaScript port of the NREL C source (`spa.c`), preserving function names, variable names, and the algorithm structure. The file is checked into git and shipped in the npm package. **src/index.ts** is a thin TypeScript wrapper. It: + - Loads `lib/spa.js` at runtime - Validates input parameters, throwing `TypeError` or `RangeError` for invalid values - Validates `options.function` and throws `RangeError` for out-of-range codes @@ -32,6 +33,7 @@ nrel-spa/ - Provides `formatTime()` as a standalone export **src/types.ts** exports all public TypeScript interfaces and constants: + - `SpaOptions`: atmospheric and calculation parameters - `SpaResult` / `SpaFormattedResult`: base return types for `getSpa` / `calcSpa` - `SpaAnglesResult` / `SpaFormattedAnglesResult`: per-angle entries in the `angles` array diff --git a/.wiki/Home.md b/.wiki/Home.md index e41796c..3ec790f 100644 --- a/.wiki/Home.md +++ b/.wiki/Home.md @@ -23,14 +23,14 @@ import { calcSpa } from 'nrel-spa'; const result = calcSpa( new Date('2025-06-21T00:00:00Z'), - 40.7128, // New York latitude - -74.006, // New York longitude - -4, // EDT (UTC-4) + 40.7128, // New York latitude + -74.006, // New York longitude + -4, // EDT (UTC-4) ); -console.log(result.sunrise); // "05:25:03" +console.log(result.sunrise); // "05:25:03" console.log(result.solarNoon); // "12:57:56" -console.log(result.sunset); // "20:30:35" +console.log(result.sunset); // "20:30:35" ``` ## Key Facts diff --git a/.wiki/Implementation-Comparison.md b/.wiki/Implementation-Comparison.md index cf8d4f0..69ac10d 100644 --- a/.wiki/Implementation-Comparison.md +++ b/.wiki/Implementation-Comparison.md @@ -2,14 +2,14 @@ Three implementations of the NREL Solar Position Algorithm exist in this ecosystem. This page documents their accuracy against the original C reference and their throughput on Node.js. -| | [NREL C reference](https://midcdmz.nrel.gov/spa/) | [nrel-spa](https://www.npmjs.com/package/nrel-spa) | [solar-spa](https://www.npmjs.com/package/solar-spa) | -| --- | --- | --- | --- | -| **Language** | C (gcc -O2) | JavaScript (JS port) | WebAssembly (Emscripten) | -| **Source** | Original C | Hand-ported | Compiled from same C | -| **Async init** | No | No | Yes (`await init()`) | -| **Dependencies** | None | None | None (WASM bundled) | -| **Return type** | Synchronous | Synchronous | Promise | -| **Algorithm** | NREL SPA 2004 | NREL SPA 2004 | NREL SPA 2004 | +| | [NREL C reference](https://midcdmz.nrel.gov/spa/) | [nrel-spa](https://www.npmjs.com/package/nrel-spa) | [solar-spa](https://www.npmjs.com/package/solar-spa) | +| ---------------- | ------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | +| **Language** | C (gcc -O2) | JavaScript (JS port) | WebAssembly (Emscripten) | +| **Source** | Original C | Hand-ported | Compiled from same C | +| **Async init** | No | No | Yes (`await init()`) | +| **Dependencies** | None | None | None (WASM bundled) | +| **Return type** | Synchronous | Synchronous | Promise | +| **Algorithm** | NREL SPA 2004 | NREL SPA 2004 | NREL SPA 2004 | --- @@ -19,33 +19,33 @@ Tested across eight global locations and three dates spanning both hemispheres, **Environment:** Node.js v24.6.0. Inputs: local midnight at each location. Atmospheric defaults: pressure 1013 mb, temperature varies by season, elevation as noted. `delta_t = 67` seconds (NREL default). -| Location | Date | Field | C Reference | nrel-spa | Δ (s) | solar-spa | Δ (s) | -| --- | --- | --- | --- | --- | ---: | --- | ---: | -| New York (40.7°N) | Jun 21 | Sunrise | 05:25:03 | 05:25:03 | 0.04 | 05:25:03 | 0.04 | -| | | Solar noon | 12:57:56 | 12:57:56 | 0.40 | 12:57:56 | 0.40 | -| | | Sunset | 20:30:35 | 20:30:35 | 0.17 | 20:30:35 | 0.17 | -| New York (40.7°N) | Dec 21 | Sunrise | 07:16:41 | 07:16:41 | 0.35 | 07:16:41 | 0.35 | -| | | Solar noon | 11:54:19 | 11:54:19 | 0.40 | 11:54:19 | 0.40 | -| | | Sunset | 16:31:56 | 16:31:56 | 0.08 | 16:31:56 | 0.08 | -| London (51.5°N) | Jun 21 | Sunrise | 04:43:07 | 04:43:07 | 0.20 | 04:43:07 | 0.20 | -| | | Solar noon | 13:02:22 | 13:02:22 | 0.14 | 13:02:22 | 0.14 | -| | | Sunset | 21:21:37 | 21:21:37 | 0.34 | 21:21:37 | 0.34 | -| Tokyo (35.7°N) | Jun 21 | Sunrise | 04:25:52 | 04:25:52 | 0.09 | 04:25:52 | 0.09 | -| | | Solar noon | 11:43:00 | 11:43:00 | 0.35 | 11:43:00 | 0.35 | -| | | Sunset | 19:00:22 | 19:00:22 | 0.24 | 19:00:22 | 0.24 | -| Sydney (33.9°S) | Jun 21 | Sunrise | 07:00:12 | 07:00:12 | 0.15 | 07:00:12 | 0.15 | -| | | Solar noon | 11:56:56 | 11:56:56 | 0.29 | 11:56:56 | 0.29 | -| | | Sunset | 16:53:52 | 16:53:52 | 0.04 | 16:53:52 | 0.04 | -| Cape Town (33.9°S) | Dec 21 | Sunrise | 05:31:55 | 05:31:55 | 0.43 | 05:31:55 | 0.43 | -| | | Solar noon | 12:44:28 | 12:44:28 | 0.31 | 12:44:28 | 0.31 | -| | | Sunset | 19:57:01 | 19:57:01 | 0.01 | 19:57:01 | 0.01 | -| Quito (0.2°S) | Mar 20 | Sunrise | 06:17:54 | 06:17:54 | 0.41 | 06:17:54 | 0.41 | -| | | Solar noon | 12:21:10 | 12:21:10 | 0.10 | 12:21:10 | 0.10 | -| | | Sunset | 18:24:25 | 18:24:25 | 0.23 | 18:24:25 | 0.23 | -| Reykjavik (64.1°N) | Jun 21 | Sunrise | 02:55:10 | 02:55:10 | 0.44 | 02:55:10 | 0.44 | -| | | Solar noon | 13:29:38 | 13:29:38 | 0.49 | 13:29:38 | 0.49 | -| | | Sunset | 00:03:54 | 00:03:54 | 0.20 | 00:03:54 | 0.20 | -| **Maximum divergence** | | | | | **0.49 s** | | **0.49 s** | +| Location | Date | Field | C Reference | nrel-spa | Δ (s) | solar-spa | Δ (s) | +| ---------------------- | ------ | ---------- | ----------- | -------- | ---------: | --------- | ---------: | +| New York (40.7°N) | Jun 21 | Sunrise | 05:25:03 | 05:25:03 | 0.04 | 05:25:03 | 0.04 | +| | | Solar noon | 12:57:56 | 12:57:56 | 0.40 | 12:57:56 | 0.40 | +| | | Sunset | 20:30:35 | 20:30:35 | 0.17 | 20:30:35 | 0.17 | +| New York (40.7°N) | Dec 21 | Sunrise | 07:16:41 | 07:16:41 | 0.35 | 07:16:41 | 0.35 | +| | | Solar noon | 11:54:19 | 11:54:19 | 0.40 | 11:54:19 | 0.40 | +| | | Sunset | 16:31:56 | 16:31:56 | 0.08 | 16:31:56 | 0.08 | +| London (51.5°N) | Jun 21 | Sunrise | 04:43:07 | 04:43:07 | 0.20 | 04:43:07 | 0.20 | +| | | Solar noon | 13:02:22 | 13:02:22 | 0.14 | 13:02:22 | 0.14 | +| | | Sunset | 21:21:37 | 21:21:37 | 0.34 | 21:21:37 | 0.34 | +| Tokyo (35.7°N) | Jun 21 | Sunrise | 04:25:52 | 04:25:52 | 0.09 | 04:25:52 | 0.09 | +| | | Solar noon | 11:43:00 | 11:43:00 | 0.35 | 11:43:00 | 0.35 | +| | | Sunset | 19:00:22 | 19:00:22 | 0.24 | 19:00:22 | 0.24 | +| Sydney (33.9°S) | Jun 21 | Sunrise | 07:00:12 | 07:00:12 | 0.15 | 07:00:12 | 0.15 | +| | | Solar noon | 11:56:56 | 11:56:56 | 0.29 | 11:56:56 | 0.29 | +| | | Sunset | 16:53:52 | 16:53:52 | 0.04 | 16:53:52 | 0.04 | +| Cape Town (33.9°S) | Dec 21 | Sunrise | 05:31:55 | 05:31:55 | 0.43 | 05:31:55 | 0.43 | +| | | Solar noon | 12:44:28 | 12:44:28 | 0.31 | 12:44:28 | 0.31 | +| | | Sunset | 19:57:01 | 19:57:01 | 0.01 | 19:57:01 | 0.01 | +| Quito (0.2°S) | Mar 20 | Sunrise | 06:17:54 | 06:17:54 | 0.41 | 06:17:54 | 0.41 | +| | | Solar noon | 12:21:10 | 12:21:10 | 0.10 | 12:21:10 | 0.10 | +| | | Sunset | 18:24:25 | 18:24:25 | 0.23 | 18:24:25 | 0.23 | +| Reykjavik (64.1°N) | Jun 21 | Sunrise | 02:55:10 | 02:55:10 | 0.44 | 02:55:10 | 0.44 | +| | | Solar noon | 13:29:38 | 13:29:38 | 0.49 | 13:29:38 | 0.49 | +| | | Sunset | 00:03:54 | 00:03:54 | 0.20 | 00:03:54 | 0.20 | +| **Maximum divergence** | | | | | **0.49 s** | | **0.49 s** | Both JavaScript implementations are numerically identical to each other across all test cases. The sub-second delta from the C reference is not an algorithmic error. It comes from floating-point rounding accumulated across roughly 200 intermediate calculations in the VSOP87 series. The practical precision ceiling is the `delta_t` parameter (default 67 seconds), which is itself an approximation. @@ -57,13 +57,13 @@ Polar night is handled correctly. Tromso in December returns the NREL sentinel v **Environment:** Node.js v24.6.0, macOS. 200,000 iterations per measurement, with a 2,000-iteration warm-up before each run. Test case: New York summer solstice. -| Mode | Implementation | ns/call | calls/s | -| --- | --- | ---: | ---: | -| **SPA_ZA_RTS** (zenith + azimuth + rise/set) | nrel-spa | 84,497 | 11,835 | -| | solar-spa | 45,139 | 22,154 | -| **SPA_ZA** (zenith + azimuth only) | nrel-spa | 9,284 | 107,711 | -| | solar-spa | 6,112 | 163,616 | -| **C reference** (native binary, estimated) | gcc -O2 | ~500–2,000 | ~500K–2M | +| Mode | Implementation | ns/call | calls/s | +| -------------------------------------------- | -------------- | ---------: | -------: | +| **SPA_ZA_RTS** (zenith + azimuth + rise/set) | nrel-spa | 84,497 | 11,835 | +| | solar-spa | 45,139 | 22,154 | +| **SPA_ZA** (zenith + azimuth only) | nrel-spa | 9,284 | 107,711 | +| | solar-spa | 6,112 | 163,616 | +| **C reference** (native binary, estimated) | gcc -O2 | ~500–2,000 | ~500K–2M | **solar-spa WASM is 1.5–1.9× faster than nrel-spa JS** for sustained throughput. The WASM binary is compiled from the same C source with `-O2`, so the engine runs optimized machine code rather than interpreted JavaScript. @@ -80,10 +80,10 @@ nrel-spa and solar-spa handle dates differently. This is the single most importa **nrel-spa** reads UTC components from the Date object: ```javascript -d.year = date.getUTCFullYear(); +d.year = date.getUTCFullYear(); d.month = date.getUTCMonth() + 1; -d.day = date.getUTCDate(); -d.hour = date.getUTCHours(); +d.day = date.getUTCDate(); +d.hour = date.getUTCHours(); ``` To represent "June 21 at midnight local time," you pass a Date whose UTC components match that local time: @@ -120,17 +120,20 @@ In short: with nrel-spa, UTC ISO strings are safe. With solar-spa, construct dat ## When to Use Each **Use nrel-spa when:** + - You need synchronous, zero-init execution (serverless functions, edge workers, middleware) - You are processing individual requests rather than large batches - You want simple date handling with no machine-timezone surprises - Bundle size matters (nrel-spa is smaller; no WASM blob) **Use solar-spa when:** + - You are pre-computing thousands or millions of solar positions in a batch - You have already paid the async init cost and want maximum throughput - You need the full SPA output struct including incidence angle, equation of time, and sun transit altitude **Use the C reference when:** + - You are writing native code or a library that wraps native code - You need validated output for scientific publication - Runtime performance is the primary constraint diff --git a/.wiki/NREL-SPA-Algorithm.md b/.wiki/NREL-SPA-Algorithm.md index 08863bb..e39a673 100644 --- a/.wiki/NREL-SPA-Algorithm.md +++ b/.wiki/NREL-SPA-Algorithm.md @@ -5,6 +5,7 @@ The Solar Position Algorithm (SPA) was developed by Ibrahim Reda and Afshin Andreas at the National Renewable Energy Laboratory (NREL) and published in 2004. It is the reference algorithm for solar position calculation in scientific and engineering applications. **Citation:** + > Reda, I., Andreas, A. (2004). "Solar Position Algorithm for Solar Radiation Applications." Solar Energy, 76(5), 577-589. https://doi.org/10.1016/j.solener.2003.12.003 **Original C source:** https://midcdmz.nrel.gov/spa/ @@ -17,18 +18,18 @@ The algorithm achieves uncertainty of +/- 0.0003 degrees in solar zenith and azi Validation test cases from `bin/test.js`: -| Location | Date | Sunrise (C) | Sunrise (JS) | Noon (C) | Noon (JS) | Sunset (C) | Sunset (JS) | -| --- | --- | --- | --- | --- | --- | --- | --- | -| New York | 2025-06-21 | 05:25:03 | 05:25:03 | 12:57:56 | 12:57:56 | 20:30:35 | 20:30:35 | -| New York | 2025-12-21 | 07:16:41 | 07:16:41 | 11:54:19 | 11:54:19 | 16:31:56 | 16:31:56 | -| London | 2025-06-21 | 04:43:07 | 04:43:07 | 13:02:22 | 13:02:22 | 21:21:37 | 21:21:37 | -| London | 2025-12-21 | 08:03:52 | 08:03:52 | 11:58:42 | 11:58:42 | 15:53:32 | 15:53:32 | -| Tokyo | 2025-06-21 | 04:25:52 | 04:25:52 | 11:43:00 | 11:43:00 | 19:00:22 | 19:00:22 | -| Sydney | 2025-06-21 | 07:00:12 | 07:00:12 | 11:56:56 | 11:56:56 | 16:53:52 | 16:53:52 | -| Reykjavik | 2025-06-21 | 02:55:10 | 02:55:10 | 13:29:38 | 13:29:38 | 00:03:54 | 00:03:54 | -| Cape Town | 2025-12-21 | 05:31:55 | 05:31:55 | 12:44:28 | 12:44:28 | 19:57:01 | 19:57:01 | -| Quito | 2025-03-20 | 06:17:54 | 06:17:54 | 12:21:10 | 12:21:10 | 18:24:25 | 18:24:25 | -| Tromso | 2025-12-21 | N/A | N/A | N/A | N/A | N/A | N/A | +| Location | Date | Sunrise (C) | Sunrise (JS) | Noon (C) | Noon (JS) | Sunset (C) | Sunset (JS) | +| --------- | ---------- | ----------- | ------------ | -------- | --------- | ---------- | ----------- | +| New York | 2025-06-21 | 05:25:03 | 05:25:03 | 12:57:56 | 12:57:56 | 20:30:35 | 20:30:35 | +| New York | 2025-12-21 | 07:16:41 | 07:16:41 | 11:54:19 | 11:54:19 | 16:31:56 | 16:31:56 | +| London | 2025-06-21 | 04:43:07 | 04:43:07 | 13:02:22 | 13:02:22 | 21:21:37 | 21:21:37 | +| London | 2025-12-21 | 08:03:52 | 08:03:52 | 11:58:42 | 11:58:42 | 15:53:32 | 15:53:32 | +| Tokyo | 2025-06-21 | 04:25:52 | 04:25:52 | 11:43:00 | 11:43:00 | 19:00:22 | 19:00:22 | +| Sydney | 2025-06-21 | 07:00:12 | 07:00:12 | 11:56:56 | 11:56:56 | 16:53:52 | 16:53:52 | +| Reykjavik | 2025-06-21 | 02:55:10 | 02:55:10 | 13:29:38 | 13:29:38 | 00:03:54 | 00:03:54 | +| Cape Town | 2025-12-21 | 05:31:55 | 05:31:55 | 12:44:28 | 12:44:28 | 19:57:01 | 19:57:01 | +| Quito | 2025-03-20 | 06:17:54 | 06:17:54 | 12:21:10 | 12:21:10 | 18:24:25 | 18:24:25 | +| Tromso | 2025-12-21 | N/A | N/A | N/A | N/A | N/A | N/A | Zero drift in all cases with sun (Tromso is polar night in December). diff --git a/.wiki/Twilight-Calculations.md b/.wiki/Twilight-Calculations.md index e9b2e8f..3a98691 100644 --- a/.wiki/Twilight-Calculations.md +++ b/.wiki/Twilight-Calculations.md @@ -4,12 +4,12 @@ Standard sunrise and sunset use a zenith angle of approximately 90.833 degrees ( ## Standard Twilight Definitions -| Type | Zenith Angle | Description | -| --- | --- | --- | -| Sunrise / Sunset | ~90.833 | Center of sun at horizon (accounting for refraction) | -| Civil twilight | 96 | Sufficient light for outdoor activities without artificial light | -| Nautical twilight | 102 | Horizon visible; used for celestial navigation | -| Astronomical twilight | 108 | Sky fully dark enough for astronomical observation | +| Type | Zenith Angle | Description | +| --------------------- | ------------ | ---------------------------------------------------------------- | +| Sunrise / Sunset | ~90.833 | Center of sun at horizon (accounting for refraction) | +| Civil twilight | 96 | Sufficient light for outdoor activities without artificial light | +| Nautical twilight | 102 | Horizon visible; used for celestial navigation | +| Astronomical twilight | 108 | Sky fully dark enough for astronomical observation | ## Usage @@ -20,14 +20,14 @@ import { calcSpa } from 'nrel-spa'; const result = calcSpa( new Date('2025-06-21T00:00:00Z'), - 40.7128, // New York + 40.7128, // New York -74.006, - -4, // EDT - {}, // default options + -4, // EDT + {}, // default options [96, 102, 108], // civil, nautical, astronomical ); -console.log(result.sunrise); // "05:25:03" (standard) +console.log(result.sunrise); // "05:25:03" (standard) console.log(result.angles[0].sunrise); // civil twilight begin console.log(result.angles[1].sunrise); // nautical twilight begin console.log(result.angles[2].sunrise); // astronomical twilight begin @@ -35,16 +35,16 @@ console.log(result.angles[2].sunrise); // astronomical twilight begin Expected output for New York, June 21, 2025: -| Event | Time | -| --- | --- | +| Event | Time | +| --------------------------- | ------ | | Astronomical twilight begin | ~03:57 | -| Nautical twilight begin | ~04:28 | -| Civil twilight begin | ~04:53 | -| Sunrise | ~05:25 | -| Sunset | ~20:30 | -| Civil twilight end | ~21:02 | -| Nautical twilight end | ~21:27 | -| Astronomical twilight end | ~21:58 | +| Nautical twilight begin | ~04:28 | +| Civil twilight begin | ~04:53 | +| Sunrise | ~05:25 | +| Sunset | ~20:30 | +| Civil twilight end | ~21:02 | +| Nautical twilight end | ~21:27 | +| Astronomical twilight end | ~21:58 | ## How It Works @@ -70,10 +70,7 @@ At high latitudes during summer (midnight sun), the sun may not set even at the ```javascript import { getSpa } from 'nrel-spa'; -const r = getSpa( - new Date('2025-06-21T00:00:00Z'), - 71, 25, 2, {}, [96], -); +const r = getSpa(new Date('2025-06-21T00:00:00Z'), 71, 25, 2, {}, [96]); if (r.angles && !isFinite(r.angles[0].sunrise)) { console.log('No civil twilight at this latitude/date');