mirror of
https://github.com/acamarata/nrel-spa.git
synced 2026-06-30 19:04:25 +00:00
feat(e6): portfolio polish — docs, CI, TypeScript standard, wiki
- Trim README to ≤80 lines with wiki link for full docs - Add CHANGELOG.md with initial entry - Fix CI: replace pinned pnpm/action-setup with corepack enable - Add "type": "module" and flat exports map (ADR-015) - Add ./package.json exports entry - Add coverage script - Rename lib/spa.js → lib/spa.cjs to fix CJS/ESM conflict under "type": "module" - Update src/index.ts and tsup.config.ts to reference spa.cjs - Add .github/wiki pages: _Sidebar, _Footer, Contributing, SECURITY, CODE_OF_CONDUCT
This commit is contained in:
parent
66dd84047b
commit
aa850f806c
12 changed files with 1188 additions and 162 deletions
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
Normal file
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Code of Conduct
|
||||
|
||||
## Summary
|
||||
|
||||
Be direct, be respectful, and focus on the work.
|
||||
|
||||
## Standards
|
||||
|
||||
Constructive behavior:
|
||||
|
||||
- Technical criticism aimed at code and ideas, not people
|
||||
- Clear and specific feedback with examples where possible
|
||||
- Acknowledging when you are wrong or do not know something
|
||||
- Staying on topic in issues and pull requests
|
||||
|
||||
Unacceptable behavior:
|
||||
|
||||
- Personal attacks, insults, or harassment
|
||||
- Sustained off-topic disruption
|
||||
- Publishing private information without consent
|
||||
|
||||
## Scope
|
||||
|
||||
This applies to all project spaces: GitHub issues, pull requests, discussions, and any other venue where project work happens.
|
||||
|
||||
## Enforcement
|
||||
|
||||
The project maintainer handles violations. Contact: aric.camarata@gmail.com.
|
||||
|
||||
Reports are reviewed promptly. Responses range from a private note to a permanent ban, depending on severity and history.
|
||||
|
||||
## Attribution
|
||||
|
||||
This code of conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
||||
60
.github/wiki/Contributing.md
vendored
Normal file
60
.github/wiki/Contributing.md
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Contributing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 20 or later
|
||||
- pnpm (enabled via corepack: `corepack enable`)
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
git clone https://github.com/acamarata/nrel-spa.git
|
||||
cd nrel-spa
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Build and test:
|
||||
|
||||
```sh
|
||||
pnpm build # compile TypeScript
|
||||
pnpm test # run full test suite (ESM + CJS)
|
||||
pnpm run typecheck # type-check without emitting
|
||||
pnpm run lint # ESLint
|
||||
pnpm run format # Prettier format
|
||||
```
|
||||
|
||||
The build output goes to `dist/`. It is gitignored; do not commit it.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
index.ts exports and public API wrappers
|
||||
types.ts all TypeScript types and constants
|
||||
lib/
|
||||
spa.js core SPA algorithm (JS port of NREL C source, tracked in git)
|
||||
dist/ tsup build output (gitignored)
|
||||
test.mjs full ESM test suite
|
||||
test-cjs.cjs CJS test subset
|
||||
bin/ C reference testing infrastructure
|
||||
```
|
||||
|
||||
## Making Changes
|
||||
|
||||
1. Keep `lib/spa.js` in git. It is the core algorithm and must stay tracked.
|
||||
2. All new exports go through `src/index.ts` and `src/types.ts`.
|
||||
3. Add tests in `test.mjs` for any new behavior. The test suite uses Node's built-in `node:test` runner.
|
||||
4. Run `pnpm test` before submitting. All tests must pass on Node 20, 22, and 24.
|
||||
|
||||
## Validation Against C Reference
|
||||
|
||||
The `bin/` directory contains infrastructure for comparing output against the original NREL C implementation. See `bin/README.md` for setup instructions. Use this when changing anything in `lib/spa.js`.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
- One logical change per PR
|
||||
- Include tests for new behavior
|
||||
- Update `CHANGELOG.md` under `[Unreleased]`
|
||||
- Do not bump the version number
|
||||
29
.github/wiki/SECURITY.md
vendored
Normal file
29
.github/wiki/SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| --- | --- |
|
||||
| 2.x | Yes |
|
||||
| 1.x | No |
|
||||
|
||||
Only the latest major version receives security fixes.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Do not open a public GitHub issue for security vulnerabilities.
|
||||
|
||||
Email: aric.camarata@gmail.com
|
||||
|
||||
Include:
|
||||
|
||||
- A description of the vulnerability
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Any suggested fix, if you have one
|
||||
|
||||
You will receive an acknowledgment within 48 hours and a resolution timeline within 7 days. Once a fix is ready and deployed, the vulnerability will be disclosed publicly with credit to the reporter (unless you prefer to remain anonymous).
|
||||
|
||||
## Scope
|
||||
|
||||
This package is a pure computation library. It performs no network requests, reads no files, and holds no credentials. The algorithm is a direct port of the [NREL SPA C source](https://midcdmz.nrel.gov/spa/) and contains no dynamic code loading.
|
||||
1
.github/wiki/_Footer.md
vendored
Normal file
1
.github/wiki/_Footer.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
[npm](https://www.npmjs.com/package/nrel-spa) · [GitHub](https://github.com/acamarata/nrel-spa) · [Changelog](https://github.com/acamarata/nrel-spa/blob/main/CHANGELOG.md) · MIT License
|
||||
22
.github/wiki/_Sidebar.md
vendored
Normal file
22
.github/wiki/_Sidebar.md
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
## nrel-spa
|
||||
|
||||
**[Home](Home)**
|
||||
|
||||
**Reference**
|
||||
- [API Reference](API-Reference)
|
||||
- [Architecture](Architecture)
|
||||
- [NREL SPA Algorithm](NREL-SPA-Algorithm)
|
||||
|
||||
**Deep Dives**
|
||||
- [Implementation Comparison](Implementation-Comparison)
|
||||
- [Twilight Calculations](Twilight-Calculations)
|
||||
|
||||
**Contributing**
|
||||
- [Contributing](Contributing)
|
||||
- [Code of Conduct](CODE_OF_CONDUCT)
|
||||
- [Security](SECURITY)
|
||||
|
||||
**Links**
|
||||
- [npm](https://www.npmjs.com/package/nrel-spa)
|
||||
- [GitHub](https://github.com/acamarata/nrel-spa)
|
||||
- [Changelog](https://github.com/acamarata/nrel-spa/blob/main/CHANGELOG.md)
|
||||
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
|
@ -17,15 +17,14 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build TypeScript
|
||||
|
|
@ -44,15 +43,14 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run typecheck
|
||||
|
||||
|
|
@ -61,13 +59,12 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run lint
|
||||
- run: pnpm run format:check
|
||||
|
|
@ -79,15 +76,14 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run build
|
||||
|
||||
|
|
|
|||
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.0.1] - 2026-05-28
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
151
README.md
151
README.md
|
|
@ -4,7 +4,7 @@
|
|||
[](https://github.com/acamarata/nrel-spa/actions/workflows/ci.yml)
|
||||
[](./LICENSE)
|
||||
|
||||
Pure JavaScript implementation of the NREL Solar Position Algorithm (SPA). Computes solar zenith angle, azimuth, sunrise, sunset, and solar noon for any location and date. Validated to produce identical results to the original NREL C reference implementation.
|
||||
Pure JavaScript implementation of the NREL Solar Position Algorithm (SPA). Computes solar zenith angle, azimuth, sunrise, sunset, and solar noon for any location and date. Zero dependencies, synchronous. Validated to produce identical results to the original NREL C reference implementation.
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -17,168 +17,51 @@ npm install nrel-spa
|
|||
```javascript
|
||||
import { getSpa, calcSpa } from 'nrel-spa';
|
||||
|
||||
const date = new Date('2025-06-21T00:00:00Z'); // UTC date/time
|
||||
const date = new Date('2025-06-21T00:00:00Z');
|
||||
|
||||
// Minimum required parameters
|
||||
const raw = getSpa(date, 40.7128, -74.006, -4); // New York, EDT (UTC-4)
|
||||
console.log(raw.sunrise); // 5.417 (fractional hours)
|
||||
// Raw fractional hours
|
||||
const raw = getSpa(date, 40.7128, -74.006, -4); // New York, EDT
|
||||
console.log(raw.sunrise); // 5.417
|
||||
console.log(raw.solarNoon); // 12.965
|
||||
console.log(raw.sunset); // 20.509
|
||||
|
||||
// Formatted output: same parameters, HH:MM:SS strings
|
||||
// Formatted HH:MM:SS strings
|
||||
const fmt = calcSpa(date, 40.7128, -74.006, -4);
|
||||
console.log(fmt.sunrise); // "05:25:03"
|
||||
console.log(fmt.solarNoon); // "12:57:56"
|
||||
console.log(fmt.sunset); // "20:30:35"
|
||||
|
||||
// With atmospheric parameters and custom zenith angles (twilight)
|
||||
const result = calcSpa(
|
||||
date,
|
||||
40.7128, // latitude (degrees, negative = south)
|
||||
-74.006, // longitude (degrees, negative = west)
|
||||
-4, // timezone offset in hours from UTC
|
||||
{
|
||||
elevation: 10, // meters above sea level
|
||||
pressure: 1013, // millibars
|
||||
temperature: 20 // degrees Celsius
|
||||
},
|
||||
[96, 102, 108], // civil, nautical, astronomical twilight zenith angles
|
||||
);
|
||||
console.log(result.sunrise); // "05:25:03"
|
||||
console.log(result.angles[0]); // { sunrise: "04:53:...", sunset: "20:02:..." }
|
||||
```
|
||||
|
||||
## API
|
||||
CommonJS:
|
||||
|
||||
### `getSpa(date, latitude, longitude, timezone?, options?, angles?)`
|
||||
|
||||
Returns raw numerical values. Sunrise, solarNoon, and sunset are fractional hours (e.g., `5.417` for 05:25).
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `date` | `Date` | Yes | UTC date and time for the calculation |
|
||||
| `latitude` | `number` | Yes | Observer latitude in degrees (-90 to 90) |
|
||||
| `longitude` | `number` | Yes | Observer longitude in degrees (-180 to 180) |
|
||||
| `timezone` | `number \| null` | No | Hours from UTC. Default: `0` |
|
||||
| `options` | `SpaOptions \| null` | No | Atmospheric and calculation parameters |
|
||||
| `angles` | `number[]` | No | Custom zenith angles in degrees for twilight |
|
||||
|
||||
**Returns:** `SpaResult` (or `SpaResultWithAngles` when `angles` is provided)
|
||||
|
||||
```typescript
|
||||
interface SpaResult {
|
||||
zenith: number; // topocentric zenith angle (degrees)
|
||||
azimuth: number; // topocentric azimuth, eastward from north (degrees)
|
||||
sunrise: number; // local sunrise time (fractional hours)
|
||||
solarNoon: number; // local solar noon (fractional hours)
|
||||
sunset: number; // local sunset time (fractional hours)
|
||||
}
|
||||
```js
|
||||
const { getSpa } = require('nrel-spa');
|
||||
```
|
||||
|
||||
### `calcSpa(date, latitude, longitude, timezone?, options?, angles?)`
|
||||
|
||||
Same as `getSpa()`, but formats sunrise, solarNoon, and sunset as `HH:MM:SS` strings. Returns `"N/A"` for those fields during polar day or polar night.
|
||||
|
||||
### `formatTime(hours)`
|
||||
|
||||
Converts fractional hours to `HH:MM:SS` format. Returns `"N/A"` for negative or non-finite values.
|
||||
|
||||
```javascript
|
||||
import { formatTime } from 'nrel-spa';
|
||||
formatTime(5.417489); // "05:25:03"
|
||||
formatTime(-1); // "N/A"
|
||||
```
|
||||
|
||||
### `SpaOptions`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `elevation` | `number` | `0` | Observer elevation in meters |
|
||||
| `pressure` | `number` | `1013` | Atmospheric pressure in millibars |
|
||||
| `temperature` | `number` | `15` | Temperature in degrees Celsius |
|
||||
| `delta_ut1` | `number` | `0` | UT1-UTC correction in seconds |
|
||||
| `delta_t` | `number` | `67` | TT-UTC difference in seconds |
|
||||
| `slope` | `number` | `0` | Surface slope from horizontal (degrees) |
|
||||
| `azm_rotation` | `number` | `0` | Surface azimuth rotation from south (degrees) |
|
||||
| `atmos_refract` | `number` | `0.5667` | Atmospheric refraction at sunrise/sunset (degrees) |
|
||||
| `function` | `SpaFunctionCode` | `SPA_ZA_RTS` | Which outputs to compute |
|
||||
|
||||
### Function Codes
|
||||
|
||||
```javascript
|
||||
import { SPA_ZA, SPA_ZA_INC, SPA_ZA_RTS, SPA_ALL } from 'nrel-spa';
|
||||
```
|
||||
|
||||
| Code | Value | Computes |
|
||||
| --- | --- | --- |
|
||||
| `SPA_ZA` | `0` | Zenith and azimuth only |
|
||||
| `SPA_ZA_INC` | `1` | Zenith, azimuth, and incidence angle |
|
||||
| `SPA_ZA_RTS` | `2` | Zenith, azimuth, sunrise, noon, sunset (default) |
|
||||
| `SPA_ALL` | `3` | All outputs |
|
||||
|
||||
## Architecture
|
||||
|
||||
The core algorithm in `lib/spa.js` is a direct port of the NREL SPA C source to JavaScript, preserving the same mathematical structure: 63 periodic nutation terms, full heliocentric coordinate calculation, topocentric correction, and atmospheric refraction. It has been validated to produce output identical to the C reference within rounding.
|
||||
|
||||
The TypeScript wrapper in `src/` provides the public API, input validation, and the `formatTime` utility. The package ships dual CJS and ESM builds via tsup, with full TypeScript definitions.
|
||||
|
||||
See the [wiki](https://github.com/acamarata/nrel-spa/wiki) for a detailed breakdown of the algorithm and architecture.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **Node.js:** 20, 22, 24 (CI tested)
|
||||
- **ESM:** `import { getSpa } from 'nrel-spa'`
|
||||
- **CommonJS:** `const { getSpa } = require('nrel-spa')`
|
||||
- **TypeScript:** Full type definitions included
|
||||
|
||||
Bundlers (Vite, Webpack, esbuild, Rollup) work via the `exports` map in package.json.
|
||||
Pass a `zenith angles` array as the sixth argument to `getSpa`/`calcSpa` for civil (96°), nautical (102°), or astronomical (108°) twilight times.
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
import {
|
||||
getSpa,
|
||||
calcSpa,
|
||||
formatTime,
|
||||
SPA_ZA,
|
||||
SPA_ZA_INC,
|
||||
SPA_ZA_RTS,
|
||||
SPA_ALL,
|
||||
type SpaOptions,
|
||||
type SpaResult,
|
||||
type SpaFormattedResult,
|
||||
type SpaAnglesResult,
|
||||
type SpaFormattedAnglesResult,
|
||||
type SpaResultWithAngles,
|
||||
type SpaFormattedResultWithAngles,
|
||||
type SpaFunctionCode,
|
||||
} from 'nrel-spa';
|
||||
import { getSpa, calcSpa, formatTime, SPA_ZA_RTS } from 'nrel-spa';
|
||||
import type { SpaOptions, SpaResult, SpaFunctionCode } from 'nrel-spa';
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation is available on the [GitHub Wiki](https://github.com/acamarata/nrel-spa/wiki):
|
||||
|
||||
- [API Reference](https://github.com/acamarata/nrel-spa/wiki/API-Reference)
|
||||
- [Architecture](https://github.com/acamarata/nrel-spa/wiki/Architecture)
|
||||
- [Twilight Calculations](https://github.com/acamarata/nrel-spa/wiki/Twilight-Calculations)
|
||||
- [NREL SPA Algorithm](https://github.com/acamarata/nrel-spa/wiki/NREL-SPA-Algorithm)
|
||||
Full API reference, algorithm notes, and twilight calculation guide: [GitHub Wiki](https://github.com/acamarata/nrel-spa/wiki)
|
||||
|
||||
## Related
|
||||
|
||||
Other packages in this collection:
|
||||
|
||||
- [solar-spa](https://www.npmjs.com/package/solar-spa): WASM build of the same algorithm, async, for high-throughput batch calculations
|
||||
- [solar-spa](https://www.npmjs.com/package/solar-spa): WASM build of the same algorithm, async, for high-throughput batch work
|
||||
- [pray-calc](https://www.npmjs.com/package/pray-calc): Islamic prayer times built on nrel-spa
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
The core algorithm is a JavaScript port of the Solar Position Algorithm (SPA) developed by Ibrahim Reda and Afshin Andreas at the National Renewable Energy Laboratory (NREL):
|
||||
The core algorithm is a JavaScript port of the NREL SPA by Ibrahim Reda and Afshin Andreas:
|
||||
|
||||
> 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](https://doi.org/10.1016/j.solener.2003.12.003)
|
||||
|
||||
Original source: [https://midcdmz.nrel.gov/spa/](https://midcdmz.nrel.gov/spa/)
|
||||
> Reda, I., Andreas, A. (2004). "Solar Position Algorithm for Solar Radiation Applications." Solar Energy, 76(5), 577-589.
|
||||
|
||||
## License
|
||||
|
||||
MIT (TypeScript wrapper and build tooling). The core algorithm in `lib/spa.js` is a port of NREL's SPA C source, which is subject to its own terms. See the [LICENSE](./LICENSE) file for the full notice.
|
||||
MIT (TypeScript wrapper and build tooling). The core algorithm in `lib/spa.js` is a port of NREL's SPA C source, subject to its own terms. See [LICENSE](./LICENSE).
|
||||
|
|
|
|||
989
lib/spa.cjs
Normal file
989
lib/spa.cjs
Normal file
|
|
@ -0,0 +1,989 @@
|
|||
// lib/spa.js
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.spa_calculate = exports.topocentric_azimuth_angle = exports.topocentric_azimuth_angle_astro = exports.topocentric_zenith_angle = exports.topocentric_elevation_angle_corrected = exports.atmospheric_refraction_correction = exports.topocentric_elevation_angle = exports.topocentric_local_hour_angle = exports.topocentric_right_ascension = exports.right_ascension_parallax_and_topocentric_dec = exports.observer_hour_angle = exports.geocentric_declination = exports.geocentric_right_ascension = exports.third_order_polynomial = exports.limit_degrees = exports.rad2deg = exports.deg2rad = exports.SpaData = exports.SPA_ALL = exports.SPA_ZA_RTS = exports.SPA_ZA_INC = exports.SPA_ZA = exports.OutCode = void 0;
|
||||
var OutCode;
|
||||
(function (OutCode) {
|
||||
OutCode[OutCode["SPA_ZA"] = 0] = "SPA_ZA";
|
||||
OutCode[OutCode["SPA_ZA_INC"] = 1] = "SPA_ZA_INC";
|
||||
OutCode[OutCode["SPA_ZA_RTS"] = 2] = "SPA_ZA_RTS";
|
||||
OutCode[OutCode["SPA_ALL"] = 3] = "SPA_ALL"; //calculate all SPA output values
|
||||
})(OutCode = exports.OutCode || (exports.OutCode = {}));
|
||||
//For external reference as per original C definition
|
||||
exports.SPA_ZA = OutCode.SPA_ZA;
|
||||
exports.SPA_ZA_INC = OutCode.SPA_ZA_INC;
|
||||
exports.SPA_ZA_RTS = OutCode.SPA_ZA_RTS;
|
||||
exports.SPA_ALL = OutCode.SPA_ALL;
|
||||
var PI = Math.PI;
|
||||
var SUN_RADIUS = 0.26667;
|
||||
var L_COUNT = 6;
|
||||
var B_COUNT = 2;
|
||||
var R_COUNT = 5;
|
||||
var Y_COUNT = 63;
|
||||
var TermsA;
|
||||
(function (TermsA) {
|
||||
TermsA[TermsA["TERM_A"] = 0] = "TERM_A";
|
||||
TermsA[TermsA["TERM_B"] = 1] = "TERM_B";
|
||||
TermsA[TermsA["TERM_C"] = 2] = "TERM_C";
|
||||
TermsA[TermsA["TERM_COUNT"] = 3] = "TERM_COUNT";
|
||||
})(TermsA || (TermsA = {}));
|
||||
var TermsX;
|
||||
(function (TermsX) {
|
||||
TermsX[TermsX["TERM_X0"] = 0] = "TERM_X0";
|
||||
TermsX[TermsX["TERM_X1"] = 1] = "TERM_X1";
|
||||
TermsX[TermsX["TERM_X2"] = 2] = "TERM_X2";
|
||||
TermsX[TermsX["TERM_X3"] = 3] = "TERM_X3";
|
||||
TermsX[TermsX["TERM_X4"] = 4] = "TERM_X4";
|
||||
TermsX[TermsX["TERM_X_COUNT"] = 5] = "TERM_X_COUNT";
|
||||
})(TermsX || (TermsX = {}));
|
||||
var TermsPE;
|
||||
(function (TermsPE) {
|
||||
TermsPE[TermsPE["TERM_PSI_A"] = 0] = "TERM_PSI_A";
|
||||
TermsPE[TermsPE["TERM_PSI_B"] = 1] = "TERM_PSI_B";
|
||||
TermsPE[TermsPE["TERM_EPS_C"] = 2] = "TERM_EPS_C";
|
||||
TermsPE[TermsPE["TERM_EPS_D"] = 3] = "TERM_EPS_D";
|
||||
TermsPE[TermsPE["TERM_PE_COUNT"] = 4] = "TERM_PE_COUNT";
|
||||
})(TermsPE || (TermsPE = {}));
|
||||
var JDSign;
|
||||
(function (JDSign) {
|
||||
JDSign[JDSign["JD_MINUS"] = 0] = "JD_MINUS";
|
||||
JDSign[JDSign["JD_ZERO"] = 1] = "JD_ZERO";
|
||||
JDSign[JDSign["JD_PLUS"] = 2] = "JD_PLUS";
|
||||
JDSign[JDSign["JD_COUNT"] = 3] = "JD_COUNT";
|
||||
})(JDSign || (JDSign = {}));
|
||||
var SunState;
|
||||
(function (SunState) {
|
||||
SunState[SunState["SUN_TRANSIT"] = 0] = "SUN_TRANSIT";
|
||||
SunState[SunState["SUN_RISE"] = 1] = "SUN_RISE";
|
||||
SunState[SunState["SUN_SET"] = 2] = "SUN_SET";
|
||||
SunState[SunState["SUN_COUNT"] = 3] = "SUN_COUNT";
|
||||
})(SunState || (SunState = {}));
|
||||
var TERM_Y_COUNT = TermsX.TERM_X_COUNT;
|
||||
var l_subcount = [64, 34, 20, 7, 3, 1];
|
||||
var b_subcount = [5, 2];
|
||||
var r_subcount = [40, 10, 6, 2, 1];
|
||||
var SpaData = /** @class */ (function () {
|
||||
function SpaData() {
|
||||
//--------------------------Input Valuse
|
||||
this.year = 0; // 4-digit year, valid range: -2000 to 6000, error code: 1
|
||||
this.month = 0; // 2-digit month, valid range: 1 to 12, error code: 2
|
||||
this.day = 0; // 2-digit day, valid range: 1 to 31, error code: 3
|
||||
this.hour = 0; // Observer local hour, valid range: 0 to 24, error code: 4
|
||||
this.minute = 0; // Observer local minute, valid range: 0 to 59, error code: 5
|
||||
this.second = 0.0; // Observer local second, valid range: 0 to <60, error code: 6
|
||||
this.delta_ut1 = 0.0; // Fractional second difference between UTC and UT which is used
|
||||
// to adjust UTC for earth's irregular rotation rate and is derived
|
||||
// from observation only and is reported in this bulletin:
|
||||
// http://maia.usno.navy.mil/ser7/ser7.dat,
|
||||
// where delta_ut1 = DUT1
|
||||
// valid range: -1 to 1 second (exclusive), error code 17
|
||||
this.delta_t = 0.0; // Difference between earth rotation time and terrestrial time
|
||||
// It is derived from observation only and is reported in this
|
||||
// bulletin: http://maia.usno.navy.mil/ser7/ser7.dat,
|
||||
// where delta_t = 32.184 + (TAI-UTC) - DUT1
|
||||
// valid range: -8000 to 8000 seconds, error code: 7
|
||||
this.timezone = 0.0; // Observer time zone (negative west of Greenwich)
|
||||
// valid range: -18 to 18 hours, error code: 8
|
||||
this.longitude = 0.0; // Observer longitude (negative west of Greenwich)
|
||||
// valid range: -180 to 180 degrees, error code: 9
|
||||
this.latitude = 0.0; // Observer latitude (negative south of equator)
|
||||
// valid range: -90 to 90 degrees, error code: 10
|
||||
this.elevation = 0.0; // Observer elevation [meters]
|
||||
// valid range: -6500000 or higher meters, error code: 11
|
||||
this.pressure = 0.0; // Annual average local pressure [millibars]
|
||||
// valid range: 0 to 5000 millibars, error code: 12
|
||||
this.temperature = 0.0; // Annual average local temperature [degrees Celsius]
|
||||
// valid range: -273 to 6000 degrees Celsius, error code 13
|
||||
this.slope = 0.0; // Surface slope (measured from the horizontal plane)
|
||||
// valid range: -360 to 360 degrees, error code: 14
|
||||
this.azm_rotation = 0.0; // Surface azimuth rotation (measured from south to projection of
|
||||
// surface normal on horizontal plane, negative east)
|
||||
// valid range: -360 to 360 degrees, error code: 15
|
||||
this.atmos_refract = 0.0; // Atmospheric refraction at sunrise and sunset (0.5667 deg is typical)
|
||||
// valid range: -5 to 5 degrees, error code: 16
|
||||
this.function = 0; // Switch to choose functions for desired output (from enumeration)
|
||||
//-----------------ermediate OUTPUT VALUES--------------------
|
||||
this.jd = 0.0; //Julian day
|
||||
this.jc = 0.0; //Julian century
|
||||
this.jde = 0.0; //Julian ephemeris day
|
||||
this.jce = 0.0; //Julian ephemeris century
|
||||
this.jme = 0.0; //Julian ephemeris millennium
|
||||
this.l = 0.0; //earth heliocentric longitude [degrees]
|
||||
this.b = 0.0; //earth heliocentric latitude [degrees]
|
||||
this.r = 0.0; //earth radius vector [Astronomical Units, AU]
|
||||
this.theta = 0.0; //geocentric longitude [degrees]
|
||||
this.beta = 0.0; //geocentric latitude [degrees]
|
||||
this.x0 = 0.0; //mean elongation (moon-sun) [degrees]
|
||||
this.x1 = 0.0; //mean anomaly (sun) [degrees]
|
||||
this.x2 = 0.0; //mean anomaly (moon) [degrees]
|
||||
this.x3 = 0.0; //argument latitude (moon) [degrees]
|
||||
this.x4 = 0.0; //ascending longitude (moon) [degrees]
|
||||
this.del_psi = 0.0; //nutation longitude [degrees]
|
||||
this.del_epsilon = 0.0; //nutation obliquity [degrees]
|
||||
this.epsilon0 = 0.0; //ecliptic mean obliquity [arc seconds]
|
||||
this.epsilon = 0.0; //ecliptic true obliquity [degrees]
|
||||
this.del_tau = 0.0; //aberration correction [degrees]
|
||||
this.lamda = 0.0; //apparent sun longitude [degrees]
|
||||
this.nu0 = 0.0; //Greenwich mean sidereal time [degrees]
|
||||
this.nu = 0.0; //Greenwich sidereal time [degrees]
|
||||
this.alpha = 0.0; //geocentric sun right ascension [degrees]
|
||||
this.delta = 0.0; //geocentric sun declination [degrees]
|
||||
this.h = 0.0; //observer hour angle [degrees]
|
||||
this.xi = 0.0; //sun equatorial horizontal parallax [degrees]
|
||||
this.del_alpha = 0.0; //sun right ascension parallax [degrees]
|
||||
this.delta_prime = 0.0; //topocentric sun declination [degrees]
|
||||
this.alpha_prime = 0.0; //topocentric sun right ascension [degrees]
|
||||
this.h_prime = 0.0; //topocentric local hour angle [degrees]
|
||||
this.e0 = 0.0; //topocentric elevation angle (uncorrected) [degrees]
|
||||
this.del_e = 0.0; //atmospheric refraction correction [degrees]
|
||||
this.e = 0.0; //topocentric elevation angle (corrected) [degrees]
|
||||
this.eot = 0.0; //equation of time [minutes]
|
||||
this.srha = 0.0; //sunrise hour angle [degrees]
|
||||
this.ssha = 0.0; //sunset hour angle [degrees]
|
||||
this.sta = 0.0; //sun transit altitude [degrees]
|
||||
//---------------------Final OUTPUT VALUES------------------------
|
||||
this.zenith = 0.0; //topocentric zenith angle [degrees]
|
||||
this.azimuth_astro = 0.0; //topocentric azimuth angle (westward from south) [for astronomers]
|
||||
this.azimuth = 0.0; //topocentric azimuth angle (eastward from north) [for navigators and solar radiation]
|
||||
this.incidence = 0.0; //surface incidence angle [degrees]
|
||||
this.suntransit = 0.0; //local sun transit time (or solar noon) [fractional hour]
|
||||
this.sunrise = 0.0; //local sunrise time (+/- 30 seconds) [fractional hour]
|
||||
this.sunset = 0.0; //local sunset time (+/- 30 seconds) [fractional hour]
|
||||
}
|
||||
return SpaData;
|
||||
}());
|
||||
exports.SpaData = SpaData;
|
||||
var copySPA = function (src) {
|
||||
return Array.isArray(src)
|
||||
? src.map(function (i) { return copySPA(i); })
|
||||
: typeof src === 'object'
|
||||
? Object.getOwnPropertyNames(src).reduce(function (o, prop) {
|
||||
Object.defineProperty(o, prop, Object.getOwnPropertyDescriptor(src, prop));
|
||||
o[prop] = copySPA(src[prop]);
|
||||
return o;
|
||||
}, Object.create(Object.getPrototypeOf(src)))
|
||||
: src;
|
||||
};
|
||||
//===================Earth Periodic Terms===================
|
||||
var L_TERMS = [
|
||||
[
|
||||
[175347046.0, 0, 0],
|
||||
[3341656.0, 4.6692568, 6283.07585],
|
||||
[34894.0, 4.6261, 12566.1517],
|
||||
[3497.0, 2.7441, 5753.3849],
|
||||
[3418.0, 2.8289, 3.5231],
|
||||
[3136.0, 3.6277, 77713.7715],
|
||||
[2676.0, 4.4181, 7860.4194],
|
||||
[2343.0, 6.1352, 3930.2097],
|
||||
[1324.0, 0.7425, 11506.7698],
|
||||
[1273.0, 2.0371, 529.691],
|
||||
[1199.0, 1.1096, 1577.3435],
|
||||
[990, 5.233, 5884.927],
|
||||
[902, 2.045, 26.298],
|
||||
[857, 3.508, 398.149],
|
||||
[780, 1.179, 5223.694],
|
||||
[753, 2.533, 5507.553],
|
||||
[505, 4.583, 18849.228],
|
||||
[492, 4.205, 775.523],
|
||||
[357, 2.92, 0.067],
|
||||
[317, 5.849, 11790.629],
|
||||
[284, 1.899, 796.298],
|
||||
[271, 0.315, 10977.079],
|
||||
[243, 0.345, 5486.778],
|
||||
[206, 4.806, 2544.314],
|
||||
[205, 1.869, 5573.143],
|
||||
[202, 2.458, 6069.777],
|
||||
[156, 0.833, 213.299],
|
||||
[132, 3.411, 2942.463],
|
||||
[126, 1.083, 20.775],
|
||||
[115, 0.645, 0.98],
|
||||
[103, 0.636, 4694.003],
|
||||
[102, 0.976, 15720.839],
|
||||
[102, 4.267, 7.114],
|
||||
[99, 6.21, 2146.17],
|
||||
[98, 0.68, 155.42],
|
||||
[86, 5.98, 161000.69],
|
||||
[85, 1.3, 6275.96],
|
||||
[85, 3.67, 71430.7],
|
||||
[80, 1.81, 17260.15],
|
||||
[79, 3.04, 12036.46],
|
||||
[75, 1.76, 5088.63],
|
||||
[74, 3.5, 3154.69],
|
||||
[74, 4.68, 801.82],
|
||||
[70, 0.83, 9437.76],
|
||||
[62, 3.98, 8827.39],
|
||||
[61, 1.82, 7084.9],
|
||||
[57, 2.78, 6286.6],
|
||||
[56, 4.39, 14143.5],
|
||||
[56, 3.47, 6279.55],
|
||||
[52, 0.19, 12139.55],
|
||||
[52, 1.33, 1748.02],
|
||||
[51, 0.28, 5856.48],
|
||||
[49, 0.49, 1194.45],
|
||||
[41, 5.37, 8429.24],
|
||||
[41, 2.4, 19651.05],
|
||||
[39, 6.17, 10447.39],
|
||||
[37, 6.04, 10213.29],
|
||||
[37, 2.57, 1059.38],
|
||||
[36, 1.71, 2352.87],
|
||||
[36, 1.78, 6812.77],
|
||||
[33, 0.59, 17789.85],
|
||||
[30, 0.44, 83996.85],
|
||||
[30, 2.74, 1349.87],
|
||||
[25, 3.16, 4690.48]
|
||||
],
|
||||
[
|
||||
[628331966747.0, 0, 0],
|
||||
[206059.0, 2.678235, 6283.07585],
|
||||
[4303.0, 2.6351, 12566.1517],
|
||||
[425.0, 1.59, 3.523],
|
||||
[119.0, 5.796, 26.298],
|
||||
[109.0, 2.966, 1577.344],
|
||||
[93, 2.59, 18849.23],
|
||||
[72, 1.14, 529.69],
|
||||
[68, 1.87, 398.15],
|
||||
[67, 4.41, 5507.55],
|
||||
[59, 2.89, 5223.69],
|
||||
[56, 2.17, 155.42],
|
||||
[45, 0.4, 796.3],
|
||||
[36, 0.47, 775.52],
|
||||
[29, 2.65, 7.11],
|
||||
[21, 5.34, 0.98],
|
||||
[19, 1.85, 5486.78],
|
||||
[19, 4.97, 213.3],
|
||||
[17, 2.99, 6275.96],
|
||||
[16, 0.03, 2544.31],
|
||||
[16, 1.43, 2146.17],
|
||||
[15, 1.21, 10977.08],
|
||||
[12, 2.83, 1748.02],
|
||||
[12, 3.26, 5088.63],
|
||||
[12, 5.27, 1194.45],
|
||||
[12, 2.08, 4694],
|
||||
[11, 0.77, 553.57],
|
||||
[10, 1.3, 6286.6],
|
||||
[10, 4.24, 1349.87],
|
||||
[9, 2.7, 242.73],
|
||||
[9, 5.64, 951.72],
|
||||
[8, 5.3, 2352.87],
|
||||
[6, 2.65, 9437.76],
|
||||
[6, 4.67, 4690.48]
|
||||
],
|
||||
[
|
||||
[52919.0, 0, 0],
|
||||
[8720.0, 1.0721, 6283.0758],
|
||||
[309.0, 0.867, 12566.152],
|
||||
[27, 0.05, 3.52],
|
||||
[16, 5.19, 26.3],
|
||||
[16, 3.68, 155.42],
|
||||
[10, 0.76, 18849.23],
|
||||
[9, 2.06, 77713.77],
|
||||
[7, 0.83, 775.52],
|
||||
[5, 4.66, 1577.34],
|
||||
[4, 1.03, 7.11],
|
||||
[4, 3.44, 5573.14],
|
||||
[3, 5.14, 796.3],
|
||||
[3, 6.05, 5507.55],
|
||||
[3, 1.19, 242.73],
|
||||
[3, 6.12, 529.69],
|
||||
[3, 0.31, 398.15],
|
||||
[3, 2.28, 553.57],
|
||||
[2, 4.38, 5223.69],
|
||||
[2, 3.75, 0.98]
|
||||
],
|
||||
[
|
||||
[289.0, 5.844, 6283.076],
|
||||
[35, 0, 0],
|
||||
[17, 5.49, 12566.15],
|
||||
[3, 5.2, 155.42],
|
||||
[1, 4.72, 3.52],
|
||||
[1, 5.3, 18849.23],
|
||||
[1, 5.97, 242.73]
|
||||
],
|
||||
[
|
||||
[114.0, 3.142, 0],
|
||||
[8, 4.13, 6283.08],
|
||||
[1, 3.84, 12566.15]
|
||||
],
|
||||
[
|
||||
[1, 3.14, 0]
|
||||
]
|
||||
];
|
||||
var B_TERMS = [
|
||||
[
|
||||
[280.0, 3.199, 84334.662],
|
||||
[102.0, 5.422, 5507.553],
|
||||
[80, 3.88, 5223.69],
|
||||
[44, 3.7, 2352.87],
|
||||
[32, 4, 1577.34]
|
||||
],
|
||||
[
|
||||
[9, 3.9, 5507.55],
|
||||
[6, 1.73, 5223.69]
|
||||
]
|
||||
];
|
||||
var R_TERMS = [
|
||||
[
|
||||
[100013989.0, 0, 0],
|
||||
[1670700.0, 3.0984635, 6283.07585],
|
||||
[13956.0, 3.05525, 12566.1517],
|
||||
[3084.0, 5.1985, 77713.7715],
|
||||
[1628.0, 1.1739, 5753.3849],
|
||||
[1576.0, 2.8469, 7860.4194],
|
||||
[925.0, 5.453, 11506.77],
|
||||
[542.0, 4.564, 3930.21],
|
||||
[472.0, 3.661, 5884.927],
|
||||
[346.0, 0.964, 5507.553],
|
||||
[329.0, 5.9, 5223.694],
|
||||
[307.0, 0.299, 5573.143],
|
||||
[243.0, 4.273, 11790.629],
|
||||
[212.0, 5.847, 1577.344],
|
||||
[186.0, 5.022, 10977.079],
|
||||
[175.0, 3.012, 18849.228],
|
||||
[110.0, 5.055, 5486.778],
|
||||
[98, 0.89, 6069.78],
|
||||
[86, 5.69, 15720.84],
|
||||
[86, 1.27, 161000.69],
|
||||
[65, 0.27, 17260.15],
|
||||
[63, 0.92, 529.69],
|
||||
[57, 2.01, 83996.85],
|
||||
[56, 5.24, 71430.7],
|
||||
[49, 3.25, 2544.31],
|
||||
[47, 2.58, 775.52],
|
||||
[45, 5.54, 9437.76],
|
||||
[43, 6.01, 6275.96],
|
||||
[39, 5.36, 4694],
|
||||
[38, 2.39, 8827.39],
|
||||
[37, 0.83, 19651.05],
|
||||
[37, 4.9, 12139.55],
|
||||
[36, 1.67, 12036.46],
|
||||
[35, 1.84, 2942.46],
|
||||
[33, 0.24, 7084.9],
|
||||
[32, 0.18, 5088.63],
|
||||
[32, 1.78, 398.15],
|
||||
[28, 1.21, 6286.6],
|
||||
[28, 1.9, 6279.55],
|
||||
[26, 4.59, 10447.39]
|
||||
],
|
||||
[
|
||||
[103019.0, 1.10749, 6283.07585],
|
||||
[1721.0, 1.0644, 12566.1517],
|
||||
[702.0, 3.142, 0],
|
||||
[32, 1.02, 18849.23],
|
||||
[31, 2.84, 5507.55],
|
||||
[25, 1.32, 5223.69],
|
||||
[18, 1.42, 1577.34],
|
||||
[10, 5.91, 10977.08],
|
||||
[9, 1.42, 6275.96],
|
||||
[9, 0.27, 5486.78]
|
||||
],
|
||||
[
|
||||
[4359.0, 5.7846, 6283.0758],
|
||||
[124.0, 5.579, 12566.152],
|
||||
[12, 3.14, 0],
|
||||
[9, 3.63, 77713.77],
|
||||
[6, 1.87, 5573.14],
|
||||
[3, 5.47, 18849.23]
|
||||
],
|
||||
[
|
||||
[145.0, 4.273, 6283.076],
|
||||
[7, 3.92, 12566.15]
|
||||
],
|
||||
[
|
||||
[4, 2.56, 6283.08]
|
||||
]
|
||||
];
|
||||
//===================Periodic Terms for the nutation in longitude and obliquity===================
|
||||
var Y_TERMS = [
|
||||
[0, 0, 0, 0, 1],
|
||||
[-2, 0, 0, 2, 2],
|
||||
[0, 0, 0, 2, 2],
|
||||
[0, 0, 0, 0, 2],
|
||||
[0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0],
|
||||
[-2, 1, 0, 2, 2],
|
||||
[0, 0, 0, 2, 1],
|
||||
[0, 0, 1, 2, 2],
|
||||
[-2, -1, 0, 2, 2],
|
||||
[-2, 0, 1, 0, 0],
|
||||
[-2, 0, 0, 2, 1],
|
||||
[0, 0, -1, 2, 2],
|
||||
[2, 0, 0, 0, 0],
|
||||
[0, 0, 1, 0, 1],
|
||||
[2, 0, -1, 2, 2],
|
||||
[0, 0, -1, 0, 1],
|
||||
[0, 0, 1, 2, 1],
|
||||
[-2, 0, 2, 0, 0],
|
||||
[0, 0, -2, 2, 1],
|
||||
[2, 0, 0, 2, 2],
|
||||
[0, 0, 2, 2, 2],
|
||||
[0, 0, 2, 0, 0],
|
||||
[-2, 0, 1, 2, 2],
|
||||
[0, 0, 0, 2, 0],
|
||||
[-2, 0, 0, 2, 0],
|
||||
[0, 0, -1, 2, 1],
|
||||
[0, 2, 0, 0, 0],
|
||||
[2, 0, -1, 0, 1],
|
||||
[-2, 2, 0, 2, 2],
|
||||
[0, 1, 0, 0, 1],
|
||||
[-2, 0, 1, 0, 1],
|
||||
[0, -1, 0, 0, 1],
|
||||
[0, 0, 2, -2, 0],
|
||||
[2, 0, -1, 2, 1],
|
||||
[2, 0, 1, 2, 2],
|
||||
[0, 1, 0, 2, 2],
|
||||
[-2, 1, 1, 0, 0],
|
||||
[0, -1, 0, 2, 2],
|
||||
[2, 0, 0, 2, 1],
|
||||
[2, 0, 1, 0, 0],
|
||||
[-2, 0, 2, 2, 2],
|
||||
[-2, 0, 1, 2, 1],
|
||||
[2, 0, -2, 0, 1],
|
||||
[2, 0, 0, 0, 1],
|
||||
[0, -1, 1, 0, 0],
|
||||
[-2, -1, 0, 2, 1],
|
||||
[-2, 0, 0, 0, 1],
|
||||
[0, 0, 2, 2, 1],
|
||||
[-2, 0, 2, 0, 1],
|
||||
[-2, 1, 0, 2, 1],
|
||||
[0, 0, 1, -2, 0],
|
||||
[-1, 0, 1, 0, 0],
|
||||
[-2, 1, 0, 0, 0],
|
||||
[1, 0, 0, 0, 0],
|
||||
[0, 0, 1, 2, 0],
|
||||
[0, 0, -2, 2, 2],
|
||||
[-1, -1, 1, 0, 0],
|
||||
[0, 1, 1, 0, 0],
|
||||
[0, -1, 1, 2, 2],
|
||||
[2, -1, -1, 2, 2],
|
||||
[0, 0, 3, 2, 2],
|
||||
[2, -1, 0, 2, 2],
|
||||
];
|
||||
var PE_TERMS = [
|
||||
[-171996, -174.2, 92025, 8.9],
|
||||
[-13187, -1.6, 5736, -3.1],
|
||||
[-2274, -0.2, 977, -0.5],
|
||||
[2062, 0.2, -895, 0.5],
|
||||
[1426, -3.4, 54, -0.1],
|
||||
[712, 0.1, -7, 0],
|
||||
[-517, 1.2, 224, -0.6],
|
||||
[-386, -0.4, 200, 0],
|
||||
[-301, 0, 129, -0.1],
|
||||
[217, -0.5, -95, 0.3],
|
||||
[-158, 0, 0, 0],
|
||||
[129, 0.1, -70, 0],
|
||||
[123, 0, -53, 0],
|
||||
[63, 0, 0, 0],
|
||||
[63, 0.1, -33, 0],
|
||||
[-59, 0, 26, 0],
|
||||
[-58, -0.1, 32, 0],
|
||||
[-51, 0, 27, 0],
|
||||
[48, 0, 0, 0],
|
||||
[46, 0, -24, 0],
|
||||
[-38, 0, 16, 0],
|
||||
[-31, 0, 13, 0],
|
||||
[29, 0, 0, 0],
|
||||
[29, 0, -12, 0],
|
||||
[26, 0, 0, 0],
|
||||
[-22, 0, 0, 0],
|
||||
[21, 0, -10, 0],
|
||||
[17, -0.1, 0, 0],
|
||||
[16, 0, -8, 0],
|
||||
[-16, 0.1, 7, 0],
|
||||
[-15, 0, 9, 0],
|
||||
[-13, 0, 7, 0],
|
||||
[-12, 0, 6, 0],
|
||||
[11, 0, 0, 0],
|
||||
[-10, 0, 5, 0],
|
||||
[-8, 0, 3, 0],
|
||||
[7, 0, -3, 0],
|
||||
[-7, 0, 0, 0],
|
||||
[-7, 0, 3, 0],
|
||||
[-7, 0, 3, 0],
|
||||
[6, 0, 0, 0],
|
||||
[6, 0, -3, 0],
|
||||
[6, 0, -3, 0],
|
||||
[-6, 0, 3, 0],
|
||||
[-6, 0, 3, 0],
|
||||
[5, 0, 0, 0],
|
||||
[-5, 0, 3, 0],
|
||||
[-5, 0, 3, 0],
|
||||
[-5, 0, 3, 0],
|
||||
[4, 0, 0, 0],
|
||||
[4, 0, 0, 0],
|
||||
[4, 0, 0, 0],
|
||||
[-4, 0, 0, 0],
|
||||
[-4, 0, 0, 0],
|
||||
[-4, 0, 0, 0],
|
||||
[3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
[-3, 0, 0, 0],
|
||||
];
|
||||
//=================== Utility functions for other applications (such as NREL's SAMPA) --------------
|
||||
function deg2rad(degrees) {
|
||||
return (PI / 180.0) * degrees;
|
||||
}
|
||||
exports.deg2rad = deg2rad;
|
||||
function rad2deg(radians) {
|
||||
return (180.0 / PI) * radians;
|
||||
}
|
||||
exports.rad2deg = rad2deg;
|
||||
function limit_degrees(degrees) {
|
||||
degrees /= 360;
|
||||
var limited = 360 * (degrees - Math.floor(degrees));
|
||||
if (limited < 0) {
|
||||
limited += 360;
|
||||
}
|
||||
return limited;
|
||||
}
|
||||
exports.limit_degrees = limit_degrees;
|
||||
function third_order_polynomial(a, b, c, d, x) {
|
||||
return ((a * x + b) + c) * x + d;
|
||||
}
|
||||
exports.third_order_polynomial = third_order_polynomial;
|
||||
function geocentric_right_ascension(lamda, epsilon, beta) {
|
||||
var lambdaRad = deg2rad(lamda);
|
||||
var epsilonRad = deg2rad(epsilon);
|
||||
return limit_degrees(rad2deg(Math.atan2(Math.sin(lambdaRad) * Math.cos(epsilonRad) -
|
||||
Math.tan(deg2rad(beta)) * Math.sin(epsilonRad), Math.cos(lambdaRad))));
|
||||
}
|
||||
exports.geocentric_right_ascension = geocentric_right_ascension;
|
||||
function geocentric_declination(beta, epsilon, lamda) {
|
||||
var betaRad = deg2rad(beta);
|
||||
var epsilonRad = deg2rad(epsilon);
|
||||
return rad2deg(Math.asin(Math.sin(betaRad) * Math.cos(epsilonRad) +
|
||||
Math.cos(betaRad) * Math.sin(epsilonRad) * Math.sin(deg2rad(lamda))));
|
||||
}
|
||||
exports.geocentric_declination = geocentric_declination;
|
||||
function observer_hour_angle(nu, longitude, alpha_deg) {
|
||||
return limit_degrees(nu + longitude - alpha_deg);
|
||||
}
|
||||
exports.observer_hour_angle = observer_hour_angle;
|
||||
function right_ascension_parallax_and_topocentric_dec(latitude, elevation, xi, h, delta, dltap) {
|
||||
var delta_alpha_rad = 0;
|
||||
var lat_rad = deg2rad(latitude);
|
||||
var xi_rad = deg2rad(xi);
|
||||
var h_rad = deg2rad(h);
|
||||
var delta_rad = deg2rad(delta);
|
||||
var u = Math.atan(0.99664719 * Math.tan(lat_rad));
|
||||
var y = 0.99664719 * Math.sin(u) + elevation * Math.sin(lat_rad) / 6378140.0;
|
||||
var x = Math.cos(u) + elevation * Math.cos(lat_rad) / 6378140.0;
|
||||
delta_alpha_rad = Math.atan2(-x * Math.sin(xi_rad) * Math.sin(h_rad), Math.cos(delta_rad) - x * Math.sin(xi_rad) * Math.cos(h_rad));
|
||||
dltap.delta_prime = rad2deg(Math.atan2((Math.sin(delta_rad) - y * Math.sin(xi_rad)) * Math.cos(delta_alpha_rad), Math.cos(delta_rad) - x * Math.sin(xi_rad) * Math.cos(h_rad)));
|
||||
dltap.delta_alpha = rad2deg(delta_alpha_rad);
|
||||
}
|
||||
exports.right_ascension_parallax_and_topocentric_dec = right_ascension_parallax_and_topocentric_dec;
|
||||
function topocentric_right_ascension(alpha_deg, delta_alpha) {
|
||||
return alpha_deg + delta_alpha;
|
||||
}
|
||||
exports.topocentric_right_ascension = topocentric_right_ascension;
|
||||
function topocentric_local_hour_angle(h, delta_alpha) {
|
||||
return h - delta_alpha;
|
||||
}
|
||||
exports.topocentric_local_hour_angle = topocentric_local_hour_angle;
|
||||
function topocentric_elevation_angle(latitude, delta_prime, h_prime) {
|
||||
var latRad = deg2rad(latitude);
|
||||
var deltaPrimeRad = deg2rad(delta_prime);
|
||||
return rad2deg(Math.asin(Math.sin(latRad) * Math.sin(deltaPrimeRad) +
|
||||
Math.cos(latRad) * Math.cos(deltaPrimeRad) * Math.cos(deg2rad(h_prime))));
|
||||
}
|
||||
exports.topocentric_elevation_angle = topocentric_elevation_angle;
|
||||
function atmospheric_refraction_correction(pressure, temperature, atmos_refract, e0) {
|
||||
var delE = 0;
|
||||
if (e0 >= -1 * (SUN_RADIUS + atmos_refract))
|
||||
delE = (pressure / 1010.0) * (283.0 / (273.0 + temperature)) *
|
||||
1.02 / (60.0 * Math.tan(deg2rad(e0 + 10.3 / (e0 + 5.11))));
|
||||
return delE;
|
||||
}
|
||||
exports.atmospheric_refraction_correction = atmospheric_refraction_correction;
|
||||
function topocentric_elevation_angle_corrected(e0, delta_e) {
|
||||
return e0 + delta_e;
|
||||
}
|
||||
exports.topocentric_elevation_angle_corrected = topocentric_elevation_angle_corrected;
|
||||
function topocentric_zenith_angle(e) {
|
||||
return 90.0 - e;
|
||||
}
|
||||
exports.topocentric_zenith_angle = topocentric_zenith_angle;
|
||||
function topocentric_azimuth_angle_astro(h_prime, latitude, delta_prime) {
|
||||
var h_prime_rad = deg2rad(h_prime);
|
||||
var lat_rad = deg2rad(latitude);
|
||||
return limit_degrees(rad2deg(Math.atan2(Math.sin(h_prime_rad), Math.cos(h_prime_rad) * Math.sin(lat_rad) - Math.tan(deg2rad(delta_prime)) * Math.cos(lat_rad))));
|
||||
}
|
||||
exports.topocentric_azimuth_angle_astro = topocentric_azimuth_angle_astro;
|
||||
function topocentric_azimuth_angle(azimuth_astro) {
|
||||
return limit_degrees(azimuth_astro + 180.0);
|
||||
}
|
||||
exports.topocentric_azimuth_angle = topocentric_azimuth_angle;
|
||||
//=================== Local Utility functions ===================
|
||||
function integer(val) {
|
||||
return Math.floor(val);
|
||||
}
|
||||
//===============================================================
|
||||
function validate_inputs(spa) {
|
||||
if ((spa.year < -2000) || (spa.year > 6000))
|
||||
return 1;
|
||||
if ((spa.month < 1) || (spa.month > 12))
|
||||
return 2;
|
||||
if ((spa.day < 1) || (spa.day > 31))
|
||||
return 3;
|
||||
if ((spa.hour < 0) || (spa.hour > 24))
|
||||
return 4;
|
||||
if ((spa.minute < 0) || (spa.minute > 59))
|
||||
return 5;
|
||||
if ((spa.second < 0) || (spa.second >= 60))
|
||||
return 6;
|
||||
if ((spa.pressure < 0) || (spa.pressure > 5000))
|
||||
return 12;
|
||||
if ((spa.temperature <= -273) || (spa.temperature > 6000))
|
||||
return 13;
|
||||
if ((spa.delta_ut1 <= -1) || (spa.delta_ut1 >= 1))
|
||||
return 17;
|
||||
if ((spa.hour == 24) && (spa.minute > 0))
|
||||
return 5;
|
||||
if ((spa.hour == 24) && (spa.second > 0))
|
||||
return 6;
|
||||
if (Math.abs(spa.delta_t) > 8000)
|
||||
return 7;
|
||||
if (Math.abs(spa.timezone) > 18)
|
||||
return 8;
|
||||
if (Math.abs(spa.longitude) > 180)
|
||||
return 9;
|
||||
if (Math.abs(spa.latitude) > 90)
|
||||
return 10;
|
||||
if (Math.abs(spa.atmos_refract) > 5)
|
||||
return 16;
|
||||
if (spa.elevation < -6500000)
|
||||
return 11;
|
||||
return 0;
|
||||
}
|
||||
//===============================================================
|
||||
function julian_day(year, month, day, hour, minute, second, dut1, tz) {
|
||||
var day_decimal = 0.0;
|
||||
var julian_day = 0.0;
|
||||
var a = 0.0;
|
||||
day_decimal = day + (hour - tz + (minute + (second + dut1) / 60.0) / 60.0) / 24.0;
|
||||
if (month < 3) {
|
||||
month += 12;
|
||||
year--;
|
||||
}
|
||||
julian_day = integer(365.25 * (year + 4716.0)) + integer(30.6001 * (month + 1)) + day_decimal - 1524.5;
|
||||
if (julian_day > 2299160.0) {
|
||||
a = integer(year / 100);
|
||||
julian_day += (2 - a + integer(a / 4));
|
||||
}
|
||||
return julian_day;
|
||||
}
|
||||
function julian_century(jd) {
|
||||
return (jd - 2451545.0) / 36525.0;
|
||||
}
|
||||
function mean_elongation_moon_sun(jce) {
|
||||
return third_order_polynomial(1.0 / 189474.0, -0.0019142, 445267.11148, 297.85036, jce);
|
||||
}
|
||||
function julian_ephemeris_day(jd, delta_t) {
|
||||
return jd + delta_t / 86400.0;
|
||||
}
|
||||
function julian_ephemeris_century(jde) {
|
||||
return (jde - 2451545.0) / 36525.0;
|
||||
}
|
||||
function julian_ephemeris_millennium(jce) {
|
||||
return (jce / 10.0);
|
||||
}
|
||||
function earth_periodic_term_summation(terms, count, jme) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < count; i++)
|
||||
sum += terms[i][TermsA.TERM_A]
|
||||
* Math.cos(terms[i][TermsA.TERM_B]
|
||||
+ terms[i][TermsA.TERM_C] * jme);
|
||||
return sum;
|
||||
}
|
||||
function earth_values(term_sum, count, jme) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < count; i++)
|
||||
sum += term_sum[i] * Math.pow(jme, i);
|
||||
sum /= 1.0e8;
|
||||
return sum;
|
||||
}
|
||||
function earth_heliocentric_longitude(jme) {
|
||||
var sum = [];
|
||||
for (var i = 0; i < L_COUNT; i++)
|
||||
sum[i] = earth_periodic_term_summation(L_TERMS[i], l_subcount[i], jme);
|
||||
return limit_degrees(rad2deg(earth_values(sum, L_COUNT, jme)));
|
||||
}
|
||||
function earth_heliocentric_latitude(jme) {
|
||||
var sum = [];
|
||||
for (var i = 0; i < B_COUNT; i++)
|
||||
sum[i] = earth_periodic_term_summation(B_TERMS[i], b_subcount[i], jme);
|
||||
return rad2deg(earth_values(sum, B_COUNT, jme));
|
||||
}
|
||||
function earth_radius_vector(jme) {
|
||||
var sum = [];
|
||||
for (var i = 0; i < R_COUNT; i++)
|
||||
sum[i] = earth_periodic_term_summation(R_TERMS[i], r_subcount[i], jme);
|
||||
return earth_values(sum, R_COUNT, jme);
|
||||
}
|
||||
function geocentric_longitude(l) {
|
||||
var theta = l + 180.0;
|
||||
if (theta >= 360.0)
|
||||
theta -= 360.0;
|
||||
return theta;
|
||||
}
|
||||
function geocentric_latitude(b) {
|
||||
return -b;
|
||||
}
|
||||
function mean_anomaly_sun(jce) {
|
||||
return third_order_polynomial(-1.0 / 300000.0, -0.0001603, 35999.05034, 357.52772, jce);
|
||||
}
|
||||
function mean_anomaly_moon(jce) {
|
||||
return third_order_polynomial(1.0 / 56250.0, 0.0086972, 477198.867398, 134.96298, jce);
|
||||
}
|
||||
function argument_latitude_moon(jce) {
|
||||
return third_order_polynomial(1.0 / 327270.0, -0.0036825, 483202.017538, 93.27191, jce);
|
||||
}
|
||||
function ascending_longitude_moon(jce) {
|
||||
return third_order_polynomial(1.0 / 450000.0, 0.0020708, -1934.136261, 125.04452, jce);
|
||||
}
|
||||
function xy_term_summation(i, x) {
|
||||
var sum = 0;
|
||||
for (var j = 0; j < TERM_Y_COUNT; j++)
|
||||
sum += x[j] * Y_TERMS[i][j];
|
||||
return sum;
|
||||
}
|
||||
function nutation_longitude_and_obliquity(jce, x, spa) {
|
||||
var xy_term_sum;
|
||||
var sum_psi = 0;
|
||||
var sum_epsilon = 0;
|
||||
for (var i = 0; i < Y_COUNT; i++) {
|
||||
xy_term_sum = deg2rad(xy_term_summation(i, x));
|
||||
sum_psi += (PE_TERMS[i][TermsPE.TERM_PSI_A] + jce * PE_TERMS[i][TermsPE.TERM_PSI_B]) * Math.sin(xy_term_sum);
|
||||
sum_epsilon += (PE_TERMS[i][TermsPE.TERM_EPS_C] + jce * PE_TERMS[i][TermsPE.TERM_EPS_D]) * Math.cos(xy_term_sum);
|
||||
}
|
||||
spa.del_psi = sum_psi / 36000000.0;
|
||||
spa.del_epsilon = sum_epsilon / 36000000.0;
|
||||
}
|
||||
function ecliptic_mean_obliquity(jme) {
|
||||
var u = jme / 10.0;
|
||||
return 84381.448 + u * (-4680.93 + u * (-1.55 + u * (1999.25 + u * (-51.38 + u * (-249.67 +
|
||||
u * (-39.05 + u * (7.12 + u * (27.87 + u * (5.79 + u * 2.45)))))))));
|
||||
}
|
||||
function ecliptic_true_obliquity(delta_epsilon, epsilon0) {
|
||||
return delta_epsilon + epsilon0 / 3600.0;
|
||||
}
|
||||
function aberration_correction(r) {
|
||||
return -20.4898 / (3600.0 * r);
|
||||
}
|
||||
function apparent_sun_longitude(theta, delta_psi, delta_tau) {
|
||||
return theta + delta_psi + delta_tau;
|
||||
}
|
||||
function greenwich_mean_sidereal_time(jd, jc) {
|
||||
return limit_degrees(280.46061837 + 360.98564736629 * (jd - 2451545.0) +
|
||||
jc * jc * (0.000387933 - jc / 38710000.0));
|
||||
}
|
||||
function greenwich_sidereal_time(nu0, delta_psi, epsilon) {
|
||||
return nu0 + delta_psi * Math.cos(deg2rad(epsilon));
|
||||
}
|
||||
function sun_equatorial_horizontal_parallax(r) {
|
||||
return 8.794 / (3600.0 * r);
|
||||
}
|
||||
function surface_incidence_angle(zenith, azimuth_astro, azm_rotation, slope) {
|
||||
var zenith_rad = deg2rad(zenith);
|
||||
var slope_rad = deg2rad(slope);
|
||||
return rad2deg(Math.acos(Math.cos(zenith_rad) * Math.cos(slope_rad) +
|
||||
Math.sin(slope_rad) * Math.sin(zenith_rad) * Math.cos(deg2rad(azimuth_astro - azm_rotation))));
|
||||
}
|
||||
function sun_mean_longitude(jme) {
|
||||
return limit_degrees(280.4664567 + jme * (360007.6982779 + jme * (0.03032028 +
|
||||
jme * (1 / 49931.0 + jme * (-1 / 15300.0 + jme * (-1 / 2000000.0))))));
|
||||
}
|
||||
function limit_minutes(minutes) {
|
||||
var limited = minutes;
|
||||
if (limited < -20.0)
|
||||
limited += 1440.0;
|
||||
else if (limited > 20.0)
|
||||
limited -= 1440.0;
|
||||
return limited;
|
||||
}
|
||||
function eot(m, alpha, del_psi, epsilon) {
|
||||
return limit_minutes(4.0 * (m - 0.0057183 - alpha + del_psi * Math.cos(deg2rad(epsilon))));
|
||||
}
|
||||
function approx_sun_transit_time(alpha_zero, longitude, nu) {
|
||||
return (alpha_zero - longitude - nu) / 360.0;
|
||||
}
|
||||
function sun_hour_angle_at_rise_set(latitude, delta_zero, h0_prime) {
|
||||
var h0 = -99999;
|
||||
var latitude_rad = deg2rad(latitude);
|
||||
var delta_zero_rad = deg2rad(delta_zero);
|
||||
var argument = (Math.sin(deg2rad(h0_prime)) - Math.sin(latitude_rad) * Math.sin(delta_zero_rad)) /
|
||||
(Math.cos(latitude_rad) * Math.cos(delta_zero_rad));
|
||||
if (Math.abs(argument) <= 1)
|
||||
h0 = limit_degrees180(rad2deg(Math.acos(argument)));
|
||||
return h0;
|
||||
}
|
||||
function limit_zero2one(value) {
|
||||
var limited = value - Math.floor(value);
|
||||
if (limited < 0)
|
||||
limited += 1.0;
|
||||
return limited;
|
||||
}
|
||||
function approx_sun_rise_and_set(m_rts, h0) {
|
||||
var h0_dfrac = h0 / 360.0;
|
||||
m_rts[SunState.SUN_RISE] = limit_zero2one(m_rts[SunState.SUN_TRANSIT] - h0_dfrac);
|
||||
m_rts[SunState.SUN_SET] = limit_zero2one(m_rts[SunState.SUN_TRANSIT] + h0_dfrac);
|
||||
m_rts[SunState.SUN_TRANSIT] = limit_zero2one(m_rts[SunState.SUN_TRANSIT]);
|
||||
}
|
||||
function rts_alpha_delta_prime(ad, n) {
|
||||
var a = ad[JDSign.JD_ZERO] - ad[JDSign.JD_MINUS];
|
||||
var b = ad[JDSign.JD_PLUS] - ad[JDSign.JD_ZERO];
|
||||
if (Math.abs(a) >= 2.0)
|
||||
a = limit_zero2one(a);
|
||||
if (Math.abs(b) >= 2.0)
|
||||
b = limit_zero2one(b);
|
||||
return ad[JDSign.JD_ZERO] + n * (a + b + (b - a) * n) / 2.0;
|
||||
}
|
||||
function limit_degrees180pm(degrees) {
|
||||
var limited;
|
||||
degrees /= 360.0;
|
||||
limited = 360.0 * (degrees - Math.floor(degrees));
|
||||
if (limited < -180.0)
|
||||
limited += 360.0;
|
||||
else if (limited > 180.0)
|
||||
limited -= 360.0;
|
||||
return limited;
|
||||
}
|
||||
function limit_degrees180(degrees) {
|
||||
var limited;
|
||||
degrees /= 180.0;
|
||||
limited = 180.0 * (degrees - Math.floor(degrees));
|
||||
if (limited < 0)
|
||||
limited += 180.0;
|
||||
return limited;
|
||||
}
|
||||
function rts_sun_altitude(latitude, delta_prime, h_prime) {
|
||||
var latitude_rad = deg2rad(latitude);
|
||||
var delta_prime_rad = deg2rad(delta_prime);
|
||||
return rad2deg(Math.asin(Math.sin(latitude_rad) * Math.sin(delta_prime_rad) +
|
||||
Math.cos(latitude_rad) * Math.cos(delta_prime_rad) * Math.cos(deg2rad(h_prime))));
|
||||
}
|
||||
function sun_rise_and_set(m_rts, h_rts, delta_prime, latitude, h_prime, h0_prime, sun) {
|
||||
return m_rts[sun] + (h_rts[sun] - h0_prime) /
|
||||
(360.0 * Math.cos(deg2rad(delta_prime[sun])) * Math.cos(deg2rad(latitude)) * Math.sin(deg2rad(h_prime[sun])));
|
||||
}
|
||||
function dayfrac_to_local_hr(dayfrac, timezone) {
|
||||
return 24.0 * limit_zero2one(dayfrac + timezone / 24.0);
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Calculate Equation of Time (EOT) and Sun Rise, Transit, & Set (RTS)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
function calculate_eot_and_sun_rise_transit_set(spa) {
|
||||
var nu = 0;
|
||||
var m = 0;
|
||||
var h0 = 0;
|
||||
var n = 0;
|
||||
var alpha = [];
|
||||
var delta = [];
|
||||
var m_rts = [];
|
||||
var nu_rts = [];
|
||||
var h_rts = [];
|
||||
var alpha_prime = [];
|
||||
var delta_prime = [];
|
||||
var h_prime = [];
|
||||
var h0_prime = -1 * (SUN_RADIUS + spa.atmos_refract);
|
||||
var sun_rts = copySPA(spa);
|
||||
sun_rts.hour = sun_rts.minute = sun_rts.second = 0;
|
||||
sun_rts.delta_ut1 = sun_rts.timezone = 0.0;
|
||||
sun_rts.jd = julian_day(sun_rts.year, sun_rts.month, sun_rts.day, sun_rts.hour, sun_rts.minute, sun_rts.second, sun_rts.delta_ut1, sun_rts.timezone);
|
||||
m = sun_mean_longitude(spa.jme);
|
||||
spa.eot = eot(m, spa.alpha, spa.del_psi, spa.epsilon);
|
||||
calculate_geocentric_sun_right_ascension_and_declination(sun_rts);
|
||||
nu = sun_rts.nu;
|
||||
sun_rts.delta_t = 0;
|
||||
sun_rts.jd--;
|
||||
for (var i = 0; i < JDSign.JD_COUNT; i++) {
|
||||
calculate_geocentric_sun_right_ascension_and_declination(sun_rts);
|
||||
alpha[i] = sun_rts.alpha;
|
||||
delta[i] = sun_rts.delta;
|
||||
sun_rts.jd++;
|
||||
}
|
||||
m_rts[SunState.SUN_TRANSIT] = approx_sun_transit_time(alpha[JDSign.JD_ZERO], spa.longitude, nu);
|
||||
h0 = sun_hour_angle_at_rise_set(spa.latitude, delta[JDSign.JD_ZERO], h0_prime);
|
||||
if (h0 >= 0) {
|
||||
approx_sun_rise_and_set(m_rts, h0);
|
||||
for (var i = 0; i < SunState.SUN_COUNT; i++) {
|
||||
nu_rts[i] = nu + 360.985647 * m_rts[i];
|
||||
n = m_rts[i] + spa.delta_t / 86400.0;
|
||||
alpha_prime[i] = rts_alpha_delta_prime(alpha, n);
|
||||
delta_prime[i] = rts_alpha_delta_prime(delta, n);
|
||||
h_prime[i] = limit_degrees180pm(nu_rts[i] + spa.longitude - alpha_prime[i]);
|
||||
h_rts[i] = rts_sun_altitude(spa.latitude, delta_prime[i], h_prime[i]);
|
||||
}
|
||||
spa.srha = h_prime[SunState.SUN_RISE];
|
||||
spa.ssha = h_prime[SunState.SUN_SET];
|
||||
spa.sta = h_rts[SunState.SUN_TRANSIT];
|
||||
spa.suntransit = dayfrac_to_local_hr(m_rts[SunState.SUN_TRANSIT] - h_prime[SunState.SUN_TRANSIT] / 360.0, spa.timezone);
|
||||
spa.sunrise = dayfrac_to_local_hr(sun_rise_and_set(m_rts, h_rts, delta_prime, spa.latitude, h_prime, h0_prime, SunState.SUN_RISE), spa.timezone);
|
||||
spa.sunset = dayfrac_to_local_hr(sun_rise_and_set(m_rts, h_rts, delta_prime, spa.latitude, h_prime, h0_prime, SunState.SUN_SET), spa.timezone);
|
||||
}
|
||||
else
|
||||
spa.srha = spa.ssha = spa.sta = spa.suntransit = spa.sunrise = spa.sunset = -99999;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Calculate required SPA parameters to get the right ascension (alpha) and declination (delta)
|
||||
// Note: JD must be already calculated and in structure
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
function calculate_geocentric_sun_right_ascension_and_declination(spa) {
|
||||
spa.jc = julian_century(spa.jd);
|
||||
spa.jde = julian_ephemeris_day(spa.jd, spa.delta_t);
|
||||
spa.jce = julian_ephemeris_century(spa.jde);
|
||||
spa.jme = julian_ephemeris_millennium(spa.jce);
|
||||
spa.l = earth_heliocentric_longitude(spa.jme);
|
||||
spa.b = earth_heliocentric_latitude(spa.jme);
|
||||
spa.r = earth_radius_vector(spa.jme);
|
||||
spa.theta = geocentric_longitude(spa.l);
|
||||
spa.beta = geocentric_latitude(spa.b);
|
||||
var x = [];
|
||||
x[TermsX.TERM_X0] = spa.x0 = mean_elongation_moon_sun(spa.jce);
|
||||
x[TermsX.TERM_X1] = spa.x1 = mean_anomaly_sun(spa.jce);
|
||||
x[TermsX.TERM_X2] = spa.x2 = mean_anomaly_moon(spa.jce);
|
||||
x[TermsX.TERM_X3] = spa.x3 = argument_latitude_moon(spa.jce);
|
||||
x[TermsX.TERM_X4] = spa.x4 = ascending_longitude_moon(spa.jce);
|
||||
nutation_longitude_and_obliquity(spa.jce, x, spa);
|
||||
spa.epsilon0 = ecliptic_mean_obliquity(spa.jme);
|
||||
spa.epsilon = ecliptic_true_obliquity(spa.del_epsilon, spa.epsilon0);
|
||||
spa.del_tau = aberration_correction(spa.r);
|
||||
spa.lamda = apparent_sun_longitude(spa.theta, spa.del_psi, spa.del_tau);
|
||||
spa.nu0 = greenwich_mean_sidereal_time(spa.jd, spa.jc);
|
||||
spa.nu = greenwich_sidereal_time(spa.nu0, spa.del_psi, spa.epsilon);
|
||||
spa.alpha = geocentric_right_ascension(spa.lamda, spa.epsilon, spa.beta);
|
||||
spa.delta = geocentric_declination(spa.beta, spa.epsilon, spa.lamda);
|
||||
}
|
||||
//Calculate SPA output values (in structure) based on input values passed in structure
|
||||
function spa_calculate(spa) {
|
||||
var result = validate_inputs(spa);
|
||||
if (result == 0) {
|
||||
spa.jd = julian_day(spa.year, spa.month, spa.day, spa.hour, spa.minute, spa.second, spa.delta_ut1, spa.timezone);
|
||||
calculate_geocentric_sun_right_ascension_and_declination(spa);
|
||||
spa.h = observer_hour_angle(spa.nu, spa.longitude, spa.alpha);
|
||||
spa.xi = sun_equatorial_horizontal_parallax(spa.r);
|
||||
var dltap = { delta_alpha: spa.del_alpha, delta_prime: spa.delta_prime };
|
||||
right_ascension_parallax_and_topocentric_dec(spa.latitude, spa.elevation, spa.xi, spa.h, spa.delta, dltap);
|
||||
spa.del_alpha = dltap.delta_alpha;
|
||||
spa.delta_prime = dltap.delta_prime;
|
||||
spa.alpha_prime = topocentric_right_ascension(spa.alpha, spa.del_alpha);
|
||||
spa.h_prime = topocentric_local_hour_angle(spa.h, spa.del_alpha);
|
||||
spa.e0 = topocentric_elevation_angle(spa.latitude, spa.delta_prime, spa.h_prime);
|
||||
spa.del_e = atmospheric_refraction_correction(spa.pressure, spa.temperature, spa.atmos_refract, spa.e0);
|
||||
spa.e = topocentric_elevation_angle_corrected(spa.e0, spa.del_e);
|
||||
spa.zenith = topocentric_zenith_angle(spa.e);
|
||||
spa.azimuth_astro = topocentric_azimuth_angle_astro(spa.h_prime, spa.latitude, spa.delta_prime);
|
||||
spa.azimuth = topocentric_azimuth_angle(spa.azimuth_astro);
|
||||
if ((spa.function == exports.SPA_ZA_INC) || (spa.function == exports.SPA_ALL))
|
||||
spa.incidence = surface_incidence_angle(spa.zenith, spa.azimuth_astro, spa.azm_rotation, spa.slope);
|
||||
if ((spa.function == exports.SPA_ZA_RTS) || (spa.function == exports.SPA_ALL))
|
||||
calculate_eot_and_sun_rise_transit_set(spa);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.spa_calculate = spa_calculate;
|
||||
21
package.json
21
package.json
|
|
@ -9,15 +9,11 @@
|
|||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
|
|
@ -38,7 +34,8 @@
|
|||
"lint": "eslint src/",
|
||||
"format": "prettier --write src/",
|
||||
"format:check": "prettier --check src/",
|
||||
"prepublishOnly": "tsup"
|
||||
"prepublishOnly": "tsup",
|
||||
"coverage": "c8 --reporter=lcov --reporter=text node --test"
|
||||
},
|
||||
"keywords": [
|
||||
"solar",
|
||||
|
|
@ -78,5 +75,7 @@
|
|||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.56.1"
|
||||
}
|
||||
},
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.11.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const DEG = Math.PI / 180;
|
|||
// option (see tsup.config.ts). In CJS builds, require() is natively available.
|
||||
declare const __require: NodeRequire;
|
||||
const _load = typeof __require === 'function' ? __require : require;
|
||||
const spa = _load('../lib/spa.js') as {
|
||||
const spa = _load('../lib/spa.cjs') as {
|
||||
SpaData: new () => SpaDataInstance;
|
||||
SPA_ZA_RTS: number;
|
||||
spa_calculate: (data: SpaDataInstance) => number;
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ export default defineConfig({
|
|||
// The core SPA algorithm lives in lib/spa.js (the JS port of the NREL C source).
|
||||
// It is checked into git and ships with the package. We load it at runtime so it
|
||||
// is kept external (not bundled) and resolves via the createRequire shim in ESM.
|
||||
external: ['../lib/spa.js'],
|
||||
external: ['../lib/spa.cjs'],
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue