393 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			393 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						||
 | 
						||
Object.defineProperty(exports, '__esModule', { value: true });
 | 
						||
 | 
						||
var base = require('./base.cjs');
 | 
						||
var coord = require('./coord.cjs');
 | 
						||
var elementequinox = require('./elementequinox.cjs');
 | 
						||
var nutation = require('./nutation.cjs');
 | 
						||
var sexagesimal = require('./sexagesimal.cjs');
 | 
						||
 | 
						||
/**
 | 
						||
 * @copyright 2013 Sonia Keys
 | 
						||
 * @copyright 2016 commenthol
 | 
						||
 * @license MIT
 | 
						||
 * @module precess
 | 
						||
 */
 | 
						||
 | 
						||
/**
 | 
						||
 * approxAnnualPrecession returns approximate annual precision in right
 | 
						||
 * ascension and declination.
 | 
						||
 *
 | 
						||
 * The two epochs should be within a few hundred years.
 | 
						||
 * The declinations should not be too close to the poles.
 | 
						||
 *
 | 
						||
 * @param {Equatorial} eqFrom
 | 
						||
 * @param {Number} epochFrom - use `base.JDEToJulianYear(year)` to get epoch
 | 
						||
 * @param {Number} epochTo - use `base.JDEToJulianYear(year)` to get epoch
 | 
						||
 * @returns {Object}
 | 
						||
 *  {sexa.HourAngle} seconds of right ascension
 | 
						||
 *  {sexa.Angle} seconds of Declination
 | 
						||
 */
 | 
						||
