238 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
Object.defineProperty(exports, '__esModule', { value: true });
 | 
						|
 | 
						|
var base = require('./base.cjs');
 | 
						|
var interpolation = require('./interpolation.cjs');
 | 
						|
 | 
						|
/**
 | 
						|
 * @copyright 2013 Sonia Keys
 | 
						|
 * @copyright 2016 commenthol
 | 
						|
 * @license MIT
 | 
						|
 * @module angle
 | 
						|
 */
 | 
						|
const { abs, acos, asin, atan2, cos, hypot, sin, sqrt, tan } = Math;
 | 
						|
 | 
						|
/**
 | 
						|
 * `sep` returns the angular separation between two celestial bodies.
 | 
						|
 *
 | 
						|
 * The algorithm is numerically naïve, and while patched up a bit for
 | 
						|
 * small separations, remains unstable for separations near π.
 | 
						|
 *
 | 
						|
 * @param {Coord} c1 - coordinate of celestial body 1
 | 
						|
 * @param {Coord} c2 - coordinate of celestial body 2
 | 
						|
 * @return {Number} angular separation between two celestial bodies
 | 
						|
 */
 | 
						|
function sep (c1, c2) {
 | 
						|
  const [sind1, cosd1] = base["default"].sincos(c1.dec);
 | 
						|
  const [sind2, cosd2] = base["default"].sincos(c2.dec);
 | 
						|
  const cd = sind1 * sind2 + cosd1 * cosd2 * cos(c1.ra - c2.ra); // (17.1) p. 109
 | 
						|
  if (cd < base["default"].CosSmallAngle) {
 | 
						|
    return acos(cd)
 | 
						|
  } else {
 | 
						|
    const cosd = cos((c2.dec + c1.dec) / 2); // average dec of two bodies
 | 
						|
    return hypot((c2.ra - c1.ra) * cosd, c2.dec - c1.dec) // (17.2) p. 109
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * `minSep` returns the minimum separation between two moving objects.
 | 
						|
 *
 | 
						|
 * The motion is represented as an ephemeris of three rows, equally spaced
 | 
						|
 * in time.  Jd1, jd3 are julian day times of the first and last rows.
 | 
						|
 * R1, d1, r2, d2 are coordinates at the three times.  They must each be
 | 
						|
 * slices of length 3.0
 | 
						|
 *
 | 
						|
 * Result is obtained by computing separation at each of the three times
 | 
						|
 * and interpolating a minimum.  This may be invalid for sufficiently close
 | 
						|
 * approaches.
 | 
						|
 *
 | 
						|
 * @throws Error
 | 
						|
 * @param {Number} jd1 - Julian day - time at cs1[0], cs2[0]
 | 
						|
 * @param {Number} jd3 - Julian day - time at cs1[2], cs2[2]
 | 
						|
 * @param {Coord[]} cs1 - 3 coordinates of moving object 1
 | 
						|
 * @param {Coord[]} cs2 - 3 coordinates of moving object 2
 | 
						|
 * @param {function} [fnSep] - alternative `sep` function e.g. `angle.sepPauwels`, `angle.sepHav`
 | 
						|
 * @return {Number} angular separation between two celestial bodies
 | 
						|
 */
 | 
						|
function minSep (jd1, jd3, cs1, cs2, fnSep) {
 | 
						|
  if (cs1.length !== 3 || cs2.length !== 3) {
 | 
						|
    throw interpolation["default"].errorNot3
 | 
						|
  }
 | 
						|
  const y = new Array(3);
 | 
						|
  cs1.forEach((c, x) => {
 | 
						|
    y[x] = sep(cs1[x], cs2[x]);
 | 
						|
  });
 | 
						|
  const d3 = new interpolation["default"].Len3(jd1, jd3, y);
 | 
						|
  const dMin = d3.extremum()[1];
 | 
						|
  return dMin
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * `minSepRect` returns the minimum separation between two moving objects.
 | 
						|
 *
 | 
						|
 * Like `minSep`, but using a method of rectangular coordinates that gives
 | 
						|
 * accurate results even for close approaches.
 | 
						|
 *
 | 
						|
 * @throws Error
 | 
						|
 * @param {Number} jd1 - Julian day - time at cs1[0], cs2[0]
 | 
						|
 * @param {Number} jd3 - Julian day - time at cs1[2], cs2[2]
 | 
						|
 * @param {Coord[]} cs1 - 3 coordinates of moving object 1
 | 
						|
 * @param {Coord[]} cs2 - 3 coordinates of moving object 2
 | 
						|
 * @return {Number} angular separation between two celestial bodies
 | 
						|
 */
 | 
						|
function minSepRect (jd1, jd3, cs1, cs2) {
 | 
						|
  if (cs1.length !== 3 || cs2.length !== 3) {
 | 
						|
    throw interpolation["default"].ErrorNot3
 | 
						|
  }
 | 
						|
  const uv = function (c1, c2) {
 | 
						|
    const [sind1, cosd1] = base["default"].sincos(c1.dec);
 | 
						|
    const Δr = c2.ra - c1.ra;
 | 
						|
    const tanΔr = tan(Δr);
 | 
						|
    const tanhΔr = tan(Δr / 2);
 | 
						|
    const K = 1 / (1 + sind1 * sind1 * tanΔr * tanhΔr);
 | 
						|
    const sinΔd = sin(c2.dec - c1.dec);
 | 
						|
    const u = -K * (1 - (sind1 / cosd1) * sinΔd) * cosd1 * tanΔr;
 | 
						|
    const v = K * (sinΔd + sind1 * cosd1 * tanΔr * tanhΔr);
 | 
						|
    return [u, v]
 | 
						|
  };
 | 
						|
  const us = new Array(3).fill(0);
 | 
						|
  const vs = new Array(3).fill(0);
 | 
						|
  cs1.forEach((c, x) => {
 | 
						|
    [us[x], vs[x]] = uv(cs1[x], cs2[x]);
 | 
						|
  });
 | 
						|
  const u3 = new interpolation["default"].Len3(-1, 1, us); // if line throws then bug not caller's fault.
 | 
						|
  const v3 = new interpolation["default"].Len3(-1, 1, vs); // if line throws then bug not caller's fault.
 | 
						|
  const up0 = (us[2] - us[0]) / 2;
 | 
						|
  const vp0 = (vs[2] - vs[0]) / 2;
 | 
						|
  const up1 = us[0] + us[2] - 2 * us[1];
 | 
						|
  const vp1 = vs[0] + vs[2] - 2 * vs[1];
 | 
						|
  const up = up0;
 | 
						|
  const vp = vp0;
 | 
						|
  let dn = -(us[1] * up + vs[1] * vp) / (up * up + vp * vp);
 | 
						|
  let n = dn;
 | 
						|
  let u;
 | 
						|
  let v;
 | 
						|
  for (let limit = 0; limit < 10; limit++) {
 | 
						|
    u = u3.interpolateN(n);
 | 
						|
    v = v3.interpolateN(n);
 | 
						|
    if (abs(dn) < 1e-5) {
 | 
						|
      return hypot(u, v) // success
 | 
						|
    }
 | 
						|
    const up = up0 + n * up1;
 | 
						|
    const vp = vp0 + n * vp1;
 | 
						|
    dn = -(u * up + v * vp) / (up * up + vp * vp);
 | 
						|
    n += dn;
 | 
						|
  }
 | 
						|
  throw new Error('minSepRect: failure to converge')
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * haversine function (17.5) p. 115
 | 
						|
 */
 | 
						|
function hav (a) {
 | 
						|
  return 0.5 * (1 - Math.cos(a))
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * `sepHav` returns the angular separation between two celestial bodies.
 | 
						|
 *
 | 
						|
 * The algorithm uses the haversine function and is superior to the naïve
 | 
						|
 * algorithm of the Sep function.
 | 
						|
 *
 | 
						|
 * @param {Coord} c1 - coordinate of celestial body 1
 | 
						|
 * @param {Coord} c2 - coordinate of celestial body 2
 | 
						|
 * @return {Number} angular separation between two celestial bodies
 | 
						|
 */
 | 
						|
function sepHav (c1, c2) {
 | 
						|
  // using (17.5) p. 115
 | 
						|
  return 2 * asin(sqrt(hav(c2.dec - c1.dec) +
 | 
						|
    cos(c1.dec) * cos(c2.dec) * hav(c2.ra - c1.ra)))
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Same as `minSep` but uses function `sepHav` to return the minimum separation
 | 
						|
 * between two moving objects.
 | 
						|
 */
 | 
						|
function minSepHav (jd1, jd3, cs1, cs2) {
 | 
						|
  return minSep(jd1, jd3, cs1, cs2)
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * `sepPauwels` returns the angular separation between two celestial bodies.
 | 
						|
 *
 | 
						|
 * The algorithm is a numerically stable form of that used in `sep`.
 | 
						|
 *
 | 
						|
 * @param {Coord} c1 - coordinate of celestial body 1
 | 
						|
 * @param {Coord} c2 - coordinate of celestial body 2
 | 
						|
 * @return {Number} angular separation between two celestial bodies
 | 
						|
 */
 | 
						|
function sepPauwels (c1, c2) {
 | 
						|
  const [sind1, cosd1] = base["default"].sincos(c1.dec);
 | 
						|
  const [sind2, cosd2] = base["default"].sincos(c2.dec);
 | 
						|
  const cosdr = cos(c2.ra - c1.ra);
 | 
						|
  const x = cosd1 * sind2 - sind1 * cosd2 * cosdr;
 | 
						|
  const y = cosd2 * sin(c2.ra - c1.ra);
 | 
						|
  const z = sind1 * sind2 + cosd1 * cosd2 * cosdr;
 | 
						|
  return atan2(hypot(x, y), z)
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Same as `minSep` but uses function `sepPauwels` to return the minimum
 | 
						|
 * separation between two moving objects.
 | 
						|
 */
 | 
						|
function minSepPauwels (jd1, jd3, cs1, cs2) {
 | 
						|
  return minSep(jd1, jd3, cs1, cs2)
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * RelativePosition returns the position angle of one body with respect to
 | 
						|
 * another.
 | 
						|
 *
 | 
						|
 * The position angle result `p` is measured counter-clockwise from North.
 | 
						|
 * If negative then `p` is in the range of 90° ... 270°
 | 
						|
 *
 | 
						|
 * ````
 | 
						|
 *                  North
 | 
						|
 *                    |
 | 
						|
 *             (p)  ..|
 | 
						|
 *                 .  |
 | 
						|
 *                V   |
 | 
						|
 *    c1 x------------x c2
 | 
						|
 *                    |
 | 
						|
 * ````
 | 
						|
 *
 | 
						|
 * @param {Coord} c1 - coordinate of celestial body 1
 | 
						|
 * @param {Coord} c2 - coordinate of celestial body 2
 | 
						|
 * @return {Number} position angle (p)
 | 
						|
 */
 | 
						|
function relativePosition (c1, c2) {
 | 
						|
  const [sinΔr, cosΔr] = base["default"].sincos(c1.ra - c2.ra);
 | 
						|
  const [sind2, cosd2] = base["default"].sincos(c2.dec);
 | 
						|
  const p = atan2(sinΔr, cosd2 * tan(c1.dec) - sind2 * cosΔr);
 | 
						|
  return p
 | 
						|
}
 | 
						|
 | 
						|
var angle = {
 | 
						|
  sep,
 | 
						|
  minSep,
 | 
						|
  minSepRect,
 | 
						|
  hav,
 | 
						|
  sepHav,
 | 
						|
  minSepHav,
 | 
						|
  sepPauwels,
 | 
						|
  minSepPauwels,
 | 
						|
  relativePosition
 | 
						|
};
 | 
						|
 | 
						|
exports["default"] = angle;
 | 
						|
exports.hav = hav;
 | 
						|
exports.minSep = minSep;
 | 
						|
exports.minSepHav = minSepHav;
 | 
						|
exports.minSepPauwels = minSepPauwels;
 | 
						|
exports.minSepRect = minSepRect;
 | 
						|
exports.relativePosition = relativePosition;
 | 
						|
exports.sep = sep;
 | 
						|
exports.sepHav = sepHav;
 | 
						|
exports.sepPauwels = sepPauwels;
 |