@openwaters/almanac 0.10.0 → 0.11.0
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 +4 -2
- package/dist/earthApsides.d.ts +14 -0
- package/dist/earthApsides.js +76 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/sun.d.ts +1 -0
- package/dist/sun.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ npm install @openwaters/almanac
|
|
|
111
111
|
```ts
|
|
112
112
|
import {
|
|
113
113
|
nextLunarEclipse, lunarEclipseVisibility, nextSolarEclipse, solarObscuration, nextGlobalSolarEclipse, sunEvents, starAltAz,
|
|
114
|
-
seasons, moonApsides, equationOfTime
|
|
114
|
+
seasons, moonApsides, earthApsides, equationOfTime
|
|
115
115
|
} from '@openwaters/almanac';
|
|
116
116
|
|
|
117
117
|
const observer = { latitudeDeg: 48.5, longitudeDeg: -123.0 };
|
|
@@ -141,6 +141,7 @@ const { azDeg, altDeg } = starAltAz(88.792939, 7.407064, today, observer);
|
|
|
141
141
|
const year = new Date(today.getUTCFullYear() + 1, 0, 1);
|
|
142
142
|
for (const { kind, time } of seasons(today, year)) console.log(kind, time.toISOString());
|
|
143
143
|
for (const { kind, time, distanceKm } of moonApsides(today, year)) console.log(kind, time.toISOString(), distanceKm);
|
|
144
|
+
for (const { kind, time, distanceKm } of earthApsides(today, year)) console.log(kind, time.toISOString(), distanceKm);
|
|
144
145
|
console.log(equationOfTime(today)); // sundial minus clock, minutes
|
|
145
146
|
```
|
|
146
147
|
|
|
@@ -160,7 +161,7 @@ const crossings = sunEvents(today, tomorrow, viewer, eyeHeightM);
|
|
|
160
161
|
Requires a Swift 6 or newer toolchain.
|
|
161
162
|
|
|
162
163
|
```swift
|
|
163
|
-
.package(url: "https://github.com/openwatersio/almanac.git", exact: "0.
|
|
164
|
+
.package(url: "https://github.com/openwatersio/almanac.git", exact: "0.11.0")
|
|
164
165
|
```
|
|
165
166
|
|
|
166
167
|
```swift
|
|
@@ -197,6 +198,7 @@ let star = try starAltAz(raDeg: 88.792939, decDeg: 7.407064, at: today, observer
|
|
|
197
198
|
let year = today.addingTimeInterval(366 * 24 * 60 * 60)
|
|
198
199
|
for event in try seasons(from: today, to: year) { print(event.kind, event.time) }
|
|
199
200
|
for apsis in try moonApsides(from: today, to: year) { print(apsis.kind, apsis.time, apsis.distanceKm) }
|
|
201
|
+
for apsis in try earthApsides(from: today, to: year) { print(apsis.kind, apsis.time, apsis.distanceKm) }
|
|
200
202
|
print(try equationOfTime(today)) // sundial minus clock, minutes
|
|
201
203
|
```
|
|
202
204
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Closest approach or farthest remove of the Earth from the Sun's centre. */
|
|
2
|
+
export type EarthApsisKind = 'perihelion' | 'aphelion';
|
|
3
|
+
/** A terrestrial perihelion or aphelion, with the Sun–Earth centre distance there. */
|
|
4
|
+
export interface EarthApsis {
|
|
5
|
+
time: Date;
|
|
6
|
+
kind: EarthApsisKind;
|
|
7
|
+
distanceKm: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The Earth's perihelia and aphelia within the half-open window
|
|
11
|
+
* `[startUtc, endUtc)`, sorted ascending, each with the heliocentric distance
|
|
12
|
+
* at that instant.
|
|
13
|
+
*/
|
|
14
|
+
export declare function earthApsides(startUtc: Date, endUtc: Date): EarthApsis[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// SearchPlanetApsis (Earth), pinned Astronomy Engine 865d3da7d8112bbc7911238052c6af4aaf877181.
|
|
2
|
+
// Central difference in UT, orbital-period/6 brackets; fixed J2000 grid gives window-independent roots.
|
|
3
|
+
import { assertSupported, assertSupportedWindowEnd } from './types.js';
|
|
4
|
+
import { utDays, ttDaysFromUt, dateFromUt } from './time.js';
|
|
5
|
+
import { KM_PER_AU } from './nutation.js';
|
|
6
|
+
import { earthHelioDistance } from './sun.js';
|
|
7
|
+
import { search, quantizedUt, assertReached } from './events.js';
|
|
8
|
+
/** UPSTREAM: the central-difference half-width `dt` inside `SearchPlanetApsis`. */
|
|
9
|
+
const SLOPE_DT_DAYS = 0.001;
|
|
10
|
+
/** UPSTREAM: `increment` — the scan step, here also the grid cell in days since J2000 UT. */
|
|
11
|
+
const APSIS_CELL_DAYS = 365.256 / 6;
|
|
12
|
+
/** UPSTREAM: `positive_slope` — dr/dt in AU per day, by central difference. */
|
|
13
|
+
function distanceSlope(ut) {
|
|
14
|
+
const r1 = earthHelioDistance(ttDaysFromUt(ut - SLOPE_DT_DAYS / 2));
|
|
15
|
+
const r2 = earthHelioDistance(ttDaysFromUt(ut + SLOPE_DT_DAYS / 2));
|
|
16
|
+
return (r2 - r1) / SLOPE_DT_DAYS;
|
|
17
|
+
}
|
|
18
|
+
function negativeDistanceSlope(ut) {
|
|
19
|
+
return -distanceSlope(ut);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* UPSTREAM: the body of `SearchPlanetApsis`'s scan loop for one cell `[t1, t2]`
|
|
23
|
+
* with the slopes `m1`, `m2` at its ends. A slope rising through zero is a
|
|
24
|
+
* perihelion and falling through zero an aphelion. A zero exactly at a grid point
|
|
25
|
+
* belongs to the cell it ends, so no apsis is reported twice.
|
|
26
|
+
*/
|
|
27
|
+
function searchApsisInCell(t1, t2, m1, m2) {
|
|
28
|
+
if (m1 < 0 && m2 >= 0) {
|
|
29
|
+
const ut = search(distanceSlope, t1, t2, 1, undefined, 'perihelion search');
|
|
30
|
+
assertReached(ut !== null, 'perihelion search');
|
|
31
|
+
return { ut: ut, kind: 'perihelion' };
|
|
32
|
+
}
|
|
33
|
+
if (m1 > 0 && m2 <= 0) {
|
|
34
|
+
const ut = search(negativeDistanceSlope, t1, t2, 1, undefined, 'aphelion search');
|
|
35
|
+
assertReached(ut !== null, 'aphelion search');
|
|
36
|
+
return { ut: ut, kind: 'aphelion' };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The Earth's perihelia and aphelia within the half-open window
|
|
42
|
+
* `[startUtc, endUtc)`, sorted ascending, each with the heliocentric distance
|
|
43
|
+
* at that instant.
|
|
44
|
+
*/
|
|
45
|
+
export function earthApsides(startUtc, endUtc) {
|
|
46
|
+
assertSupported(startUtc);
|
|
47
|
+
assertSupportedWindowEnd(endUtc);
|
|
48
|
+
if (startUtc.getTime() >= endUtc.getTime())
|
|
49
|
+
return [];
|
|
50
|
+
const startUt = utDays(startUtc);
|
|
51
|
+
const endUt = utDays(endUtc);
|
|
52
|
+
const events = [];
|
|
53
|
+
let cell = Math.floor(startUt / APSIS_CELL_DAYS);
|
|
54
|
+
let t1 = cell * APSIS_CELL_DAYS;
|
|
55
|
+
let m1 = distanceSlope(t1);
|
|
56
|
+
while (t1 < endUt) {
|
|
57
|
+
const t2 = (cell + 1) * APSIS_CELL_DAYS;
|
|
58
|
+
const m2 = distanceSlope(t2);
|
|
59
|
+
const found = searchApsisInCell(t1, t2, m1, m2);
|
|
60
|
+
if (found !== null) {
|
|
61
|
+
const q = quantizedUt(found.ut); // judge the half-open bounds on the reported instant, not the raw root
|
|
62
|
+
if (q >= endUt)
|
|
63
|
+
break;
|
|
64
|
+
if (q >= startUt) {
|
|
65
|
+
events.push({
|
|
66
|
+
time: dateFromUt(found.ut), kind: found.kind,
|
|
67
|
+
distanceKm: earthHelioDistance(ttDaysFromUt(found.ut)) * KM_PER_AU
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
cell++;
|
|
72
|
+
t1 = t2;
|
|
73
|
+
m1 = m2;
|
|
74
|
+
}
|
|
75
|
+
return events;
|
|
76
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,3 +22,5 @@ export type { SolarEclipse, SolarEclipseKind, SolarEclipseSunAltitudes } from '.
|
|
|
22
22
|
export { nextSolarEclipse, previousSolarEclipse, solarEclipses, solarObscuration } from './solar.js';
|
|
23
23
|
export type { GlobalSolarEclipse, SolarEclipseAxisPoint } from './globalSolar.js';
|
|
24
24
|
export { nextGlobalSolarEclipse, previousGlobalSolarEclipse, globalSolarEclipses, solarEclipseAxisPoint, solarEclipseCentralLine } from './globalSolar.js';
|
|
25
|
+
export type { EarthApsisKind, EarthApsis } from './earthApsides.js';
|
|
26
|
+
export { earthApsides } from './earthApsides.js';
|
package/dist/index.js
CHANGED
|
@@ -12,3 +12,4 @@ export { sunIsUp, sunEvents, moonEvents, searchMoonPhases, planetEvents, sunAlti
|
|
|
12
12
|
export { nextLunarEclipse, previousLunarEclipse, lunarEclipses, lunarEclipseVisibility } from './eclipse.js';
|
|
13
13
|
export { nextSolarEclipse, previousSolarEclipse, solarEclipses, solarObscuration } from './solar.js';
|
|
14
14
|
export { nextGlobalSolarEclipse, previousGlobalSolarEclipse, globalSolarEclipses, solarEclipseAxisPoint, solarEclipseCentralLine } from './globalSolar.js';
|
|
15
|
+
export { earthApsides } from './earthApsides.js';
|
package/dist/sun.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const vsopEarth: VsopModel;
|
|
|
11
11
|
export declare function vsopRotate(eclip: Vec3): Vec3;
|
|
12
12
|
/** UPSTREAM: `VsopSphereToRect`, astronomy.ts lines 3243-3253. */
|
|
13
13
|
export declare function vsopSphereToRect(lon: number, lat: number, radius: number): Vec3;
|
|
14
|
+
export declare function earthHelioDistance(tt: number): number;
|
|
14
15
|
/**
|
|
15
16
|
* UPSTREAM: `CalcVsop`, astronomy.ts lines 3255-3262, specialised to
|
|
16
17
|
* `vsop.Earth` — i.e. `HelioVector(Body.Earth, time)`. J2000 mean equator (EQJ).
|
package/dist/sun.js
CHANGED
|
@@ -223,6 +223,8 @@ export function vsopSphereToRect(lon, lat, radius) {
|
|
|
223
223
|
z: radius * Math.sin(lat)
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
|
+
// UPSTREAM: HelioDistance(Body.Earth), the scalar VSOP radius without rotations.
|
|
227
|
+
export function earthHelioDistance(tt) { return earthRad(tt / 365250); }
|
|
226
228
|
/**
|
|
227
229
|
* UPSTREAM: `CalcVsop`, astronomy.ts lines 3255-3262, specialised to
|
|
228
230
|
* `vsop.Earth` — i.e. `HelioVector(Body.Earth, time)`. J2000 mean equator (EQJ).
|