function approxAnnualPrecession (eqFrom, epochFrom, epochTo) {
 | 
						||
  const [m, na, nd] = mn(epochFrom, epochTo);
 | 
						||
  const [sa, ca] = base["default"].sincos(eqFrom.ra);
 | 
						||
  // (21.1) p. 132
 | 
						||
  const Δαs = m + na * sa * Math.tan(eqFrom.dec); // seconds of RA
 | 
						||
  const Δδs = nd * ca; // seconds of Dec
 | 
						||
  const ra = new sexagesimal.HourAngle(false, 0, 0, Δαs).rad();
 | 
						||
  const dec = new sexagesimal.Angle(false, 0, 0, Δδs).rad();
 | 
						||
  return { ra, dec }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * @param {Number} epochFrom - use `base.JDEToJulianYear(year)` to get epoch
 | 
						||
 * @param {Number} epochTo - use `base.JDEToJulianYear(year)` to get epoch
 | 
						||
 * @returns {Number[]}
 | 
						||
 */
 | 
						||
function mn (epochFrom, epochTo) {
 | 
						||
  const T = (epochTo - epochFrom) * 0.01;
 | 
						||
  const m = 3.07496 + 0.00186 * T;
 | 
						||
  const na = 1.33621 - 0.00057 * T;
 | 
						||
  const nd = 20.0431 - 0.0085 * T;
 | 
						||
  return [m, na, nd]
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * ApproxPosition uses ApproxAnnualPrecession to compute a simple and quick
 | 
						||
 * precession while still considering proper motion.
 | 
						||
 *
 | 
						||
 * @param {Equatorial} eqFrom
 | 
						||
 * @param {Number} epochFrom
 | 
						||
 * @param {Number} epochTo
 | 
						||
 * @param {Number} mα - in radians
 | 
						||
 * @param {Number} mδ - in radians
 | 
						||
 * @returns {Equatorial} eqTo
 | 
						||
 */
 | 
						||
function approxPosition (eqFrom, epochFrom, epochTo, mα, mδ) {
 | 
						||
  const { ra, dec } = approxAnnualPrecession(eqFrom, epochFrom, epochTo);
 | 
						||
  const dy = epochTo - epochFrom;
 | 
						||
  const eqTo = new coord.Equatorial();
 | 
						||
  eqTo.ra = eqFrom.ra + (ra + mα) * dy;
 | 
						||
  eqTo.dec = eqFrom.dec + (dec + mδ) * dy;
 | 
						||
  return eqTo
 | 
						||
}
 | 
						||
 | 
						||
// constants
 | 
						||
const d = Math.PI / 180;
 | 
						||
const s = d / 3600;
 | 
						||
 | 
						||
// coefficients from (21.2) p. 134
 | 
						||
const ζT = [2306.2181 * s, 1.39656 * s, -0.000139 * s];
 | 
						||
const zT = [2306.2181 * s, 1.39656 * s, -0.000139 * s];
 | 
						||
const θT = [2004.3109 * s, -0.8533 * s, -0.000217 * s];
 | 
						||
// coefficients from (21.3) p. 134
 | 
						||
const ζt = [2306.2181 * s, 0.30188 * s, 0.017998 * s];
 | 
						||
const zt = [2306.2181 * s, 1.09468 * s, 0.018203 * s];
 | 
						||
const θt = [2004.3109 * s, -0.42665 * s, -0.041833 * s];
 | 
						||
 | 
						||
/**
 | 
						||
 * Precessor represents precession from one epoch to another.
 | 
						||
 *
 | 
						||
 * Construct with NewPrecessor, then call method Precess.
 | 
						||
 * After construction, Precess may be called multiple times to precess
 | 
						||
 * different coordinates with the same initial and final epochs.
 | 
						||
 */
 | 
						||
class Precessor {
 | 
						||
  /**
 | 
						||
   * constructs a Precessor object and initializes it to precess
 | 
						||
   * coordinates from epochFrom to epochTo.
 | 
						||
   * @param {Number} epochFrom
 | 
						||
   * @param {Number} epochTo
 | 
						||
   */
 | 
						||
  constructor (epochFrom, epochTo) {
 | 
						||
    // (21.2) p. 134
 | 
						||
    let ζCoeff = ζt;
 | 
						||
    let zCoeff = zt;
 | 
						||
    let θCoeff = θt;
 | 
						||
    if (epochFrom !== 2000) {
 | 
						||
      const T = (epochFrom - 2000) * 0.01;
 | 
						||
      ζCoeff = [
 | 
						||
        base["default"].horner(T, ...ζT),
 | 
						||
        0.30188 * s - 0.000344 * s * T,
 | 
						||
        0.017998 * s
 | 
						||
      ];
 | 
						||
      zCoeff = [
 | 
						||
        base["default"].horner(T, ...zT),
 | 
						||
        1.09468 * s + 0.000066 * s * T,
 | 
						||
        0.018203 * s
 | 
						||
      ];
 | 
						||
      θCoeff = [
 | 
						||
        base["default"].horner(T, ...θT),
 | 
						||
        -0.42665 * s - 0.000217 * s * T,
 | 
						||
        -0.041833 * s
 | 
						||
      ];
 | 
						||
    }
 | 
						||
    const t = (epochTo - epochFrom) * 0.01;
 | 
						||
    this.ζ = base["default"].horner(t, ...ζCoeff) * t;
 | 
						||
    this.z = base["default"].horner(t, ...zCoeff) * t;
 | 
						||
    const θ = base["default"].horner(t, ...θCoeff) * t;
 | 
						||
    this.sθ = Math.sin(θ);
 | 
						||
    this.cθ = Math.cos(θ);
 | 
						||
  }
 | 
						||
 | 
						||
  /**
 | 
						||
   * Precess precesses coordinates eqFrom, leaving result in eqTo.
 | 
						||
   *
 | 
						||
   * @param {Equatorial} eqFrom
 | 
						||
   * @returns {Equatorial} eqTo
 | 
						||
   */
 | 
						||
  precess (eqFrom) {
 | 
						||
    // (21.4) p. 134
 | 
						||
    const [sδ, cδ] = base["default"].sincos(eqFrom.dec);
 | 
						||
    const [sαζ, cαζ] = base["default"].sincos(eqFrom.ra + this.ζ);
 | 
						||
    const A = cδ * sαζ;
 | 
						||
    const B = this.cθ * cδ * cαζ - this.sθ * sδ;
 | 
						||
    const C = this.sθ * cδ * cαζ + this.cθ * sδ;
 | 
						||
    const eqTo = new coord.Equatorial();
 | 
						||
    eqTo.ra = Math.atan2(A, B) + this.z;
 | 
						||
    if (C < base["default"].CosSmallAngle) {
 | 
						||
      eqTo.dec = Math.asin(C);
 | 
						||
    } else {
 | 
						||
      eqTo.dec = Math.acos(Math.hypot(A, B)); // near pole
 | 
						||
    }
 | 
						||
    return eqTo
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * Position precesses equatorial coordinates from one epoch to another,
 | 
						||
 * including proper motions.
 | 
						||
 *
 | 
						||
 * If proper motions are not to be considered or are not applicable, pass 0, 0
 | 
						||
 * for mα, mδ
 | 
						||
 *
 | 
						||
 * Both eqFrom and eqTo must be non-nil, although they may point to the same
 | 
						||
 * struct.  EqTo is returned for convenience.
 | 
						||
 * @param {Equatorial} eqFrom
 | 
						||
 * @param {Number} epochFrom
 | 
						||
 * @param {Number} epochTo
 | 
						||
 * @param {Number} mα - in radians
 | 
						||
 * @param {Number} mδ - in radians
 | 
						||
 * @returns {Equatorial} [eqTo]
 | 
						||
 */
 | 
						||
function position (eqFrom, epochFrom, epochTo, mα, mδ) {
 | 
						||
  const p = new Precessor(epochFrom, epochTo);
 | 
						||
  const t = epochTo - epochFrom;
 | 
						||
  const eqTo = new coord.Equatorial();
 | 
						||
  eqTo.ra = eqFrom.ra + mα * t;
 | 
						||
  eqTo.dec = eqFrom.dec + mδ * t;
 | 
						||
  return p.precess(eqTo)
 | 
						||
}
 | 
						||
 | 
						||
// coefficients from (21.5) p. 136
 | 
						||
const ηT = [47.0029 * s, -0.06603 * s, 0.000598 * s];
 | 
						||
const πT = [174.876384 * d, 3289.4789 * s, 0.60622 * s];
 | 
						||
const pT = [5029.0966 * s, 2.22226 * s, -0.000042 * s];
 | 
						||
const ηt = [47.0029 * s, -0.03302 * s, 0.000060 * s];
 | 
						||
const πt = [174.876384 * d, -869.8089 * s, 0.03536 * s];
 | 
						||
const pt = [5029.0966 * s, 1.11113 * s, -0.000006 * s];
 | 
						||
 | 
						||
/**
 | 
						||
 * EclipticPrecessor represents precession from one epoch to another.
 | 
						||
 *
 | 
						||
 * Construct with NewEclipticPrecessor, then call method Precess.
 | 
						||
 * After construction, Precess may be called multiple times to precess
 | 
						||
 * different coordinates with the same initial and final epochs.
 | 
						||
 */
 | 
						||
class EclipticPrecessor {
 | 
						||
  /**
 | 
						||
   * constructs an EclipticPrecessor object and initializes
 | 
						||
   * it to precess coordinates from epochFrom to epochTo.
 | 
						||
   * @param {Number} epochFrom
 | 
						||
   * @param {Number} epochTo
 | 
						||
   */
 | 
						||
  constructor (epochFrom, epochTo) {
 | 
						||
    // (21.5) p. 136
 | 
						||
    let ηCoeff = ηt;
 | 
						||
    let πCoeff = πt;
 | 
						||
    let pCoeff = pt;
 | 
						||
    if (epochFrom !== 2000) {
 | 
						||
      const T = (epochFrom - 2000) * 0.01;
 | 
						||
      ηCoeff = [
 | 
						||
        base["default"].horner(T, ...ηT),
 | 
						||
        -0.03302 * s + 0.000598 * s * T,
 | 
						||
        0.000060 * s
 | 
						||
      ];
 | 
						||
      πCoeff = [
 | 
						||
        base["default"].horner(T, ...πT),
 | 
						||
        -869.8089 * s - 0.50491 * s * T,
 | 
						||
        0.03536 * s
 | 
						||
      ];
 | 
						||
      pCoeff = [
 | 
						||
        base["default"].horner(T, ...pT),
 | 
						||
        1.11113 * s - 0.000042 * s * T,
 | 
						||
        -0.000006 * s
 | 
						||
      ];
 | 
						||
    }
 | 
						||
    const t = (epochTo - epochFrom) * 0.01;
 | 
						||
    this.π = base["default"].horner(t, ...πCoeff);
 | 
						||
    this.p = base["default"].horner(t, ...pCoeff) * t;
 | 
						||
    const η = base["default"].horner(t, ...ηCoeff) * t;
 | 
						||
    this.sη = Math.sin(η);
 | 
						||
    this.cη = Math.cos(η);
 | 
						||
  }
 | 
						||
 | 
						||
  /**
 | 
						||
   * EclipticPrecess precesses coordinates eclFrom, leaving result in eclTo.
 | 
						||
   *
 | 
						||
   * The same struct may be used for eclFrom and eclTo.
 | 
						||
   * EclTo is returned for convenience.
 | 
						||
   * @param {Ecliptic} eclFrom
 | 
						||
   * @returns {Ecliptic} [eclTo]
 | 
						||
   */
 | 
						||
  precess (eclFrom) {
 | 
						||
    // (21.7) p. 137
 | 
						||
    const [sβ, cβ] = base["default"].sincos(eclFrom.lat);
 | 
						||
    const [sd, cd] = base["default"].sincos(this.π - eclFrom.lon);
 | 
						||
    const A = this.cη * cβ * sd - this.sη * sβ;
 | 
						||
    const B = cβ * cd;
 | 
						||
    const C = this.cη * sβ + this.sη * cβ * sd;
 | 
						||
    const eclTo = new coord.Ecliptic(this.p + this.π - Math.atan2(A, B));
 | 
						||
    if (C < base["default"].CosSmallAngle) {
 | 
						||
      eclTo.lat = Math.asin(C);
 | 
						||
    } else {
 | 
						||
      eclTo.lat = Math.acos(Math.hypot(A, B)); // near pole
 | 
						||
    }
 | 
						||
    return eclTo
 | 
						||
  }
 | 
						||
 | 
						||
  /**
 | 
						||
   * ReduceElements reduces orbital elements of a solar system body from one
 | 
						||
   * equinox to another.
 | 
						||
   *
 | 
						||
   * This function is described in chapter 24, but is located in this
 | 
						||
   * package so it can be a method of EclipticPrecessor.
 | 
						||
   *
 | 
						||
   * @param {Elements} eFrom
 | 
						||
   * @returns {Elements} eTo
 | 
						||
   */
 | 
						||
  reduceElements (eFrom) {
 | 
						||
    const ψ = this.π + this.p;
 | 
						||
    const [si, ci] = base["default"].sincos(eFrom.inc);
 | 
						||
    const [snp, cnp] = base["default"].sincos(eFrom.node - this.π);
 | 
						||
    const eTo = new elementequinox.Elements();
 | 
						||
    // (24.1) p. 159
 | 
						||
    eTo.inc = Math.acos(ci * this.cη + si * this.sη * cnp);
 | 
						||
    // (24.2) p. 159
 | 
						||
    eTo.node = Math.atan2(si * snp, this.cη * si * cnp - this.sη * ci) + ψ;
 | 
						||
    // (24.3) p. 159
 | 
						||
    eTo.peri = Math.atan2(-this.sη * snp, si * this.cη - ci * this.sη * cnp) + eFrom.peri;
 | 
						||
    return eTo
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * eclipticPosition precesses ecliptic coordinates from one epoch to another,
 | 
						||
 * including proper motions.
 | 
						||
 * While eclFrom is given as ecliptic coordinates, proper motions mα, mδ are
 | 
						||
 * still expected to be equatorial.  If proper motions are not to be considered
 | 
						||
 * or are not applicable, pass 0, 0.
 | 
						||
 * Both eclFrom and eclTo must be non-nil, although they may point to the same
 | 
						||
 * struct.  EclTo is returned for convenience.
 | 
						||
 *
 | 
						||
 * @param {Ecliptic} eclFrom,
 | 
						||
 * @param {Number} epochFrom
 | 
						||
 * @param {HourAngle} [mα]
 | 
						||
 * @param {Angle} [mδ]
 | 
						||
 * @returns {Ecliptic} eclTo
 | 
						||
 */
 | 
						||
function eclipticPosition (eclFrom, epochFrom, epochTo, mα, mδ) {
 | 
						||
  const p = new EclipticPrecessor(epochFrom, epochTo);
 | 
						||
 | 
						||
  if (mα && mδ && (mα.rad() !== 0 || mδ.rad() !== 0)) {
 | 
						||
    const { lon, lat } = properMotion(mα.rad(), mδ.rad(), epochFrom, eclFrom);
 | 
						||
    const t = epochTo - epochFrom;
 | 
						||
    eclFrom.lon += lon * t;
 | 
						||
    eclFrom.lat += lat * t;
 | 
						||
  }
 | 
						||
  return p.precess(eclFrom)
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * @param {Number} mα - anual proper motion (ra)
 | 
						||
 * @param {Number} mδ - anual proper motion (dec)
 | 
						||
 * @param {Number} epoch
 | 
						||
 * @param {Ecliptic} ecl
 | 
						||
 * @returns {Ecliptic} {lon, lat}
 | 
						||
 */
 | 
						||
function properMotion (mα, mδ, epoch, ecl) {
 | 
						||
  const ε = nutation["default"].meanObliquity(base["default"].JulianYearToJDE(epoch));
 | 
						||
  const [εsin, εcos] = base["default"].sincos(ε);
 | 
						||
  const { ra, dec } = ecl.toEquatorial(ε);
 | 
						||
  const [sα, cα] = base["default"].sincos(ra);
 | 
						||
  const [sδ, cδ] = base["default"].sincos(dec);
 | 
						||
  const cβ = Math.cos(ecl.lat);
 | 
						||
  const lon = (mδ * εsin * cα + mα * cδ * (εcos * cδ + εsin * sδ * sα)) / (cβ * cβ);
 | 
						||
  const lat = (mδ * (εcos * cδ + εsin * sδ * sα) - mα * εsin * cα * cδ) / cβ;
 | 
						||
  return new coord.Ecliptic(lon, lat)
 | 
						||
}
 | 
						||
 | 
						||
/**
 | 
						||
 * ProperMotion3D takes the 3D equatorial coordinates of an object
 | 
						||
 * at one epoch and computes its coordinates at a new epoch, considering
 | 
						||
 * proper motion and radial velocity.
 | 
						||
 *
 | 
						||
 * Radial distance (r) must be in parsecs, radial velocitiy (mr) in
 | 
						||
 * parsecs per year.
 | 
						||
 *
 | 
						||
 * Both eqFrom and eqTo must be non-nil, although they may point to the same
 | 
						||
 * struct.  EqTo is returned for convenience.
 | 
						||
 *
 | 
						||
 * @param {Equatorial} eqFrom,
 | 
						||
 * @param {Number} epochFrom
 | 
						||
 * @param {Number} r
 | 
						||
 * @param {Number} mr
 | 
						||
 * @param {HourAngle} mα
 | 
						||
 * @param {Angle} mδ
 | 
						||
 * @returns {Equatorial} eqTo
 | 
						||
 */
 | 
						||
function properMotion3D (eqFrom, epochFrom, epochTo, r, mr, mα, mδ) {
 | 
						||
  const [sα, cα] = base["default"].sincos(eqFrom.ra);
 | 
						||
  const [sδ, cδ] = base["default"].sincos(eqFrom.dec);
 | 
						||
  const x = r * cδ * cα;
 | 
						||
  const y = r * cδ * sα;
 | 
						||
  const z = r * sδ;
 | 
						||
  const mrr = mr / r;
 | 
						||
  const zmδ = z * mδ.rad();
 | 
						||
  const mx = x * mrr - zmδ * cα - y * mα.rad();
 | 
						||
  const my = y * mrr - zmδ * sα + x * mα.rad();
 | 
						||
  const mz = z * mrr + r * mδ.rad() * cδ;
 | 
						||
  const t = epochTo - epochFrom;
 | 
						||
  const xp = x + t * mx;
 | 
						||
  const yp = y + t * my;
 | 
						||
  const zp = z + t * mz;
 | 
						||
  const eqTo = new coord.Equatorial();
 | 
						||
  eqTo.ra = Math.atan2(yp, xp);
 | 
						||
  eqTo.dec = Math.atan2(zp, Math.hypot(xp, yp));
 | 
						||
  return eqTo
 | 
						||
}
 | 
						||
 | 
						||
var precess = {
 | 
						||
  approxAnnualPrecession,
 | 
						||
  mn,
 | 
						||
  approxPosition,
 | 
						||
  Precessor,
 | 
						||
  position,
 | 
						||
  EclipticPrecessor,
 | 
						||
  eclipticPosition,
 | 
						||
  properMotion,
 | 
						||
  properMotion3D
 | 
						||
};
 | 
						||
 | 
						||
exports.EclipticPrecessor = EclipticPrecessor;
 | 
						||
exports.Precessor = Precessor;
 | 
						||
exports.approxAnnualPrecession = approxAnnualPrecession;
 | 
						||
exports.approxPosition = approxPosition;
 | 
						||
exports["default"] = precess;
 | 
						||
exports.eclipticPosition = eclipticPosition;
 | 
						||
exports.mn = mn;
 | 
						||
exports.position = position;
 | 
						||
exports.properMotion = properMotion;
 | 
						||
exports.properMotion3D = properMotion3D;
 |