UNPKG

8.22 kBJavaScriptView Raw
1/**
2 * @copyright 2013 Sonia Keys
3 * @copyright 2016 commenthol
4 * @license MIT
5 * @module angle
6 */
7/**
8 * Angle: Chapter 17: Angular Separation.
9 *
10 * Functions in this package are useful for Ecliptic, Equatorial, or any
11 * similar coordinate frame. To avoid suggestion of a particular frame,
12 * function parameters are specified simply as "r1, d1" to correspond to a
13 * right ascenscion, declination pair or to a longitude, latitude pair.
14 *
15 * In function Sep, Meeus recommends 10 arc min as a threshold. This
16 * value is in package base as base.SmallAngle because it has general utility.
17 *
18 * All angles are in radians.
19 */
20
21import base from './base';
22import interp from './interpolation';
23var abs = Math.abs,
24 acos = Math.acos,
25 asin = Math.asin,
26 atan2 = Math.atan2,
27 cos = Math.cos,
28 hypot = Math.hypot,
29 sin = Math.sin,
30 sqrt = Math.sqrt,
31 tan = Math.tan;
32
33/**
34 * `sep` returns the angular separation between two celestial bodies.
35 *
36 * The algorithm is numerically naïve, and while patched up a bit for
37 * small separations, remains unstable for separations near π.
38 *
39 * @param {base.Coord} c1 - coordinate of celestial body 1
40 * @param {base.Coord} c2 - coordinate of celestial body 2
41 * @return {Number} angular separation between two celestial bodies
42 */
43
44export function sep(c1, c2) {
45 var _base$sincos = base.sincos(c1.dec),
46 sind1 = _base$sincos[0],
47 cosd1 = _base$sincos[1];
48
49 var _base$sincos2 = base.sincos(c2.dec),
50 sind2 = _base$sincos2[0],
51 cosd2 = _base$sincos2[1];
52
53 var cd = sind1 * sind2 + cosd1 * cosd2 * cos(c1.ra - c2.ra); // (17.1) p. 109
54 if (cd < base.CosSmallAngle) {
55 return acos(cd);
56 }
57 return hypot((c2.ra - c1.ra) * cosd1, c2.dec - c1.dec); // (17.2) p. 109
58}
59
60/**
61 * `minSep` returns the minimum separation between two moving objects.
62 *
63 * The motion is represented as an ephemeris of three rows, equally spaced
64 * in time. Jd1, jd3 are julian day times of the first and last rows.
65 * R1, d1, r2, d2 are coordinates at the three times. They must each be
66 * slices of length 3.0
67 *
68 * Result is obtained by computing separation at each of the three times
69 * and interpolating a minimum. This may be invalid for sufficiently close
70 * approaches.
71 *
72 * @throws Error
73 * @param {Number} jd1 - Julian day - time at cs1[0], cs2[0]
74 * @param {Number} jd3 - Julian day - time at cs1[2], cs2[2]
75 * @param {base.Coord[]} cs1 - 3 coordinates of moving object 1
76 * @param {base.Coord[]} cs2 - 3 coordinates of moving object 2
77 * @param {function} [fnSep] - alternative `sep` function e.g. `angle.sepPauwels`, `angle.sepHav`
78 * @return {Number} angular separation between two celestial bodies
79 */
80export function minSep(jd1, jd3, cs1, cs2, fnSep) {
81 fnSep = fnSep || sep;
82 if (cs1.length !== 3 || cs2.length !== 3) {
83 throw interp.errorNot3;
84 }
85 var y = new Array(3);
86 cs1.forEach(function (c, x) {
87 y[x] = sep(cs1[x], cs2[x]);
88 });
89 var d3 = new interp.Len3(jd1, jd3, y);
90 var dMin = d3.extremum()[1];
91 return dMin;
92}
93
94/**
95 * `minSepRect` returns the minimum separation between two moving objects.
96 *
97 * Like `minSep`, but using a method of rectangular coordinates that gives
98 * accurate results even for close approaches.
99 *
100 * @throws Error
101 * @param {Number} jd1 - Julian day - time at cs1[0], cs2[0]
102 * @param {Number} jd3 - Julian day - time at cs1[2], cs2[2]
103 * @param {base.Coord[]} cs1 - 3 coordinates of moving object 1
104 * @param {base.Coord[]} cs2 - 3 coordinates of moving object 2
105 * @return {Number} angular separation between two celestial bodies
106 */
107export function minSepRect(jd1, jd3, cs1, cs2) {
108 if (cs1.length !== 3 || cs2.length !== 3) {
109 throw interp.ErrorNot3;
110 }
111 var uv = function uv(c1, c2) {
112 var _base$sincos3 = base.sincos(c1.dec),
113 sind1 = _base$sincos3[0],
114 cosd1 = _base$sincos3[1];
115
116 var Δr = c2.ra - c1.ra;
117 var tanΔr = tan(Δr);
118 var tanhΔr = tan(Δr / 2);
119 var K = 1 / (1 + sind1 * sind1 * tanΔr * tanhΔr);
120 var sinΔd = sin(c2.dec - c1.dec);
121 var u = -K * (1 - sind1 / cosd1 * sinΔd) * cosd1 * tanΔr;
122 var v = K * (sinΔd + sind1 * cosd1 * tanΔr * tanhΔr);
123 return [u, v];
124 };
125 var us = new Array(3).fill(0);
126 var vs = new Array(3).fill(0);
127 cs1.forEach(function (c, x) {
128 var _uv = uv(cs1[x], cs2[x]);
129
130 us[x] = _uv[0];
131 vs[x] = _uv[1];
132 });
133 var u3 = new interp.Len3(-1, 1, us); // if line throws then bug not caller's fault.
134 var v3 = new interp.Len3(-1, 1, vs); // if line throws then bug not caller's fault.
135 var up0 = (us[2] - us[0]) / 2;
136 var vp0 = (vs[2] - vs[0]) / 2;
137 var up1 = us[0] + us[2] - 2 * us[1];
138 var vp1 = vs[0] + vs[2] - 2 * vs[1];
139 var up = up0;
140 var vp = vp0;
141 var dn = -(us[1] * up + vs[1] * vp) / (up * up + vp * vp);
142 var n = dn;
143 var u = void 0;
144 var v = void 0;
145 for (var limit = 0; limit < 10; limit++) {
146 u = u3.interpolateN(n);
147 v = v3.interpolateN(n);
148 if (abs(dn) < 1e-5) {
149 return hypot(u, v); // success
150 }
151 var _up = up0 + n * up1;
152 var _vp = vp0 + n * vp1;
153 dn = -(u * _up + v * _vp) / (_up * _up + _vp * _vp);
154 n += dn;
155 }
156 throw new Error('minSepRect: failure to converge');
157}
158
159/**
160 * haversine function (17.5) p. 115
161 */
162export function hav(a) {
163 return 0.5 * (1 - Math.cos(a));
164}
165
166/**
167 * `sepHav` returns the angular separation between two celestial bodies.
168 *
169 * The algorithm uses the haversine function and is superior to the naïve
170 * algorithm of the Sep function.
171 *
172 * @param {base.Coord} c1 - coordinate of celestial body 1
173 * @param {base.Coord} c2 - coordinate of celestial body 2
174 * @return {Number} angular separation between two celestial bodies
175 */
176export function sepHav(c1, c2) {
177 // using (17.5) p. 115
178 return 2 * asin(sqrt(hav(c2.dec - c1.dec) + cos(c1.dec) * cos(c2.dec) * hav(c2.ra - c1.ra)));
179}
180
181/**
182 * Same as `minSep` but uses function `sepHav` to return the minimum separation
183 * between two moving objects.
184 */
185export function minSepHav(jd1, jd3, cs1, cs2) {
186 return minSep(jd1, jd3, cs1, cs2, sepHav);
187}
188
189/**
190 * `sepPauwels` returns the angular separation between two celestial bodies.
191 *
192 * The algorithm is a numerically stable form of that used in `sep`.
193 *
194 * @param {base.Coord} c1 - coordinate of celestial body 1
195 * @param {base.Coord} c2 - coordinate of celestial body 2
196 * @return {Number} angular separation between two celestial bodies
197 */
198export function sepPauwels(c1, c2) {
199 var _base$sincos4 = base.sincos(c1.dec),
200 sind1 = _base$sincos4[0],
201 cosd1 = _base$sincos4[1];
202
203 var _base$sincos5 = base.sincos(c2.dec),
204 sind2 = _base$sincos5[0],
205 cosd2 = _base$sincos5[1];
206
207 var cosdr = cos(c2.ra - c1.ra);
208 var x = cosd1 * sind2 - sind1 * cosd2 * cosdr;
209 var y = cosd2 * sin(c2.ra - c1.ra);
210 var z = sind1 * sind2 + cosd1 * cosd2 * cosdr;
211 return atan2(hypot(x, y), z);
212}
213
214/**
215 * Same as `minSep` but uses function `sepPauwels` to return the minimum
216 * separation between two moving objects.
217 */
218export function minSepPauwels(jd1, jd3, cs1, cs2) {
219 return minSep(jd1, jd3, cs1, cs2, sepPauwels);
220}
221
222/**
223 * RelativePosition returns the position angle of one body with respect to
224 * another.
225 *
226 * The position angle result `p` is measured counter-clockwise from North.
227 * If negative then `p` is in the range of 90° ... 270°
228 *
229 * ````
230 * North
231 * |
232 * (p) ..|
233 * . |
234 * V |
235 * c1 x------------x c2
236 * |
237 * ````
238 *
239 * @param {base.Coord} c1 - coordinate of celestial body 1
240 * @param {base.Coord} c2 - coordinate of celestial body 2
241 * @return {Number} position angle (p)
242 */
243export function relativePosition(c1, c2) {
244 var _base$sincos6 = base.sincos(c2.ra - c1.ra),
245 sinΔr = _base$sincos6[0],
246 cosΔr = _base$sincos6[1];
247
248 var _base$sincos7 = base.sincos(c2.dec),
249 sind2 = _base$sincos7[0],
250 cosd2 = _base$sincos7[1];
251
252 var p = atan2(sinΔr, cosd2 * tan(c1.dec) - sind2 * cosΔr);
253 return p;
254}
255
256export default {
257 sep: sep,
258 minSep: minSep,
259 minSepRect: minSepRect,
260 hav: hav,
261 sepHav: sepHav,
262 minSepHav: minSepHav,
263 sepPauwels: sepPauwels,
264 minSepPauwels: minSepPauwels,
265 relativePosition: relativePosition
266};
\No newline at end of file