nrel-spa/bin/test.js

88 lines
4 KiB
JavaScript

// 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}`
);
});