UNPKG

astronomia

Version:
339 lines (311 loc) 10.9 kB
/* eslint key-spacing: 1 */ /** * @copyright 2013 Sonia Keys * @copyright 2016 commenthol * @license MIT * @module rise */ /** * Rise: Chapter 15, Rising, Transit, and Setting. */ import base from './base.js' import deltat from './deltat.js' import elliptic from './elliptic.js' import interp from './interpolation.js' import julian from './julian.js' import sexa from './sexagesimal.js' import sidereal from './sidereal.js' import { Coord as GlobeCoord } from './globe.js' // eslint-disable-line no-unused-vars import { Planet } from './planetposition.js' // eslint-disable-line no-unused-vars const { acos, asin, cos, sin } = Math /** * @typedef {object} RiseObj * @property {number} rise - in seconds * @property {number} transit - in seconds * @property {number} set - in seconds */ const SECS_PER_DEGREE = 240 // = 86400 / 360 const SECS_PER_DAY = 86400 const D2R = Math.PI / 180 export const errorAboveHorizon = base.errorCode('always above horizon', -1) export const errorBelowHorizon = base.errorCode('always below horizon', 1) /** * mean refraction of the atmosphere */ export const meanRefraction = new sexa.Angle(false, 0, 34, 0).rad() /** * "Standard altitudes" for various bodies already including `meanRefraction` of 0°34' * * The standard altitude is the geometric altitude of the center of body * at the time of apparent rising or seting. */ export const stdh0 = { stellar: -meanRefraction, solar: new sexa.Angle(true, 0, 50, 0).rad(), // not containing meanRefraction lunar: sexa.angleFromDeg(0.7275), lunarMean: sexa.angleFromDeg(0.125) } /** * Helper function to obtain corrected refraction * @param {number} h0 - altitude of the body in radians containing `meanRefraction` of 0°34' * @param {number} [corr] - the calcluated refraction e.g. from package `refraction` in radians * @return {number} refraction value in radians */ export function refraction (h0, corr) { if (!corr) { return h0 } else { return h0 - meanRefraction - corr } } /** * standard altitude for stars, planets at apparent rising, seting */ export const stdh0Stellar = (_refraction) => refraction(stdh0.stellar, _refraction) export const Stdh0Stellar = stdh0Stellar() // for backward-compatibility /** * standard altitude for sun for upper limb of the disk */ export const stdh0Solar = (_refraction) => refraction(stdh0.solar, _refraction) export const Stdh0Solar = stdh0Solar() // for backward-compatibility /** * standard altitude for moon (low accuracy) */ export const stdh0LunarMean = (_refraction) => { return stdh0.lunarMean - refraction(_refraction) } export const Stdh0LunarMean = stdh0LunarMean() // for backward-compatibility /** * Stdh0Lunar is the standard altitude of the Moon considering π, the * Moon's horizontal parallax. * @param {number} π - the Moon's horizontal parallax * @param {number} [refraction] - optional value for refraction in radians if * omitted than meanRefraction is used * @return {number} altitude of Moon in radians */ export const stdh0Lunar = (π, refraction) => { return stdh0.lunar * π - (refraction || meanRefraction) } export const Stdh0Lunar = stdh0Lunar // for backward-compatibility /** * @return {number} local angle in radians */ export function hourAngle (lat, h0, δ) { // approximate local hour angle const cosH = (sin(h0) - sin(lat) * sin(δ)) / (cos(lat) * cos(δ)) // (15.1) p. 102 if (cosH < -1) { throw errorAboveHorizon } else if (cosH > 1) { throw errorBelowHorizon } const H = acos(cosH) return H } /** * @param {number} lon - longitude in radians * @param {number} α - right ascension in radians * @param {number} th0 - sidereal.apparent0UT in seconds of day `[0...86400[` * @return {number} time of transit in seconds of day `[0, 86400[` */ function _mt (lon, α, th0) { // const mt = (((lon + α) * 180 / Math.PI - (th0 * 360 / 86400)) * 86400 / 360) const mt = (lon + α) * SECS_PER_DEGREE * 180 / Math.PI - th0 return mt } /** * @param {number} Th0 - sidereal.apparent0UT in seconds of day `[0...86400[` * @param {number} m - motion in seconds of day `[0...86400[` * @return {number} new siderial time seconds of day `[0...86400[` */ function _th0 (Th0, m) { // in original formula Th0 = 0...360 and m = 0...1 -> return value would be in 0...360 degrees // Th0 /= 240 // m /= 86400 const th0 = base.pmod(Th0 + m * 360.985647 / 360, SECS_PER_DAY) // p103 return th0 // 0...86400 in seconds angle } /** * maintain backward compatibility - will be removed in v2 * return value in future will be an object not an array * @private * @param {RiseObj} rs * @return {RiseObj} */ function _compatibility (rs) { const _rs = [rs.rise, rs.transit, rs.set] _rs.rise = rs.rise _rs.transit = rs.transit _rs.set = rs.set return _rs } /** * ApproxTimes computes approximate UT rise, transit and set times for * a celestial object on a day of interest. * * The function argurments do not actually include the day, but do include * values computed from the day. * * @param {GlobeCoord} p - is geographic coordinates of observer. * @param {number} h0 - is "standard altitude" of the body in radians * @param {number} Th0 - is apparent sidereal time at 0h UT at Greenwich in seconds * (range 0...86400) must be the time on the day of interest, in seconds. * See sidereal.apparent0UT * @param {number} α - right ascension (radians) * @param {number} δ - declination (radians) * @return {RiseObj} Result units are seconds and are in the range [0,86400) * @throws Error */ export function approxTimes (p, h0, Th0, α, δ) { const H0 = hourAngle(p.lat, h0, δ) * SECS_PER_DEGREE * 180 / Math.PI // in degrees per day === seconds // approximate transit, rise, set times. // (15.2) p. 102.0 const mt = _mt(p.lon, α, Th0) const rs = {} rs.transit = base.pmod(mt, SECS_PER_DAY) rs.rise = base.pmod(mt - H0, SECS_PER_DAY) rs.set = base.pmod(mt + H0, SECS_PER_DAY) return _compatibility(rs) } /** * Times computes UT rise, transit and set times for a celestial object on * a day of interest. * * The function argurments do not actually include the day, but do include * a number of values computed from the day. * * @param {GlobeCoord} p - is geographic coordinates of observer. * @param {number} ΔT - is delta T in seconds * @param {number} h0 - is "standard altitude" of the body in radians * @param {number} Th0 - is apparent sidereal time at 0h UT at Greenwich in seconds * (range 0...86400) must be the time on the day of interest, in seconds. * See sidereal.apparent0UT * @param {Array<number>} α3 - slices of three right ascensions * @param {Array<number>} δ3 - slices of three declinations. * α3, δ3 must be values at 0h dynamical time for the day before, the day of, * and the day after the day of interest. Units are radians. * * @return {RiseObj} Result units are seconds and are in the range [0,86400) * @throws Error */ export function times (p, ΔT, h0, Th0, α3, δ3) { // (p globe.Coord, ΔT, h0, Th0 float64, α3, δ3 []float64) (mRise, mTransit, mSet float64, err error) const rs = approxTimes(p, h0, Th0, α3[1], δ3[1]) const d3α = new interp.Len3(-SECS_PER_DAY, SECS_PER_DAY, α3) const d3δ = new interp.Len3(-SECS_PER_DAY, SECS_PER_DAY, δ3) // adjust mTransit const ut = rs.transit + ΔT const α = d3α.interpolateX(ut) const th0 = _th0(Th0, rs.transit) const H = -1 * _mt(p.lon, α, th0) // in secs // Hmeus = 0...360 rs.transit -= H // adjust mRise, mSet const [sLat, cLat] = base.sincos(p.lat) const adjustRS = function (m) { const ut = m + ΔT const α = d3α.interpolateX(ut) const δ = d3δ.interpolateX(ut) const th0 = _th0(Th0, m) const H = -1 * _mt(p.lon, α, th0) const Hrad = (H / SECS_PER_DEGREE) * D2R const h = asin(((sLat * sin(δ)) + (cLat * cos(δ) * cos(Hrad)))) // formula 13.6 const Δm = (SECS_PER_DAY * (h - h0) / (cos(δ) * cLat * sin(Hrad) * 2 * Math.PI)) // formula p103 3 return m + Δm } rs.rise = adjustRS(rs.rise) rs.set = adjustRS(rs.set) return _compatibility(rs) } /** * RisePlanet computes rise, transit and set times for a planet on a day of interest. */ export class PlanetRise { /** * @param {number|Date} jd - Julian Day starting at midnight or Date object * @param {number} lat - geographic latitude of the observerin degrees * @param {number} lon - geographic longitude of the observer in degrees (measured positively westward) * @param {Planet} earth - VSOP87 Planet object for Earth * @param {Planet} planet - VSOP87 Planet object of observed body * @param {object} [opts] * @param {boolean} [opts.date] - return times as Date objects * @param {number} [opts.refraction] - use different refraction than `stdh0Stellar` */ constructor (jd, lat, lon, earth, planet, opts) { this.opts = opts || {} this.refraction = this.opts.refraction || stdh0Stellar() if (jd instanceof Date) { jd = new julian.Calendar().fromDate(jd).toJD() } this.jd = Math.floor(jd - 0.5) + 0.5 // start at midnight this.lat = lat * D2R // convert to radians this.lon = lon * D2R const cal = new julian.Calendar().fromJD(this.jd) this.jde = cal.toJDE() this.ΔT = deltat.deltaT(cal.toYear()) this.earth = earth this.planet = planet } approxTimes () { const body = elliptic.position(this.planet, this.earth, this.jde) const Th0 = sidereal.apparent0UT(this.jd) const rs = approxTimes( { lat: this.lat, lon: this.lon }, this.refraction, Th0, body.ra, body.dec ) return this._rsToJD(rs) } times () { const body = [ elliptic.position(this.planet, this.earth, this.jde - 1), elliptic.position(this.planet, this.earth, this.jde), elliptic.position(this.planet, this.earth, this.jde + 1) ] const Th0 = sidereal.apparent0UT(this.jd) const rs = times( { lat: this.lat, lon: this.lon }, this.ΔT, this.refraction, Th0, this._toArr(body, 'ra'), this._toArr(body, 'dec') ) return this._rsToJD(rs) } /** @private */ _toArr (body, p) { return body.map((item) => { return item[p] }) } /** @private */ _rsToJD (rs) { return { rise: this._toJD(rs.rise), transit: this._toJD(rs.transit), set: this._toJD(rs.set) } } /** @private */ _toJD (secs) { const jd = this.jd + secs / 86400 if (this.opts.date) { return new julian.Calendar().fromJD(jd).toDate() } else { return jd } } } export default { errorAboveHorizon, errorBelowHorizon, meanRefraction, stdh0, refraction, stdh0Stellar, Stdh0Stellar, stdh0Solar, Stdh0Solar, stdh0LunarMean, Stdh0LunarMean, stdh0Lunar, Stdh0Lunar, hourAngle, approxTimes, times, PlanetRise }