@openwaters/almanac 0.2.0 → 0.3.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 +53 -16
- package/dist/eclipse.js +9 -6
- package/dist/events.js +37 -9
- package/dist/illumination.js +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/moon.js +7 -29
- package/dist/transforms.d.ts +12 -0
- package/dist/transforms.js +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Almanac
|
|
2
2
|
|
|
3
3
|
Offline sun & moon engine for the Salish Sea and everywhere else — positions,
|
|
4
|
-
rise/set/twilight, moon phase,
|
|
5
|
-
|
|
4
|
+
rise/set/twilight, moon phase, lunar eclipses (solar planned), and where any fixed
|
|
5
|
+
star stands from a catalog position, computed from pure geometry with zero network
|
|
6
|
+
and zero runtime data files.
|
|
6
7
|
|
|
7
8
|
Twin implementations, one behavior:
|
|
8
9
|
|
|
@@ -22,6 +23,48 @@ future — see the design spec's Conventions section for what that means and why
|
|
|
22
23
|
Algorithms translated from [Astronomy Engine](https://github.com/cosinekitty/astronomy)
|
|
23
24
|
(MIT, Don Cross) — see [NOTICE](NOTICE). MIT licensed.
|
|
24
25
|
|
|
26
|
+
## Performance
|
|
27
|
+
|
|
28
|
+
Almanac includes a shared performance harness for both ports: **15 workloads**
|
|
29
|
+
cover positions, a 228-hour sky track, short/year/polar event windows, full-range
|
|
30
|
+
moon phases, and next/previous/range eclipse searches, including empty windows.
|
|
31
|
+
|
|
32
|
+
The v0.2.1 optimizations reuse Moon and shadow geometry, flatten scratch arrays,
|
|
33
|
+
and refine altitude crossings with fewer position evaluations. In the
|
|
34
|
+
[hosted CI comparison](https://github.com/openwatersio/almanac/actions/runs/34119038860),
|
|
35
|
+
median query time fell relative to the v0.2.0 astronomy code:
|
|
36
|
+
|
|
37
|
+
| Workload | TypeScript: less time | Swift: less time |
|
|
38
|
+
| --- | ---: | ---: |
|
|
39
|
+
| Moon positions / 1,024 hours | 29.8% | 22.1% |
|
|
40
|
+
| Sky track / 228 hours | 40.0% | 42.4% |
|
|
41
|
+
| Sun events / 228 hours | 39.3% | 40.2% |
|
|
42
|
+
| Moon events / 228 hours | 39.1% | 35.1% |
|
|
43
|
+
| Previous lunar eclipse | 26.0% | 36.6% |
|
|
44
|
+
| Lunar eclipses / 1950–2100 | 26.4% | 23.4% |
|
|
45
|
+
|
|
46
|
+
TypeScript was measured on Ubuntu with Node 22; Swift used release builds on
|
|
47
|
+
macOS 15. Each comparison builds both revisions with the same harness and
|
|
48
|
+
toolchain, then takes seven interleaved process pairs with 300 ms warmup per
|
|
49
|
+
process. Build and startup time are excluded. Timings vary by machine; the shared
|
|
50
|
+
correctness fixtures and parity tolerances remain the accuracy gates.
|
|
51
|
+
|
|
52
|
+
Reproduce the comparison locally from the repository root (Node 22+ and Swift
|
|
53
|
+
5.9+):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm ci --prefix typescript
|
|
57
|
+
node benchmarks/run.mjs --base v0.2.0
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
CI runs the harness on code changes and fails on **median regressions over 20%**.
|
|
61
|
+
Results include timing tables, raw samples, checksums, and revision/toolchain
|
|
62
|
+
metadata. See [the harness guide](CONTRIBUTING.md#performance) for choosing a
|
|
63
|
+
baseline, running one port, and inspecting reports.
|
|
64
|
+
|
|
65
|
+
Event searches scale with window length. Run a full 151-year sweep in a worker or
|
|
66
|
+
background task; use shorter windows for interactive queries.
|
|
67
|
+
|
|
25
68
|
## Usage
|
|
26
69
|
|
|
27
70
|
### TypeScript
|
|
@@ -31,7 +74,7 @@ npm install @openwaters/almanac
|
|
|
31
74
|
```
|
|
32
75
|
|
|
33
76
|
```ts
|
|
34
|
-
import { nextLunarEclipse, lunarEclipseVisibility, sunEvents } from '@openwaters/almanac';
|
|
77
|
+
import { nextLunarEclipse, lunarEclipseVisibility, sunEvents, starAltAz } from '@openwaters/almanac';
|
|
35
78
|
|
|
36
79
|
const observer = { latitudeDeg: 48.5, longitudeDeg: -123.0 };
|
|
37
80
|
|
|
@@ -44,12 +87,15 @@ const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
|
|
44
87
|
for (const { kind, time } of sunEvents(today, tomorrow, observer)) {
|
|
45
88
|
console.log(kind, time.toISOString());
|
|
46
89
|
}
|
|
90
|
+
|
|
91
|
+
// Betelgeuse from its J2000 catalog position: az/alt in degrees, refracted.
|
|
92
|
+
const { azDeg, altDeg } = starAltAz(88.792939, 7.407064, today, observer);
|
|
47
93
|
```
|
|
48
94
|
|
|
49
95
|
### Swift
|
|
50
96
|
|
|
51
97
|
```swift
|
|
52
|
-
.package(url: "https://github.com/openwatersio/almanac.git", exact: "0.
|
|
98
|
+
.package(url: "https://github.com/openwatersio/almanac.git", exact: "0.3.0")
|
|
53
99
|
```
|
|
54
100
|
|
|
55
101
|
```swift
|
|
@@ -67,6 +113,9 @@ let tomorrow = today.addingTimeInterval(24 * 60 * 60)
|
|
|
67
113
|
for event in try sunEvents(from: today, to: tomorrow, observer: observer) {
|
|
68
114
|
print(event.kind, event.time)
|
|
69
115
|
}
|
|
116
|
+
|
|
117
|
+
// Betelgeuse from its J2000 catalog position: az/alt in degrees, refracted.
|
|
118
|
+
let star = try starAltAz(raDeg: 88.792939, decDeg: 7.407064, at: today, observer: observer)
|
|
70
119
|
```
|
|
71
120
|
|
|
72
121
|
### Eclipse searches
|
|
@@ -87,15 +136,3 @@ let eclipses = try lunarEclipses(from: today, to: tomorrow)
|
|
|
87
136
|
Ranges include peaks at the start and exclude peaks at the end. Contacts may
|
|
88
137
|
extend outside the range. Previous/next searches skip peaks within 100 ms of the
|
|
89
138
|
anchor. Search results are global; apply `lunarEclipseVisibility` for an observer.
|
|
90
|
-
|
|
91
|
-
### Performance
|
|
92
|
-
|
|
93
|
-
Event searches (`sunEvents`/`moonEvents`) are linear in window length. The full
|
|
94
|
-
151-year supported interval is a measured cost, not a guess: ~68 s in TypeScript,
|
|
95
|
-
~44 s in Swift release builds. Chunk or worker a full-range call rather than
|
|
96
|
-
running it inline.
|
|
97
|
-
|
|
98
|
-
Run `node benchmarks/run.mjs --base origin/main` from the repository root to
|
|
99
|
-
compare both ports against a baseline. CI reports timings and fails on median
|
|
100
|
-
regressions over 20%; see [the performance harness](CONTRIBUTING.md#performance)
|
|
101
|
-
for workloads, results and local options.
|
package/dist/eclipse.js
CHANGED
|
@@ -108,10 +108,13 @@ function peakEarthShadow(centerUt) {
|
|
|
108
108
|
* then averages the two halves, so the contacts this package reports are
|
|
109
109
|
* symmetric about the peak by construction (as upstream's are).
|
|
110
110
|
*/
|
|
111
|
-
function shadowSemiDurationMinutes(
|
|
111
|
+
function shadowSemiDurationMinutes(shadow, radiusLimitKm, windowMinutes) {
|
|
112
|
+
const centerUt = shadow.ut;
|
|
113
|
+
// Both contact searches sample the peak, whose geometry is already known.
|
|
114
|
+
const distance = (ut) => ut === centerUt ? shadow.r : earthShadow(ut).r;
|
|
112
115
|
const window = windowMinutes / MINUTES_PER_DAY;
|
|
113
|
-
const t1 = search(ut => -(
|
|
114
|
-
const t2 = search(ut => +(
|
|
116
|
+
const t1 = search(ut => -(distance(ut) - radiusLimitKm), centerUt - window, centerUt, SHADOW_TOL_SECONDS, SHADOW_ITER_CAP, 'shadow semiduration (before)');
|
|
117
|
+
const t2 = search(ut => +(distance(ut) - radiusLimitKm), centerUt, centerUt + window, SHADOW_TOL_SECONDS, SHADOW_ITER_CAP, 'shadow semiduration (after)');
|
|
115
118
|
if (t1 === null || t2 === null)
|
|
116
119
|
throw new Error('almanac internal: failed to find shadow semiduration');
|
|
117
120
|
return (t2 - t1) * (MINUTES_PER_DAY / 2.0); // convert days to minutes and average the semi-durations
|
|
@@ -209,15 +212,15 @@ function buildEclipse(shadow, peak) {
|
|
|
209
212
|
let kind = 'penumbral';
|
|
210
213
|
let sdTotal = 0.0;
|
|
211
214
|
let sdPartial = 0.0;
|
|
212
|
-
const sdPenum = shadowSemiDurationMinutes(shadow
|
|
215
|
+
const sdPenum = shadowSemiDurationMinutes(shadow, shadow.p + MOON_MEAN_RADIUS_KM, PENUMBRAL_WINDOW_MINUTES);
|
|
213
216
|
if (shadow.r < shadow.k + MOON_MEAN_RADIUS_KM) {
|
|
214
217
|
// This is at least a partial eclipse.
|
|
215
218
|
kind = 'partial';
|
|
216
|
-
sdPartial = shadowSemiDurationMinutes(shadow
|
|
219
|
+
sdPartial = shadowSemiDurationMinutes(shadow, shadow.k + MOON_MEAN_RADIUS_KM, sdPenum);
|
|
217
220
|
if (shadow.r + MOON_MEAN_RADIUS_KM < shadow.k) {
|
|
218
221
|
// This is a total eclipse.
|
|
219
222
|
kind = 'total';
|
|
220
|
-
sdTotal = shadowSemiDurationMinutes(shadow
|
|
223
|
+
sdTotal = shadowSemiDurationMinutes(shadow, shadow.k - MOON_MEAN_RADIUS_KM, sdPartial);
|
|
221
224
|
}
|
|
222
225
|
}
|
|
223
226
|
// Fraction of the Moon's diameter immersed in each shadow. Shadow radii,
|
package/dist/events.js
CHANGED
|
@@ -215,13 +215,25 @@ function refineExtremum(sample, centreUt, wantMax, halfWidthDays, afterUt) {
|
|
|
215
215
|
return score(s1) > score(s2) ? s1 : s2;
|
|
216
216
|
}
|
|
217
217
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
218
|
+
* Quadratically accelerated search for the single crossing of `level` on a
|
|
219
|
+
* monotonic segment, with bisection if the estimate cannot be verified.
|
|
220
220
|
*/
|
|
221
221
|
function bisectCrossing(sample, level, s0, s1) {
|
|
222
222
|
let a = s0.ut;
|
|
223
223
|
let b = s1.ut;
|
|
224
|
-
|
|
224
|
+
const belowAtA = offsetDeg(s0, level) < 0;
|
|
225
|
+
// Reuse the phase search's quadratic acceleration, normalized to an
|
|
226
|
+
// ascending crossing. Endpoint geometry has already been evaluated.
|
|
227
|
+
const f = (ut) => (belowAtA ? 1 : -1) * offsetDeg(ut === s0.ut ? s0 : ut === s1.ut ? s1 : sample(ut), level);
|
|
228
|
+
const root = search(f, a, b, 0.25, BISECTION_CAP, 'altitude crossing');
|
|
229
|
+
if (root !== null) {
|
|
230
|
+
// A derivative-based estimate alone does not establish a time bound.
|
|
231
|
+
// Accept only a verified half-second bracket, including near grazes.
|
|
232
|
+
const left = Math.max(a, root - TIME_TOL_DAYS / 4);
|
|
233
|
+
const right = Math.min(b, root + TIME_TOL_DAYS / 4);
|
|
234
|
+
if (f(left) < 0 && f(right) >= 0)
|
|
235
|
+
return (left + right) / 2;
|
|
236
|
+
}
|
|
225
237
|
for (let i = 0; i < BISECTION_CAP; i++) {
|
|
226
238
|
if (b - a < TIME_TOL_DAYS)
|
|
227
239
|
return (a + b) / 2;
|
|
@@ -266,12 +278,28 @@ function searchAltitudeEvents(sample, levels, transitKind, startUt, endUt, haRat
|
|
|
266
278
|
const s1 = atRangeEnd ? sample(MAX_UT)
|
|
267
279
|
: refineExtremum(sample, nextEst, nextIsMax, extremumHalfWidthDays, s0.ut);
|
|
268
280
|
assertReached(s1.ut > s0.ut, 'extremum chain ordering');
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
281
|
+
// Earlier extrema still establish the chain, but their crossings cannot be emitted.
|
|
282
|
+
if (quantizedUt(s1.ut) >= startUt) {
|
|
283
|
+
let crossingSample = sample;
|
|
284
|
+
if (levels.length > 1) {
|
|
285
|
+
// Twilight levels share midpoint probes; retain them only for this segment.
|
|
286
|
+
const samples = new Map();
|
|
287
|
+
crossingSample = (ut) => {
|
|
288
|
+
const previous = samples.get(ut);
|
|
289
|
+
if (previous)
|
|
290
|
+
return previous;
|
|
291
|
+
const current = sample(ut);
|
|
292
|
+
samples.set(ut, current);
|
|
293
|
+
return current;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
for (const spec of levels) {
|
|
297
|
+
const g0 = crossingSign(s0, spec.level);
|
|
298
|
+
const g1 = crossingSign(s1, spec.level);
|
|
299
|
+
if (g0 * g1 >= 0)
|
|
300
|
+
continue; // no crossing, or a graze that only touches
|
|
301
|
+
emit(bisectCrossing(crossingSample, spec.level, s0, s1), g1 > 0 ? spec.rising : spec.falling);
|
|
302
|
+
}
|
|
275
303
|
}
|
|
276
304
|
if (nextIsMax && transitKind !== null && !atRangeEnd)
|
|
277
305
|
emit(nextEst, transitKind);
|
package/dist/illumination.js
CHANGED
|
@@ -63,7 +63,10 @@ function normalizeLongitude(lon) {
|
|
|
63
63
|
* that far off the USNO catalogue.
|
|
64
64
|
*/
|
|
65
65
|
export function moonPhaseDeg(tt) {
|
|
66
|
-
return
|
|
66
|
+
return moonPhaseFromVector(tt, moonGeoVectorEqj(tt));
|
|
67
|
+
}
|
|
68
|
+
function moonPhaseFromVector(tt, moon) {
|
|
69
|
+
return normalizeLongitude(eclipticLonOfDateDeg(moon, tt) - eclipticLonOfDateDeg(sunGeoVectorEqj(tt), tt));
|
|
67
70
|
}
|
|
68
71
|
/** INTERNAL: moon illumination for a TT instant. See {@link moonIllumination}. */
|
|
69
72
|
export function moonIlluminationAtTT(tt) {
|
|
@@ -74,7 +77,7 @@ export function moonIlluminationAtTT(tt) {
|
|
|
74
77
|
const hc = { x: earth.x + gc.x, y: earth.y + gc.y, z: earth.z + gc.z };
|
|
75
78
|
const phaseAngleDeg = angleBetweenDeg(gc, hc);
|
|
76
79
|
const fraction = (1 + Math.cos(DEG2RAD * phaseAngleDeg)) / 2;
|
|
77
|
-
const phase =
|
|
80
|
+
const phase = moonPhaseFromVector(tt, gc) / 360;
|
|
78
81
|
return { fraction, phaseAngleDeg, phase, waxing: phase < 0.5 };
|
|
79
82
|
}
|
|
80
83
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { AlmanacOutOfRangeError } from './types.js';
|
|
|
3
3
|
export type { SunPosition, MoonPosition } from './positions.js';
|
|
4
4
|
export { sunPosition, moonPosition } from './positions.js';
|
|
5
5
|
export type { AltAz } from './transforms.js';
|
|
6
|
-
export { sunAltAz, moonAltAz } from './transforms.js';
|
|
6
|
+
export { sunAltAz, moonAltAz, starAltAz } from './transforms.js';
|
|
7
7
|
export type { MoonIllumination } from './illumination.js';
|
|
8
8
|
export { moonIllumination } from './illumination.js';
|
|
9
9
|
export type { SunEvent, SunEventKind, MoonEvent, MoonEventKind, MoonPhaseEvent, MoonPhaseName } from './events.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AlmanacOutOfRangeError } from './types.js';
|
|
2
2
|
export { sunPosition, moonPosition } from './positions.js';
|
|
3
|
-
export { sunAltAz, moonAltAz } from './transforms.js';
|
|
3
|
+
export { sunAltAz, moonAltAz, starAltAz } from './transforms.js';
|
|
4
4
|
export { moonIllumination } from './illumination.js';
|
|
5
5
|
export { sunEvents, moonEvents, searchMoonPhases } from './events.js';
|
|
6
6
|
export { nextLunarEclipse, previousLunarEclipse, lunarEclipses, lunarEclipseVisibility } from './eclipse.js';
|
package/dist/moon.js
CHANGED
|
@@ -14,43 +14,21 @@ import { ARC, EARTH_EQUATORIAL_RADIUS_AU, PI2, PrecessDirection, eclipticToEquat
|
|
|
14
14
|
*/
|
|
15
15
|
export function calcMoon(tt) {
|
|
16
16
|
const T = tt / 36525;
|
|
17
|
-
function DeclareArray1(xmin, xmax) {
|
|
18
|
-
const array = [];
|
|
19
|
-
let i;
|
|
20
|
-
for (i = 0; i <= xmax - xmin; ++i) {
|
|
21
|
-
array.push(0);
|
|
22
|
-
}
|
|
23
|
-
return { min: xmin, array: array };
|
|
24
|
-
}
|
|
25
|
-
function DeclareArray2(xmin, xmax, ymin, ymax) {
|
|
26
|
-
const array = [];
|
|
27
|
-
for (let i = 0; i <= xmax - xmin; ++i) {
|
|
28
|
-
array.push(DeclareArray1(ymin, ymax));
|
|
29
|
-
}
|
|
30
|
-
return { min: xmin, array: array };
|
|
31
|
-
}
|
|
32
|
-
function ArrayGet2(a, x, y) {
|
|
33
|
-
const m = a.array[x - a.min];
|
|
34
|
-
return m.array[y - m.min];
|
|
35
|
-
}
|
|
36
|
-
function ArraySet2(a, x, y, v) {
|
|
37
|
-
const m = a.array[x - a.min];
|
|
38
|
-
m.array[y - m.min] = v;
|
|
39
|
-
}
|
|
40
17
|
let S, MAX, ARG, FAC, I, J, T2, DGAM, DLAM, N, GAM1C, SINPI, L0, L, LS, F, D, DL0, DL, DLS, DF, DD, DS;
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
// CO/SI tables: x in -6...6, y in 1...4, stored in flat 13x4 arrays.
|
|
19
|
+
const coArray = new Array(52).fill(0);
|
|
20
|
+
const siArray = new Array(52).fill(0);
|
|
43
21
|
function CO(x, y) {
|
|
44
|
-
return
|
|
22
|
+
return coArray[(x + 6) * 4 + y - 1];
|
|
45
23
|
}
|
|
46
24
|
function SI(x, y) {
|
|
47
|
-
return
|
|
25
|
+
return siArray[(x + 6) * 4 + y - 1];
|
|
48
26
|
}
|
|
49
27
|
function SetCO(x, y, v) {
|
|
50
|
-
|
|
28
|
+
coArray[(x + 6) * 4 + y - 1] = v;
|
|
51
29
|
}
|
|
52
30
|
function SetSI(x, y, v) {
|
|
53
|
-
|
|
31
|
+
siArray[(x + 6) * 4 + y - 1] = v;
|
|
54
32
|
}
|
|
55
33
|
function AddThe(c1, s1, c2, s2, func) {
|
|
56
34
|
func(c1 * c2 - s1 * s2, s1 * c2 + c1 * s2);
|
package/dist/transforms.d.ts
CHANGED
|
@@ -59,3 +59,15 @@ export declare function sunAltAz(time: Date, observer: Observer): AltAz;
|
|
|
59
59
|
* refraction (upstream `'normal'` model) both applied.
|
|
60
60
|
*/
|
|
61
61
|
export declare function moonAltAz(time: Date, observer: Observer): AltAz;
|
|
62
|
+
/**
|
|
63
|
+
* Topocentric azimuth/altitude of a fixed star from its J2000 (ICRS)
|
|
64
|
+
* catalog right ascension and declination, both in degrees.
|
|
65
|
+
*
|
|
66
|
+
* Applied: precession and nutation to the equator of date, Greenwich
|
|
67
|
+
* apparent sidereal time, and atmospheric refraction (upstream `'normal'`
|
|
68
|
+
* model, the same as sunAltAz/moonAltAz). Not applied: annual aberration
|
|
69
|
+
* (at most 20.5″ — see the roadmap), proper motion (pass catalog
|
|
70
|
+
* coordinates; fast movers such as Arcturus drift ~1′ in 26 years), and
|
|
71
|
+
* stellar parallax (negligible at the 1000 ly the star is placed at).
|
|
72
|
+
*/
|
|
73
|
+
export declare function starAltAz(raDeg: number, decDeg: number, time: Date, observer: Observer): AltAz;
|
package/dist/transforms.js
CHANGED
|
@@ -196,3 +196,41 @@ export function moonAltAz(time, observer) {
|
|
|
196
196
|
const ut = utDays(time);
|
|
197
197
|
return refract(topoAltAzUnrefracted(moonGeoVectorEqj(ttDaysFromUt(ut)), ut, observer));
|
|
198
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* UPSTREAM: the user-defined star path — `HelioVector` for a `DefineStar`
|
|
201
|
+
* body (astronomy.ts ~4097: `VectorFromSphere(Spherical(dec, 15*ra, dist))`)
|
|
202
|
+
* into `Equator(body, date, observer, ofdate=true, aberration=false)` (~2803)
|
|
203
|
+
* and `Horizon` (~2565): the same topocentric path sunAltAz/moonAltAz take.
|
|
204
|
+
* Upstream floors a star's distance at 1 light-year and suggests 1000 when
|
|
205
|
+
* unknown; the star sits at 1000 ly here (`AU_PER_LY`, astronomy.ts line 52).
|
|
206
|
+
* Upstream's geocentric step (star vector minus Earth's heliocentric
|
|
207
|
+
* position) is skipped: at 1000 ly it moves the star 0.003″.
|
|
208
|
+
*/
|
|
209
|
+
const STAR_DISTANCE_AU = 1000 * 63241.07708807546;
|
|
210
|
+
/**
|
|
211
|
+
* Topocentric azimuth/altitude of a fixed star from its J2000 (ICRS)
|
|
212
|
+
* catalog right ascension and declination, both in degrees.
|
|
213
|
+
*
|
|
214
|
+
* Applied: precession and nutation to the equator of date, Greenwich
|
|
215
|
+
* apparent sidereal time, and atmospheric refraction (upstream `'normal'`
|
|
216
|
+
* model, the same as sunAltAz/moonAltAz). Not applied: annual aberration
|
|
217
|
+
* (at most 20.5″ — see the roadmap), proper motion (pass catalog
|
|
218
|
+
* coordinates; fast movers such as Arcturus drift ~1′ in 26 years), and
|
|
219
|
+
* stellar parallax (negligible at the 1000 ly the star is placed at).
|
|
220
|
+
*/
|
|
221
|
+
export function starAltAz(raDeg, decDeg, time, observer) {
|
|
222
|
+
assertSupported(time);
|
|
223
|
+
assertObserver(observer);
|
|
224
|
+
if (!(raDeg >= 0 && raDeg < 360))
|
|
225
|
+
throw new RangeError(`raDeg out of range: ${raDeg}`);
|
|
226
|
+
if (!(decDeg >= -90 && decDeg <= 90))
|
|
227
|
+
throw new RangeError(`decDeg out of range: ${decDeg}`);
|
|
228
|
+
const ut = utDays(time);
|
|
229
|
+
const rCosDec = STAR_DISTANCE_AU * Math.cos(decDeg * DEG2RAD);
|
|
230
|
+
const eqj = {
|
|
231
|
+
x: rCosDec * Math.cos(raDeg * DEG2RAD),
|
|
232
|
+
y: rCosDec * Math.sin(raDeg * DEG2RAD),
|
|
233
|
+
z: STAR_DISTANCE_AU * Math.sin(decDeg * DEG2RAD),
|
|
234
|
+
};
|
|
235
|
+
return refract(topoAltAzUnrefracted(eqj, ut, observer));
|
|
236
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwaters/almanac",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Offline sun & moon engine: positions, rise/set, moon phase, lunar eclipses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": { "type": "git", "url": "git+https://github.com/openwatersio/almanac.git", "directory": "typescript" },
|