@openwaters/almanac 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -11
- package/dist/angles.d.ts +4 -0
- package/dist/angles.js +16 -0
- package/dist/apsides.d.ts +14 -0
- package/dist/apsides.js +88 -0
- package/dist/events.d.ts +53 -0
- package/dist/events.js +62 -13
- package/dist/illumination.d.ts +25 -0
- package/dist/illumination.js +40 -2
- package/dist/index.d.ts +10 -5
- package/dist/index.js +7 -3
- package/dist/planetModels.js +795 -1
- package/dist/planets.js +7 -0
- package/dist/positions.d.ts +15 -0
- package/dist/positions.js +28 -1
- package/dist/seasons.d.ts +17 -0
- package/dist/seasons.js +73 -0
- package/dist/transforms.d.ts +6 -3
- package/dist/transforms.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/package.json +3 -2
package/dist/planets.js
CHANGED
|
@@ -35,6 +35,13 @@ function visualMagnitude(planet, phase, helioDist, geoDist) {
|
|
|
35
35
|
c0 = -9.40;
|
|
36
36
|
c1 = 0.50;
|
|
37
37
|
break;
|
|
38
|
+
case 'uranus':
|
|
39
|
+
c0 = -7.19;
|
|
40
|
+
c1 = 0.25;
|
|
41
|
+
break;
|
|
42
|
+
case 'neptune':
|
|
43
|
+
c0 = -6.87;
|
|
44
|
+
break;
|
|
38
45
|
default: throw new Error('unsupported planet in visualMagnitude');
|
|
39
46
|
}
|
|
40
47
|
const x = phase / 100;
|
package/dist/positions.d.ts
CHANGED
|
@@ -32,3 +32,18 @@ export declare function sunPosition(time: Date): SunPosition;
|
|
|
32
32
|
* the Earth-Moon centre distance in kilometres.
|
|
33
33
|
*/
|
|
34
34
|
export declare function moonPosition(time: Date): MoonPosition;
|
|
35
|
+
/**
|
|
36
|
+
* INTERNAL: equation of time for a TT instant, in minutes. Not from upstream:
|
|
37
|
+
* Meeus, *Astronomical Algorithms*, formula 28.1, `E = L0 − 0.0057183° − α +
|
|
38
|
+
* Δψ cos ε`, with `L0` the Sun's mean longitude from formula 28.2 and `α` the
|
|
39
|
+
* apparent right ascension `sunApparentAtTT` already computes. The constant
|
|
40
|
+
* is aberration plus the FK5 correction; `Δψ cos ε` moves `L0` from the mean
|
|
41
|
+
* equinox to the true one `α` is measured from.
|
|
42
|
+
*/
|
|
43
|
+
export declare function equationOfTimeAtTT(tt: number): number;
|
|
44
|
+
/**
|
|
45
|
+
* Equation of time: apparent solar time minus mean solar time, in minutes.
|
|
46
|
+
* Positive when a sundial runs ahead of the clock; about +16.4 at the start
|
|
47
|
+
* of November and −14.2 in mid February.
|
|
48
|
+
*/
|
|
49
|
+
export declare function equationOfTime(time: Date): number;
|
package/dist/positions.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// parallax subtraction) is zero and drops out.
|
|
14
14
|
import { assertSupported } from './types.js';
|
|
15
15
|
import { ttDays } from './time.js';
|
|
16
|
-
import { KM_PER_AU, equatorialFromVector } from './nutation.js';
|
|
16
|
+
import { DEG2RAD, KM_PER_AU, earthTilt, equatorialFromVector } from './nutation.js';
|
|
17
17
|
import { moonGeoVector } from './moon.js';
|
|
18
18
|
import { sunGeoVector } from './sun.js';
|
|
19
19
|
/**
|
|
@@ -50,3 +50,30 @@ export function moonPosition(time) {
|
|
|
50
50
|
assertSupported(time);
|
|
51
51
|
return moonApparentAtTT(ttDays(time));
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* INTERNAL: equation of time for a TT instant, in minutes. Not from upstream:
|
|
55
|
+
* Meeus, *Astronomical Algorithms*, formula 28.1, `E = L0 − 0.0057183° − α +
|
|
56
|
+
* Δψ cos ε`, with `L0` the Sun's mean longitude from formula 28.2 and `α` the
|
|
57
|
+
* apparent right ascension `sunApparentAtTT` already computes. The constant
|
|
58
|
+
* is aberration plus the FK5 correction; `Δψ cos ε` moves `L0` from the mean
|
|
59
|
+
* equinox to the true one `α` is measured from.
|
|
60
|
+
*/
|
|
61
|
+
export function equationOfTimeAtTT(tt) {
|
|
62
|
+
const tau = tt / 365250;
|
|
63
|
+
const l0 = 280.4664567 + tau * (360007.6982779 + tau * (0.03032028
|
|
64
|
+
+ tau * (1 / 49931 + tau * (-1 / 15300 + tau * (-1 / 2000000)))));
|
|
65
|
+
const tilt = earthTilt(tt);
|
|
66
|
+
const nutationRaDeg = (tilt.dpsi / 3600) * Math.cos(tilt.tobl * DEG2RAD);
|
|
67
|
+
let e = l0 - 0.0057183 - sunApparentAtTT(tt).raDeg + nutationRaDeg;
|
|
68
|
+
e = ((e % 360) + 540) % 360 - 180; // (−180, 180]
|
|
69
|
+
return e * 4; // 360° of right ascension is 1440 minutes
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Equation of time: apparent solar time minus mean solar time, in minutes.
|
|
73
|
+
* Positive when a sundial runs ahead of the clock; about +16.4 at the start
|
|
74
|
+
* of November and −14.2 in mid February.
|
|
75
|
+
*/
|
|
76
|
+
export function equationOfTime(time) {
|
|
77
|
+
assertSupported(time);
|
|
78
|
+
return equationOfTimeAtTT(ttDays(time));
|
|
79
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** The season the Sun's longitude starts: 0° March equinox, 90° June solstice, 180° September equinox, 270° December solstice. */
|
|
2
|
+
export type SeasonKind = 'marchEquinox' | 'juneSolstice' | 'septemberEquinox' | 'decemberSolstice';
|
|
3
|
+
/** An equinox or solstice. */
|
|
4
|
+
export interface SeasonEvent {
|
|
5
|
+
time: Date;
|
|
6
|
+
kind: SeasonKind;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The equinoxes and solstices within the half-open window `[startUtc, endUtc)`,
|
|
10
|
+
* sorted ascending.
|
|
11
|
+
*
|
|
12
|
+
* A season starts the instant the Sun's apparent geocentric ecliptic longitude
|
|
13
|
+
* reaches 0° (March equinox), 90° (June solstice), 180° (September equinox) or
|
|
14
|
+
* 270° (December solstice). The names are months, not seasons, because the
|
|
15
|
+
* same instant starts opposite seasons in the two hemispheres.
|
|
16
|
+
*/
|
|
17
|
+
export declare function seasons(startUtc: Date, endUtc: Date): SeasonEvent[];
|
package/dist/seasons.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// L3 season search: the four instants a year when the Sun's apparent
|
|
2
|
+
// geocentric ecliptic longitude reaches 0°, 90°, 180° and 270°.
|
|
3
|
+
//
|
|
4
|
+
// Translated from the cosinekitty/astronomy upstream, pinned sha
|
|
5
|
+
// 865d3da7d8112bbc7911238052c6af4aaf877181, source/js/astronomy.ts:
|
|
6
|
+
// `SearchSunLongitude` (~4787), whose `sun_offset` and 0.01 s `Search` are
|
|
7
|
+
// kept. Upstream's `Seasons(year)` (~6130) brackets each search from a fixed
|
|
8
|
+
// calendar date. Here the bracket is a cell of a fixed 80-day grid anchored at
|
|
9
|
+
// J2000, so every window that contains a season finds it from the same bracket
|
|
10
|
+
// and reports the same millisecond — the fixed-seed rule the eclipse searches
|
|
11
|
+
// follow. A cell spans under 90° of solar longitude, so it holds at most one
|
|
12
|
+
// quarter crossing.
|
|
13
|
+
//
|
|
14
|
+
// INTERNAL except `seasons`: not part of the curated public API (see index.ts).
|
|
15
|
+
import { assertSupported, assertSupportedWindowEnd } from './types.js';
|
|
16
|
+
import { utDays, ttDaysFromUt, dateFromUt } from './time.js';
|
|
17
|
+
import { sunEclipticLonDeg } from './illumination.js';
|
|
18
|
+
import { search, angleOffset, quantizedUt, assertReached } from './events.js';
|
|
19
|
+
const SEASON_KINDS = ['marchEquinox', 'juneSolstice', 'septemberEquinox', 'decemberSolstice'];
|
|
20
|
+
/** Grid cell, in days since J2000 UT: under the 89-day shortest season, so a cell holds at most one crossing. */
|
|
21
|
+
const SEASON_CELL_DAYS = 80;
|
|
22
|
+
function sunLonDeg(ut) {
|
|
23
|
+
return sunEclipticLonDeg(ttDaysFromUt(ut));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* UPSTREAM: `SearchSunLongitude` on one grid cell `[t1, t2]`, with
|
|
27
|
+
* `lon1 = sunLonDeg(t1)` already in hand. Returns the crossing of the next
|
|
28
|
+
* multiple of 90° after `lon1`, or `null` when the cell ends before it.
|
|
29
|
+
*/
|
|
30
|
+
function searchSeasonInCell(t1, t2, lon1) {
|
|
31
|
+
const quarter = (Math.floor(lon1 / 90) + 1) % 4;
|
|
32
|
+
const targetLonDeg = 90 * quarter;
|
|
33
|
+
const sunOffset = (ut) => angleOffset(sunLonDeg(ut) - targetLonDeg);
|
|
34
|
+
if (sunOffset(t2) < 0)
|
|
35
|
+
return null;
|
|
36
|
+
const ut = search(sunOffset, t1, t2, 0.01, undefined, 'season search');
|
|
37
|
+
assertReached(ut !== null, 'season search');
|
|
38
|
+
return { ut: ut, quarter };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The equinoxes and solstices within the half-open window `[startUtc, endUtc)`,
|
|
42
|
+
* sorted ascending.
|
|
43
|
+
*
|
|
44
|
+
* A season starts the instant the Sun's apparent geocentric ecliptic longitude
|
|
45
|
+
* reaches 0° (March equinox), 90° (June solstice), 180° (September equinox) or
|
|
46
|
+
* 270° (December solstice). The names are months, not seasons, because the
|
|
47
|
+
* same instant starts opposite seasons in the two hemispheres.
|
|
48
|
+
*/
|
|
49
|
+
export function seasons(startUtc, endUtc) {
|
|
50
|
+
assertSupported(startUtc);
|
|
51
|
+
assertSupportedWindowEnd(endUtc);
|
|
52
|
+
if (startUtc.getTime() >= endUtc.getTime())
|
|
53
|
+
return [];
|
|
54
|
+
const startUt = utDays(startUtc);
|
|
55
|
+
const endUt = utDays(endUtc);
|
|
56
|
+
const events = [];
|
|
57
|
+
let t1 = Math.floor(startUt / SEASON_CELL_DAYS) * SEASON_CELL_DAYS;
|
|
58
|
+
let lon1 = sunLonDeg(t1);
|
|
59
|
+
while (t1 < endUt) {
|
|
60
|
+
const t2 = t1 + SEASON_CELL_DAYS;
|
|
61
|
+
const found = searchSeasonInCell(t1, t2, lon1);
|
|
62
|
+
if (found !== null) {
|
|
63
|
+
const q = quantizedUt(found.ut); // judge the half-open bounds on the reported instant, not the raw root
|
|
64
|
+
if (q >= endUt)
|
|
65
|
+
break;
|
|
66
|
+
if (q >= startUt)
|
|
67
|
+
events.push({ time: dateFromUt(found.ut), kind: SEASON_KINDS[found.quarter] });
|
|
68
|
+
}
|
|
69
|
+
t1 = t2;
|
|
70
|
+
lon1 = sunLonDeg(t1);
|
|
71
|
+
}
|
|
72
|
+
return events;
|
|
73
|
+
}
|
package/dist/transforms.d.ts
CHANGED
|
@@ -7,13 +7,16 @@ export interface AltAz {
|
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* INTERNAL: what {@link topoAltAzUnrefracted} already computes on the way to
|
|
10
|
-
* az/alt. The L3 event layer needs
|
|
11
|
-
* local hour angle brackets each daily cycle's altitude extrema,
|
|
12
|
-
* topocentric distance sets the Moon's semidiameter, hence its rise/set target
|
|
10
|
+
* az/alt. The L3 event layer needs these from a single evaluation — the
|
|
11
|
+
* local hour angle brackets each daily cycle's altitude extrema, the
|
|
12
|
+
* topocentric distance sets the Moon's semidiameter, hence its rise/set target,
|
|
13
|
+
* and the topocentric RA/Dec orient the Moon's disc.
|
|
13
14
|
*/
|
|
14
15
|
export interface TopoUnrefracted extends AltAz {
|
|
15
16
|
hourAngleDeg: number;
|
|
16
17
|
distanceAu: number;
|
|
18
|
+
raDeg: number;
|
|
19
|
+
decDeg: number;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* UPSTREAM: `sidereal_time`, astronomy.ts ~2031 — Greenwich Apparent Sidereal
|
package/dist/transforms.js
CHANGED
|
@@ -160,7 +160,7 @@ export function topoAltAzUnrefracted(bodyEqj, ut, observer) {
|
|
|
160
160
|
let hourAngleDeg = (gast + observer.longitudeDeg - raDeg) % 360;
|
|
161
161
|
if (hourAngleDeg < 0)
|
|
162
162
|
hourAngleDeg += 360;
|
|
163
|
-
return { azDeg: az, altDeg: 90 - zd, hourAngleDeg, distanceAu };
|
|
163
|
+
return { azDeg: az, altDeg: 90 - zd, hourAngleDeg, distanceAu, raDeg, decDeg };
|
|
164
164
|
}
|
|
165
165
|
/**
|
|
166
166
|
* UPSTREAM: `Refraction('normal', altitude)`, astronomy.ts ~7176 — the
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface Observer {
|
|
|
3
3
|
longitudeDeg: number;
|
|
4
4
|
elevationM?: number;
|
|
5
5
|
}
|
|
6
|
-
export type Planet = 'mercury' | 'venus' | 'earth' | 'mars' | 'jupiter' | 'saturn';
|
|
6
|
+
export type Planet = 'mercury' | 'venus' | 'earth' | 'mars' | 'jupiter' | 'saturn' | 'uranus' | 'neptune';
|
|
7
7
|
/** INTERNAL: Earth has no planetary direction from an Earth-based observer. */
|
|
8
8
|
export declare function assertPlanet(planet: Planet, earthAllowed?: boolean): void;
|
|
9
9
|
export declare class AlmanacOutOfRangeError extends RangeError {
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** INTERNAL: Earth has no planetary direction from an Earth-based observer. */
|
|
2
2
|
export function assertPlanet(planet, earthAllowed = false) {
|
|
3
|
-
if (!['mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn'].includes(planet) || (!earthAllowed && planet === 'earth'))
|
|
3
|
+
if (!['mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'].includes(planet) || (!earthAllowed && planet === 'earth'))
|
|
4
4
|
throw new RangeError(`invalid planet: ${planet}`);
|
|
5
5
|
}
|
|
6
6
|
export class AlmanacOutOfRangeError extends RangeError {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwaters/almanac",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Offline astronomy: Sun, Moon and planet positions, apparent horizon, rise/set, phases and eclipses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"ephemeris",
|
|
19
19
|
"lunar-eclipse",
|
|
20
20
|
"solar-eclipse",
|
|
21
|
-
"marine"
|
|
21
|
+
"marine",
|
|
22
|
+
"suncalc"
|
|
22
23
|
],
|
|
23
24
|
"publishConfig": {
|
|
24
25
|
"access": "public"
|