astronomia
Version:
An astronomical library
269 lines (252 loc) • 7.55 kB
JavaScript
/**
* @copyright 2013 Sonia Keys
* @copyright 2016 commenthol
* @license MIT
* @module eclipse
*/
/**
* Eclipse: Chapter 54, Eclipses.
*/
import base from './base.js'
import moonphase from './moonphase.js'
/**
* @private
*/
const g = function (k, jm, c1, c2) { // (k, jm, c1, c2 float64) (eclipse bool, jdeMax, γ, u, Mʹ float64)
const ck = 1 / 1236.85
const p = Math.PI / 180
const T = k * ck
const F = base.horner(T, 160.7108 * p, 390.67050284 * p / ck,
-0.0016118 * p, -0.00000227 * p, 0.000000011 * p)
if (Math.abs(Math.sin(F)) > 0.36) {
return [false] // no eclipse
}
const eclipse = true
const E = base.horner(T, 1, -0.002516, -0.0000074)
const M = base.horner(T, 2.5534 * p, 29.1053567 * p / ck,
-0.0000014 * p, -0.00000011 * p)
const Mʹ = base.horner(T, 201.5643 * p, 385.81693528 * p / ck,
0.0107582 * p, 0.00001238 * p, -0.000000058 * p)
const Ω = base.horner(T, 124.7746 * p, -1.56375588 * p / ck,
0.0020672 * p, 0.00000215 * p)
const sΩ = Math.sin(Ω)
const F1 = F - 0.02665 * p * sΩ
const A1 = base.horner(T, 299.77 * p, 0.107408 * p / ck, -0.009173 * p)
// (54.1) p. 380
const jdeMax = jm +
c1 * Math.sin(Mʹ) +
c2 * Math.sin(M) * E +
0.0161 * Math.sin(2 * Mʹ) +
-0.0097 * Math.sin(2 * F1) +
0.0073 * Math.sin(Mʹ - M) * E +
-0.005 * Math.sin(Mʹ + M) * E +
-0.0023 * Math.sin(Mʹ - 2 * F1) +
0.0021 * Math.sin(2 * M) * E +
0.0012 * Math.sin(Mʹ + 2 * F1) +
0.0006 * Math.sin(2 * Mʹ + M) * E +
-0.0004 * Math.sin(3 * Mʹ) +
-0.0003 * Math.sin(M + 2 * F1) * E +
0.0003 * Math.sin(A1) +
-0.0002 * Math.sin(M - 2 * F1) * E +
-0.0002 * Math.sin(2 * Mʹ - M) * E +
-0.0002 * sΩ
const P = 0.207 * Math.sin(M) * E +
0.0024 * Math.sin(2 * M) * E +
-0.0392 * Math.sin(Mʹ) +
0.0116 * Math.sin(2 * Mʹ) +
-0.0073 * Math.sin(Mʹ + M) * E +
0.0067 * Math.sin(Mʹ - M) * E +
0.0118 * Math.sin(2 * F1)
const Q = 5.2207 +
-0.0048 * Math.cos(M) * E +
0.002 * Math.cos(2 * M) * E +
-0.3299 * Math.cos(Mʹ) +
-0.006 * Math.cos(Mʹ + M) * E +
0.0041 * Math.cos(Mʹ - M) * E
const [sF1, cF1] = base.sincos(F1)
const W = Math.abs(cF1)
const γ = (P * cF1 + Q * sF1) * (1 - 0.0048 * W)
const u = 0.0059 +
0.0046 * Math.cos(M) * E +
-0.0182 * Math.cos(Mʹ) +
0.0004 * Math.cos(2 * Mʹ) +
-0.0005 * Math.cos(M + Mʹ)
return [eclipse, jdeMax, γ, u, Mʹ] // (eclipse bool, jdeMax, γ, u, Mʹ float64)
}
/**
* Eclipse type identifiers returned from Solar and Lunar.
*/
export const TYPE = {
None: 0,
Partial: 1, // for solar eclipses
Annular: 2, // solar
AnnularTotal: 3, // solar
Penumbral: 4, // for lunar eclipses
Umbral: 5, // lunar
Total: 6 // solar or lunar
}
/**
* Snap returns k at specified quarter q nearest year y.
* Cut and paste from moonphase. Time corresponding to k needed in these
* algorithms but otherwise not meaningful enough to export from moonphase.
*/
const snap = function (y, q) { // (y, q float64) float64
const k = (y - 2000) * 12.3685 // (49.2) p. 350
return Math.floor(k - q + 0.5) + q
}
/**
* Solar computes quantities related to solar eclipses.
*
* Argument year is a decimal year specifying a date.
*
* eclipseType will be None, Partial, Annular, AnnularTotal, or Total.
* If None, none of the other return values may be meaningful.
*
* central is true if the center of the eclipse shadow touches the Earth.
*
* jdeMax is the jde when the center of the eclipse shadow is closest to the
* Earth center, in a plane through the center of the Earth.
*
* γ is the distance from the eclipse shadow center to the Earth center
* at time jdeMax.
*
* u is the radius of the Moon's umbral cone in the plane of the Earth.
*
* p is the radius of the penumbral cone.
*
* mag is eclipse magnitude for partial eclipses. It is not valid for other
* eclipse types.
*
* γ, u, and p are in units of equatorial Earth radii.
*/
export function solar (year) { // (year float64) (eclipseType int, central bool, jdeMax, γ, u, p, mag float64)
let eclipseType = TYPE.None
let mag
const [e, jdeMax, γ, u, _] = g(snap(year, 0), moonphase.meanNew(year), -0.4075, 0.1721) // eslint-disable-line no-unused-vars
const p = u + 0.5461
if (!e) {
return { type: eclipseType } // no eclipse
}
const aγ = Math.abs(γ)
if (aγ > 1.5433 + u) {
return { type: eclipseType } // no eclipse
}
const central = aγ < 0.9972 // eclipse center touches Earth
if (!central) {
eclipseType = TYPE.Partial // most common case
if (aγ < 1.026) { // umbral cone may touch earth
if (aγ < 0.9972 + Math.abs(u)) { // total or annular
eclipseType = TYPE.Total // report total in both cases
}
}
} else if (u < 0) {
eclipseType = TYPE.Total
} else if (u > 0.0047) {
eclipseType = TYPE.Annular
} else {
const ω = 0.00464 * Math.sqrt(1 - γ * γ)
if (u < ω) {
eclipseType = TYPE.AnnularTotal
} else {
eclipseType = TYPE.Annular
}
}
if (eclipseType === TYPE.Partial) {
// (54.2) p. 382
mag = (1.5433 + u - aγ) / (0.5461 + 2 * u)
}
return {
type: eclipseType,
central,
jdeMax,
magnitude: mag,
distance: γ,
umbral: u,
penumbral: p
}
}
/**
* Lunar computes quantities related to lunar eclipses.
*
* Argument year is a decimal year specifying a date.
*
* eclipseType will be None, Penumbral, Umbral, or Total.
* If None, none of the other return values may be meaningful.
*
* jdeMax is the jde when the center of the eclipse shadow is closest to the
* Moon center, in a plane through the center of the Moon.
*
* γ is the distance from the eclipse shadow center to the moon center
* at time jdeMax.
*
* σ is the radius of the umbral cone in the plane of the Moon.
*
* ρ is the radius of the penumbral cone.
*
* mag is eclipse magnitude.
*
* sd- return values are semidurations of the phases of the eclipse, in days.
*
* γ, σ, and ρ are in units of equatorial Earth radii.
*/
export function lunar (year) { // (year float64) (eclipseType int, jdeMax, γ, ρ, σ, mag, sdTotal, sdPartial, sdPenumbral float64)
let eclipseType = TYPE.None
let mag
let sdTotal
let sdPartial
let sdPenumbral
const [e, jdeMax, γ, u, Mʹ] = g(snap(year, 0.5),
moonphase.meanFull(year), -0.4065, 0.1727)
if (!e) {
return { type: eclipseType } // no eclipse
}
const ρ = 1.2848 + u
const σ = 0.7403 - u
const aγ = Math.abs(γ)
mag = (1.0128 - u - aγ) / 0.545 // (54.3) p. 382
if (mag > 1) {
eclipseType = TYPE.Total
} else if (mag > 0) {
eclipseType = TYPE.Umbral
} else {
mag = (1.5573 + u - aγ) / 0.545 // (54.4) p. 382
if (mag < 0) {
return { type: eclipseType } // no eclipse
}
eclipseType = TYPE.Penumbral
}
const p = 1.0128 - u
const t = 0.4678 - u
const n = 0.5458 + 0.04 * Math.cos(Mʹ)
const γ2 = γ * γ
/* eslint-disable no-fallthrough */
switch (eclipseType) {
case TYPE.Total: {
sdTotal = Math.sqrt(t * t - γ2) / n / 24
}
case TYPE.Umbral: {
sdPartial = Math.sqrt(p * p - γ2) / n / 24
}
default: {
const h = 1.5573 + u
sdPenumbral = Math.sqrt(h * h - γ2) / n / 24
}
}
/* eslint-enable */
return {
type: eclipseType,
jdeMax,
magnitude: mag,
distance: γ,
umbral: σ,
penumbral: ρ,
sdTotal,
sdPartial,
sdPenumbral
}
}
export default {
TYPE,
solar,
lunar
}