mirror of
https://github.com/acamarata/nrel-spa.git
synced 2026-06-30 19:04:25 +00:00
Major fix to core time calculations
This commit is contained in:
parent
322342d978
commit
b1c7f638ae
8 changed files with 323 additions and 98 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -1,2 +1,8 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# Ignore NREL SPA C sources and binaries
|
||||||
|
/bin/spa
|
||||||
|
/bin/spa_cli
|
||||||
|
/bin/*.c
|
||||||
|
/bin/*.h
|
||||||
|
|
@ -14,3 +14,10 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
- Moved timezone to main args and changed default behavior (major)
|
- Moved timezone to main args and changed default behavior (major)
|
||||||
- Updated test cases and readme to reflect new usage (minor)
|
- Updated test cases and readme to reflect new usage (minor)
|
||||||
|
|
||||||
|
## [1.3.0] - 2025-05-04
|
||||||
|
|
||||||
|
- Major update to fix discrepancies between original C and this implementation
|
||||||
|
- Folder "bin" added to compile and test against original C version
|
||||||
|
- This NPM now gives the exact same results as the original NREL-SPA
|
||||||
|
|
||||||
88
bin/README.md
Normal file
88
bin/README.md
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
# bin/README.md
|
||||||
|
|
||||||
|
This folder contains the C reference executable (`spa_cli`) and the JavaScript test harness (`test.js`) to compare your JS port of the NREL Solar Position Algorithm (SPA) against the original C implementation.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* **Node.js** (v14+) installed on your machine.
|
||||||
|
* **C compiler** (e.g. `gcc`) supporting C99.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
* `spa_cli.c` – C CLI wrapper to parse command-line arguments into the SPA structure and output Sunrise, Solar Noon, Sunset in `HH:MM:SS` format.
|
||||||
|
* `spa.c`, `spa.h` – The NREL SPA reference source (download separately).
|
||||||
|
* `test.js` – Node.js script that runs 10 diverse test cases through both `spa_cli` and your JS port (`getSpa`) and prints a side-by-side comparison.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. **Download the NREL SPA source**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd bin
|
||||||
|
curl -O https://midcdmz.nrel.gov/spa/spa.c
|
||||||
|
curl -O https://midcdmz.nrel.gov/spa/spa.h
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Copy or create `spa_cli.c`**
|
||||||
|
Place the `spa_cli.c` file (provided alongside this README) into this folder.
|
||||||
|
|
||||||
|
3. **Compile the C executable**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gcc -std=c99 -O2 -o spa_cli spa.c spa_cli.c -lm
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Do NOT** include `spa_tester.c` for this purpose. The custom `spa_cli.c` handles all required argument parsing and output.
|
||||||
|
|
||||||
|
4. **Install Node.js dependencies**
|
||||||
|
From the project root (one level up):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures your JS port (`index.js` and `dist/spa.js`) is available.
|
||||||
|
|
||||||
|
## Running the Tests
|
||||||
|
|
||||||
|
Inside the `bin/` folder, execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see a table with each city/date, and matching Sunrise, Solar Noon, and Sunset times from both the C reference and your JS implementation.
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Location | Date | C Rise | JS Rise | C Noon | JS Noon | C Set | JS Set
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
New York Summer | 2025-06-21 | 05:25:03 | 05:25:03 | 12:57:56 | 12:57:56 | 20:30:35 | 20:30:35
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
* If you update your JS port (`index.js`), rerun `node test.js` to verify that drift remains within a second.
|
||||||
|
* Ensure `spa_cli` is executable (`chmod +x spa_cli`) and located in the same directory as `test.js`.
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
Results from my personal tests when comparing original C version to this JS version is below:
|
||||||
|
|
||||||
|
```
|
||||||
|
% node test.js
|
||||||
|
Location | Date | C Rise | JS Rise | C Noon | JS Noon | C Set | JS Set
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
New York Summer | 2025-06-21 | 05:25:03 | 05:25:03 | 12:57:56 | 12:57:56 | 20:30:35 | 20:30:35
|
||||||
|
New York Winter | 2025-12-21 | 07:16:41 | 07:16:41 | 11:54:19 | 11:54:19 | 16:31:56 | 16:31:56
|
||||||
|
London Summer | 2025-06-21 | 04:43:07 | 04:43:07 | 13:02:22 | 13:02:22 | 21:21:37 | 21:21:37
|
||||||
|
London Winter | 2025-12-21 | 08:03:52 | 08:03:52 | 11:58:42 | 11:58:42 | 15:53:32 | 15:53:32
|
||||||
|
Tokyo Summer | 2025-06-21 | 04:25:52 | 04:25:52 | 11:43:00 | 11:43:00 | 19:00:22 | 19:00:22
|
||||||
|
Sydney Winter | 2025-06-21 | 07:00:12 | 07:00:12 | 11:56:56 | 11:56:56 | 16:53:52 | 16:53:52
|
||||||
|
Reykjavik Mids | 2025-06-21 | 02:55:10 | 02:55:10 | 13:29:38 | 13:29:38 | 00:03:54 | 00:03:54
|
||||||
|
Cape Town Summer | 2025-12-21 | 05:31:55 | 05:31:55 | 12:44:28 | 12:44:28 | 19:57:01 | 19:57:01
|
||||||
|
Quito Equinox | 2025-03-20 | 06:17:54 | 06:17:54 | 12:21:10 | 12:21:10 | 18:24:25 | 18:24:25
|
||||||
|
Tromso Polar | 2025-12-21 | N/A | N/A | N/A | N/A | N/A | N/A
|
||||||
|
```
|
||||||
88
bin/test.js
Normal file
88
bin/test.js
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
// bin/test.js
|
||||||
|
// Run 10 diverse test cases through both your JS port and the C reference (spa_cli) and compare.
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
const path = require('path');
|
||||||
|
const { getSpa } = require('../index');
|
||||||
|
|
||||||
|
// Constants for C invocation
|
||||||
|
const DELTA_UT1 = 0.0;
|
||||||
|
const DELTA_T = 67.0;
|
||||||
|
const SLOPE = 0.0;
|
||||||
|
const AZM_ROT = 0.0;
|
||||||
|
const ATMOS_REF = 0.5667;
|
||||||
|
|
||||||
|
// Helper to format fractional hours into "HH:MM:SS" or "N/A"
|
||||||
|
function formatJS(hour) {
|
||||||
|
if (typeof hour !== 'number' || isNaN(hour) || hour < 0 || hour >= 24) {
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
const total = Math.round(hour * 3600);
|
||||||
|
const H = Math.floor(total / 3600);
|
||||||
|
const M = Math.floor((total % 3600) / 60);
|
||||||
|
const S = total % 60;
|
||||||
|
return [H, M, S]
|
||||||
|
.map(v => v.toString().padStart(2, '0'))
|
||||||
|
.join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ten diverse test cases
|
||||||
|
const cases = [
|
||||||
|
{ label: 'New York Summer', dateUTC: new Date(Date.UTC(2025, 5, 21, 0,0,0)), lat: 40.7128, lon: -74.0060, tz: -4, elevation: 10, pressure: 1013, temperature: 20 },
|
||||||
|
{ label: 'New York Winter', dateUTC: new Date(Date.UTC(2025,11,21, 0,0,0)), lat: 40.7128, lon: -74.0060, tz: -5, elevation: 10, pressure: 1013, temperature: 5 },
|
||||||
|
{ label: 'London Summer', dateUTC: new Date(Date.UTC(2025, 5, 21, 0,0,0)), lat: 51.5074, lon: -0.1278, tz: 1, elevation: 11, pressure: 1013, temperature: 18 },
|
||||||
|
{ label: 'London Winter', dateUTC: new Date(Date.UTC(2025,11,21, 0,0,0)), lat: 51.5074, lon: -0.1278, tz: 0, elevation: 11, pressure: 1013, temperature: 7 },
|
||||||
|
{ label: 'Tokyo Summer', dateUTC: new Date(Date.UTC(2025, 5, 21, 0,0,0)), lat: 35.6895, lon: 139.6917, tz: 9, elevation: 40, pressure: 1013, temperature: 22 },
|
||||||
|
{ label: 'Sydney Winter', dateUTC: new Date(Date.UTC(2025, 5, 21, 0,0,0)), lat: -33.8688,lon: 151.2093, tz: 10, elevation: 58, pressure: 1013, temperature: 15 },
|
||||||
|
{ label: 'Reykjavik Mids', dateUTC: new Date(Date.UTC(2025, 5, 21, 0,0,0)), lat: 64.1466, lon: -21.9426,tz: 0, elevation: 0, pressure: 1013, temperature: 10 },
|
||||||
|
{ label: 'Cape Town Summer', dateUTC: new Date(Date.UTC(2025,11,21, 0,0,0)), lat: -33.9249,lon: 18.4241, tz: 2, elevation: 25, pressure: 1013, temperature: 18 },
|
||||||
|
{ label: 'Quito Equinox', dateUTC: new Date(Date.UTC(2025, 2, 20, 0,0,0)), lat: -0.1807, lon: -78.4678,tz: -5, elevation:2850, pressure: 789, temperature: 14 },
|
||||||
|
{ label: 'Tromso Polar', dateUTC: new Date(Date.UTC(2025,11,21, 0,0,0)), lat: 69.6492, lon: 18.9553,tz: 1, elevation: 0, pressure: 1013, temperature: -2 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Print header
|
||||||
|
console.log(
|
||||||
|
'Location | Date | C Rise | JS Rise | C Noon | JS Noon | C Set | JS Set'
|
||||||
|
);
|
||||||
|
console.log('-'.repeat(83));
|
||||||
|
|
||||||
|
cases.forEach(({ label, dateUTC, lat, lon, tz, elevation, pressure, temperature }) => {
|
||||||
|
const dateStr = dateUTC.toISOString().slice(0,10);
|
||||||
|
|
||||||
|
// JS calculation
|
||||||
|
const jsResult = getSpa(dateUTC, lat, lon, tz, { elevation, pressure, temperature });
|
||||||
|
const jsRise = formatJS(jsResult.sunrise);
|
||||||
|
const jsNoon = formatJS(jsResult.solarNoon);
|
||||||
|
const jsSet = formatJS(jsResult.sunset);
|
||||||
|
|
||||||
|
// C reference via spa_cli
|
||||||
|
const cli = path.join(__dirname, 'spa_cli');
|
||||||
|
const args = [
|
||||||
|
dateUTC.getUTCFullYear(), dateUTC.getUTCMonth()+1, dateUTC.getUTCDate(),
|
||||||
|
dateUTC.getUTCHours(), dateUTC.getUTCMinutes(), dateUTC.getUTCSeconds(),
|
||||||
|
DELTA_UT1, DELTA_T, tz,
|
||||||
|
lon, lat,
|
||||||
|
elevation, pressure, temperature,
|
||||||
|
SLOPE, AZM_ROT, ATMOS_REF
|
||||||
|
].map(String);
|
||||||
|
|
||||||
|
const stdout = spawnSync(cli, args, { encoding: 'utf8' }).stdout || '';
|
||||||
|
let cRise = 'N/A', cNoon = 'N/A', cSet = 'N/A';
|
||||||
|
stdout.split(/\r?\n/).forEach(line => {
|
||||||
|
const match = line.match(/(\d{2}:\d{2}:\d{2})/);
|
||||||
|
if (match) {
|
||||||
|
const key = line.toLowerCase();
|
||||||
|
if (key.includes('sunrise')) cRise = match[1];
|
||||||
|
else if (key.includes('solar noon')) cNoon = match[1];
|
||||||
|
else if (key.includes('sunset')) cSet = match[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Print row
|
||||||
|
console.log(
|
||||||
|
`${label.padEnd(16)} | ${dateStr} | ${cRise.padEnd(8)} | ${jsRise.padEnd(8)} | ` +
|
||||||
|
`${cNoon.padEnd(8)} | ${jsNoon.padEnd(8)} | ${cSet.padEnd(8)} | ${jsSet}`
|
||||||
|
);
|
||||||
|
});
|
||||||
171
index.js
171
index.js
|
|
@ -1,89 +1,118 @@
|
||||||
|
// index.js
|
||||||
const spa = require('./dist/spa');
|
const spa = require('./dist/spa');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert fractional hours to HH:MM:SS.mmm (rounding total seconds)
|
||||||
|
*/
|
||||||
function fractalTime(fractionalHour) {
|
function fractalTime(fractionalHour) {
|
||||||
const hours = Math.floor(fractionalHour);
|
const totalSec = Math.round(fractionalHour * 3600);
|
||||||
const minutes = Math.floor((fractionalHour - hours) * 60);
|
const H = Math.floor(totalSec / 3600);
|
||||||
const seconds = Math.floor((fractionalHour * 3600) - (hours * 3600) - (minutes * 60));
|
const rem = totalSec - H * 3600;
|
||||||
const ms = Math.floor((fractionalHour * 3600000) - (hours * 3600000) - (minutes * 60000) - (seconds * 1000));
|
const M = Math.floor(rem / 60);
|
||||||
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}`;
|
const S = rem - M * 60;
|
||||||
|
const ms = Math.round((fractionalHour * 3600 - Math.floor(fractionalHour * 3600)) * 1000);
|
||||||
|
return `${H.toString().padStart(2,'0')}:` +
|
||||||
|
`${M.toString().padStart(2,'0')}:` +
|
||||||
|
`${S.toString().padStart(2,'0')}.` +
|
||||||
|
`${ms.toString().padStart(3,'0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustForCustomAngle(baseSpaData, zenithAngle) {
|
/**
|
||||||
let adjustedData = { ...baseSpaData };
|
* Re-solve hour-angle for a custom zenith angle Zdeg (in degrees)
|
||||||
const standardZenith = 90.83;
|
*/
|
||||||
const angleDifference = zenithAngle - standardZenith;
|
function adjustForCustomAngle(base, Zdeg) {
|
||||||
const timeAdjustment = angleDifference / 360 * 24;
|
const φ = base.latitude * Math.PI/180;
|
||||||
adjustedData.sunrise -= timeAdjustment;
|
const δ = base.delta * Math.PI/180;
|
||||||
adjustedData.sunset += timeAdjustment;
|
const Z = Zdeg * Math.PI/180;
|
||||||
return adjustedData;
|
const cosH0 = (Math.cos(Z) - Math.sin(φ) * Math.sin(δ)) /
|
||||||
|
(Math.cos(φ) * Math.cos(δ));
|
||||||
|
if (cosH0 < -1 || cosH0 > 1) {
|
||||||
|
return { ...base, sunrise: NaN, sunset: NaN };
|
||||||
}
|
}
|
||||||
|
const H0h = (Math.acos(cosH0) * 180/Math.PI) / 15;
|
||||||
function getSpa(date, lat, lng, tz = 0, params = null, angles = []) {
|
|
||||||
let data = new spa.SpaData();
|
|
||||||
data.year = date.getFullYear();
|
|
||||||
data.month = date.getMonth() + 1; // JavaScript months are 0-indexed
|
|
||||||
data.day = date.getDate();
|
|
||||||
data.hour = date.getHours();
|
|
||||||
data.minute = date.getMinutes();
|
|
||||||
data.second = date.getSeconds();
|
|
||||||
data.longitude = lng;
|
|
||||||
data.latitude = lat;
|
|
||||||
data.timezone = tz;
|
|
||||||
|
|
||||||
// Set default values if optional parameters are not provided
|
|
||||||
data.elevation = params?.elevation ?? 50;
|
|
||||||
data.pressure = params?.pressure ?? 1013.25;
|
|
||||||
data.temperature = params?.temperature ?? 15;
|
|
||||||
data.function = spa.SPA_ALL;
|
|
||||||
|
|
||||||
let result = spa.spa_calculate(data);
|
|
||||||
let output = {};
|
|
||||||
|
|
||||||
if (result === 0) {
|
|
||||||
output = {
|
|
||||||
zenith: data.zenith,
|
|
||||||
azimuth: data.azimuth,
|
|
||||||
sunrise: data.sunrise,
|
|
||||||
solarNoon: data.suntransit,
|
|
||||||
sunset: data.sunset
|
|
||||||
};
|
|
||||||
|
|
||||||
if (angles.length > 0) {
|
|
||||||
output.angles = angles.map(angle => {
|
|
||||||
let customSpaData = adjustForCustomAngle({ ...data }, angle);
|
|
||||||
return {
|
return {
|
||||||
sunrise: customSpaData.sunrise,
|
...base,
|
||||||
sunset: customSpaData.sunset
|
sunrise: base.suntransit - H0h,
|
||||||
|
sunset: base.suntransit + H0h
|
||||||
};
|
};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.error('SPA Calculation failed');
|
/**
|
||||||
|
* Core SPA data calculation (raw fractional hours)
|
||||||
|
* @param {Date} date - JavaScript Date (UTC)
|
||||||
|
* @param {number} lat
|
||||||
|
* @param {number} lng
|
||||||
|
* @param {number} tz - timezone offset in hours (e.g. -4 for EDT)
|
||||||
|
* @param {object} params - { elevation, pressure, temperature, delta_ut1, delta_t, slope, azm_rotation, atmos_refract }
|
||||||
|
* @param {number[]} angles - custom zenith angles (deg) for twilight
|
||||||
|
*/
|
||||||
|
function getSpa(date, lat, lng, tz = 0, params = {}, angles = []) {
|
||||||
|
const d = new spa.SpaData();
|
||||||
|
// Use UTC components and explicit tz
|
||||||
|
d.year = date.getUTCFullYear();
|
||||||
|
d.month = date.getUTCMonth() + 1;
|
||||||
|
d.day = date.getUTCDate();
|
||||||
|
d.hour = date.getUTCHours();
|
||||||
|
d.minute = date.getUTCMinutes();
|
||||||
|
d.second = date.getUTCSeconds();
|
||||||
|
d.longitude = lng;
|
||||||
|
d.latitude = lat;
|
||||||
|
d.timezone = tz;
|
||||||
|
|
||||||
|
// Align defaults to reference C code
|
||||||
|
d.elevation = params.elevation ?? 0;
|
||||||
|
d.pressure = params.pressure ?? 1013;
|
||||||
|
d.temperature = params.temperature ?? 15;
|
||||||
|
d.delta_ut1 = params.delta_ut1 ?? 0;
|
||||||
|
d.delta_t = params.delta_t ?? 67;
|
||||||
|
d.slope = params.slope ?? 0;
|
||||||
|
d.azm_rotation = params.azm_rotation ?? 0;
|
||||||
|
d.atmos_refract= params.atmos_refract?? 0.5667;
|
||||||
|
|
||||||
|
// Only compute ZA and rise/transit/set
|
||||||
|
d.function = spa.SPA_ZA_RTS;
|
||||||
|
|
||||||
|
const rc = spa.spa_calculate(d);
|
||||||
|
if (rc !== 0) {
|
||||||
|
throw new Error(`SPA calculation failed with code ${rc}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base outputs
|
||||||
|
const output = {
|
||||||
|
zenith: d.zenith,
|
||||||
|
azimuth: d.azimuth,
|
||||||
|
sunrise: d.sunrise,
|
||||||
|
solarNoon: d.suntransit,
|
||||||
|
sunset: d.sunset
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom angles (twilight)
|
||||||
|
if (angles.length) {
|
||||||
|
output.angles = angles.map(Z => {
|
||||||
|
const c = adjustForCustomAngle(d, Z);
|
||||||
|
return { sunrise: c.sunrise, sunset: c.sunset };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcSpa(date, lat, lng, tz = 0, params = null, angles = []) {
|
/**
|
||||||
let rawData = getSpa(date, lat, lng, tz, params, angles);
|
* Same as getSpa, but formats sunrise/noon/sunset to strings
|
||||||
rawData.sunrise = fractalTime(rawData.sunrise);
|
*/
|
||||||
rawData.solarNoon = fractalTime(rawData.solarNoon);
|
function calcSpa(date, lat, lng, tz = 0, params = {}, angles = []) {
|
||||||
rawData.sunset = fractalTime(rawData.sunset);
|
const raw = getSpa(date, lat, lng, tz, params, angles);
|
||||||
|
|
||||||
if (rawData.angles) {
|
|
||||||
rawData.angles = rawData.angles.map(angleData => {
|
|
||||||
return {
|
return {
|
||||||
sunrise: fractalTime(angleData.sunrise),
|
zenith: raw.zenith,
|
||||||
sunset: fractalTime(angleData.sunset)
|
azimuth: raw.azimuth,
|
||||||
|
sunrise: fractalTime(raw.sunrise),
|
||||||
|
solarNoon: fractalTime(raw.solarNoon),
|
||||||
|
sunset: fractalTime(raw.sunset),
|
||||||
|
angles: raw.angles ? raw.angles.map(a => ({
|
||||||
|
sunrise: fractalTime(a.sunrise),
|
||||||
|
sunset: fractalTime(a.sunset)
|
||||||
|
})) : undefined
|
||||||
};
|
};
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawData;
|
module.exports = { getSpa, calcSpa, fractalTime, adjustForCustomAngle };
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getSpa,
|
|
||||||
calcSpa,
|
|
||||||
fractalTime
|
|
||||||
};
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "nrel-spa",
|
"name": "nrel-spa",
|
||||||
"version": "1.2.4",
|
"version": "1.3.0",
|
||||||
"description": "NREL SPA native implementation in JS",
|
"description": "NREL SPA native implementation in JS",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
55
test.js
55
test.js
|
|
@ -1,34 +1,41 @@
|
||||||
|
// tested.js
|
||||||
const { getSpa, calcSpa } = require('./index');
|
const { getSpa, calcSpa } = require('./index');
|
||||||
|
|
||||||
|
// Use current date/time
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
console.log(date)
|
console.log(`Current Date: ${date.toString()}\n`);
|
||||||
|
|
||||||
/* NYC - minimum params
|
/*
|
||||||
const city = "New York"
|
// Example: New York with minimum params
|
||||||
|
const city = "New York";
|
||||||
const lat = 40.7128;
|
const lat = 40.7128;
|
||||||
const lng = -74.006;
|
const lng = -74.0060;
|
||||||
const tz = -5;
|
const tz = -5; // Eastern Standard Time
|
||||||
const params = null
|
const params = null;
|
||||||
const angles = []
|
const angles = [];
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Jakarta - all params
|
// Jakarta with all params
|
||||||
const city = "Jakarta"
|
const city = "Jakarta";
|
||||||
const lat = -6.2088
|
const lat = -6.2088;
|
||||||
const lng = 106.8456
|
const lng = 106.8456;
|
||||||
const tz = 0
|
const tz = 7; // UTC+7
|
||||||
const elevation = 18
|
const params = {
|
||||||
const temperature = 26.56
|
elevation: 18, // meters
|
||||||
const pressure = 1017
|
temperature: 26.56, // °C
|
||||||
const params = {elevation, temperature, pressure}
|
pressure: 1017 // mbar
|
||||||
const angles = [63.435]
|
};
|
||||||
|
const angles = [63.435]; // example custom zenith angle
|
||||||
|
|
||||||
|
console.log(`Test: ${city} (lat: ${lat}, lng: ${lng}, UTC${tz >= 0 ? '+' : ''}${tz})\n`);
|
||||||
|
|
||||||
// Get results
|
// Raw fractional outputs
|
||||||
const get = getSpa(date, lat, lng, tz, params, angles);
|
const raw = getSpa(date, lat, lng, tz, params, angles);
|
||||||
const calc = calcSpa(date, lat, lng, tz, params, angles);
|
// Formatted HH:MM:SS outputs
|
||||||
|
const formatted = calcSpa(date, lat, lng, tz, params, angles);
|
||||||
|
|
||||||
// Print results
|
console.log('getSpa (raw fractional values):');
|
||||||
console.log(`\nTest: ${city} with current Date():\n`)
|
console.log(JSON.stringify(raw, null, 2), '\n');
|
||||||
console.log("getSpa =", get, "\n");
|
|
||||||
console.log("calcSpa =", calc, "\n");
|
console.log('calcSpa (formatted HH:MM:SS):');
|
||||||
|
console.log(JSON.stringify(formatted, null, 2), '\n');
|
||||||
Loading…
Reference in a new issue