@openwaters/almanac 0.9.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 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
 
@@ -157,8 +158,10 @@ const crossings = sunEvents(today, tomorrow, viewer, eyeHeightM);
157
158
 
158
159
  ### Swift
159
160
 
161
+ Requires a Swift 6 or newer toolchain.
162
+
160
163
  ```swift
161
- .package(url: "https://github.com/openwatersio/almanac.git", exact: "0.8.0")
164
+ .package(url: "https://github.com/openwatersio/almanac.git", exact: "0.11.0")
162
165
  ```
163
166
 
164
167
  ```swift
@@ -195,6 +198,7 @@ let star = try starAltAz(raDeg: 88.792939, decDeg: 7.407064, at: today, observer
195
198
  let year = today.addingTimeInterval(366 * 24 * 60 * 60)
196
199
  for event in try seasons(from: today, to: year) { print(event.kind, event.time) }
197
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) }
198
202
  print(try equationOfTime(today)) // sundial minus clock, minutes
199
203
  ```
200
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).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwaters/almanac",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Offline astronomy: Sun, Moon and planet positions, apparent horizon, rise/set, phases and eclipses.",
5
5
  "license": "MIT",
6
6
  "repository": {