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 |
|
21 | import base from './base';
|
22 | import interp from './interpolation';
|
23 | var 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 |
|
44 | export 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 | */
|
80 | export 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 | */
|
107 | export 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 | */
|
162 | export 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 | */
|
176 | export 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 | */
|
185 | export 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 | */
|
198 | export 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 | */
|
218 | export 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 | */
|
243 | export 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 |
|
256 | export 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 |