-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy pathhorizon.js
148 lines (120 loc) · 4.67 KB
/
horizon.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
horizon.js - Don Cross - 2019-12-14
Example Node.js program for Astronomy Engine:
https://github.com/cosinekitty/astronomy
This is a more advanced example. It shows how to use coordinate
transforms and a binary search to find the two azimuths where the
ecliptic intersects with an observer's horizon at a given date and time.
node horizon.js latitude longitude [date]
*/
'use strict';
const Astronomy = require('./astronomy.js');
const NUM_SAMPLES = 4;
function ECLIPLON(i) {
return (360 * i) / NUM_SAMPLES;
}
function HorizontalCoords(ecliptic_longitude, time, rot_ecl_hor) {
const eclip = new Astronomy.Spherical(
0.0, /* being "on the ecliptic plane" means ecliptic latitude is zero. */
ecliptic_longitude,
1.0); /* any positive distance value will work fine. */
/* Convert ecliptic angular coordinates to ecliptic vector. */
const ecl_vec = Astronomy.VectorFromSphere(eclip, time);
/* Use the rotation matrix to convert ecliptic vector to horizontal vector. */
const hor_vec = Astronomy.RotateVector(rot_ecl_hor, ecl_vec);
/* Find horizontal angular coordinates, correcting for atmospheric refraction. */
return Astronomy.HorizonFromVector(hor_vec, 'normal');
}
function Search(time, rot_ecl_hor, e1, e2) {
const tolerance = 1.0e-6; /* one-millionth of a degree is close enough! */
/*
Binary search: find the ecliptic longitude such that the horizontal altitude
ascends through a zero value. The caller must pass e1, e2 such that the altitudes
bound zero in ascending order.
*/
for(;;) {
const e3 = (e1 + e2) / 2.0;
const h3 = HorizontalCoords(e3, time, rot_ecl_hor);
if (Math.abs(e2-e1) < tolerance) {
/* We have found the horizon crossing within tolerable limits. */
return { ex:e3, h:h3 };
}
if (h3.lat < 0.0)
e1 = e3;
else
e2 = e3;
}
}
function FindEclipticCrossings(observer, time) {
/*
The ecliptic is a celestial circle that describes the mean plane of
the Earth's orbit around the Sun. We use J2000 ecliptic coordinates,
meaning the x-axis is defined to where the plane of the Earth's
equator on January 1, 2000 at noon UTC intersects the ecliptic plane.
The positive x-axis points toward the March equinox.
Calculate a rotation matrix that converts J2000 ecliptic vectors
to horizontal vectors for this observer and time.
*/
const rot = Astronomy.Rotation_ECL_HOR(time, observer);
/*
Sample several points around the ecliptic.
Remember the horizontal coordinates for each sample.
*/
const hor = [];
let i;
for (i=0; i < NUM_SAMPLES; ++i) {
hor.push(HorizontalCoords(ECLIPLON(i), time, rot));
}
for (i=0; i < NUM_SAMPLES; ++i) {
const a1 = hor[i].lat;
const a2 = hor[(i+1) % NUM_SAMPLES].lat;
const e1 = ECLIPLON(i);
const e2 = ECLIPLON(i+1);
if (a1 * a2 <= 0) {
let s, direction;
if (a2 > a1)
s = Search(time, rot, e1, e2);
else
s = Search(time, rot, e2, e1);
if (s.h.lon > 0 && s.h.lon < 180)
direction = 'ascends';
else
direction = 'descends';
console.log(`Ecliptic longitude ${s.ex.toFixed(4)} ${direction} through horizon at azimuth ${s.h.lon.toFixed(4)}`);
if (Math.abs(s.h.lat) > 5.0e-7) {
console.error(`FindEclipticCrossing: excessive altitude = ${s.h.lat}`);
process.exit(1);
}
}
}
}
function ParseNumber(text, name) {
const x = Number(text);
if (!Number.isFinite(x)) {
console.error(`ERROR: Not a valid numeric value for ${name}: "${text}"`);
process.exit(1);
}
return x;
}
function ParseDate(text) {
const d = new Date(text);
if (!Number.isFinite(d.getTime())) {
console.error(`ERROR: Not a valid date: "${text}"`);
process.exit(1);
}
return d;
}
function Demo() {
if (process.argv.length === 4 || process.argv.length === 5) {
const latitude = ParseNumber(process.argv[2]);
const longitude = ParseNumber(process.argv[3]);
const observer = new Astronomy.Observer(latitude, longitude, 0);
const time = Astronomy.MakeTime((process.argv.length === 5) ? ParseDate(process.argv[4]) : new Date());
FindEclipticCrossings(observer, time);
process.exit(0);
} else {
console.log('USAGE: node horizon.js latitude longitude [date]');
process.exit(1);
}
}
Demo();