@tubular/astronomy
Version:
Astronomical calculations for planetary positions, moon phases, eclipses, rise, transit, and set times, and more.
241 lines • 9.38 kB
JavaScript
import { floor, max, min, sqrt } from '@tubular/math';
import { clone, extendDelimited } from '@tubular/util';
import { FIRST_JUPITER_MOON, NO_MATCH } from './astro-constants';
import { SolarSystem } from './solar-system';
import { utToTdt } from '@tubular/time';
export const AS_SEEN_FROM_EARTH = false;
export const AS_SEEN_FROM_SUN = true;
export const MOON_ITSELF = false;
export const MOON_SHADOW = true;
export var MoonEvent;
(function (MoonEvent) {
MoonEvent[MoonEvent["TR_I"] = 1] = "TR_I";
MoonEvent[MoonEvent["TR_E"] = 2] = "TR_E";
MoonEvent[MoonEvent["OC_D"] = 3] = "OC_D";
MoonEvent[MoonEvent["OC_R"] = 4] = "OC_R";
MoonEvent[MoonEvent["EC_D"] = 5] = "EC_D";
MoonEvent[MoonEvent["EC_R"] = 6] = "EC_R";
MoonEvent[MoonEvent["SH_I"] = 7] = "SH_I";
MoonEvent[MoonEvent["SH_E"] = 8] = "SH_E"; // Shadow Egress - shadow of moon ends
})(MoonEvent || (MoonEvent = {}));
const moonNumbers = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X',
'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX'];
const moonEvents = ['{0} Tr.I.', '{0} Tr.E.', '{0} Oc.D.', '{0} Oc.R.',
'{0} Ec.D.', '{0} Ec.R.', '{0} Sh.I.', '{0} Sh.E.'];
const moonEventsLong = ['{0} begins transit', '{0} ends transit', '{0} becomes occulted', '{0} emerges from occultation',
'{0} becomes eclipsed', '{0} emerges from eclipse', 'Shadow of {0} appears', 'Shadow of {0} ends'];
const CACHE_SIZE = 6;
export class MoonEvents {
constructor() {
this.count = 0;
this.text = '';
this.searchΔT = 1; // In minutes
}
}
export class PlanetaryMoons {
constructor() {
this.cachedTimes = [];
this.cachedMoons = [];
this.solarSystem = new SolarSystem();
this.cachedTimes[0] = [];
this.cachedTimes[1] = [];
this.cachedMoons[0] = [];
this.cachedMoons[1] = [];
}
getMoonPosition(moonIndex, time_JDE, sunPerspective = false) {
const moons = this.getMoonPositions(time_JDE, sunPerspective, false);
for (const moon of moons) {
if (moon.moonIndex === moonIndex)
return clone(moon);
}
return undefined;
}
getMoonPositions(time_JDE, sunPerspective = false, makeClones = true) {
const index = (sunPerspective ? 1 : 0);
let moons;
for (let i = 0; i < CACHE_SIZE; ++i) {
if (time_JDE === this.cachedTimes[index][i] && this.cachedMoons[index][i] != null) {
moons = this.cachedMoons[index][i];
break;
}
}
if (!moons) {
moons = this.getMoonPositionsAux(time_JDE, sunPerspective);
// Shuffle cache
for (let i = 0; i < CACHE_SIZE - 1; ++i) {
this.cachedTimes[index][i] = this.cachedTimes[index][i + 1];
this.cachedMoons[index][i] = this.cachedMoons[index][i + 1];
}
this.cachedTimes[index][CACHE_SIZE - 1] = time_JDE;
this.cachedMoons[index][CACHE_SIZE - 1] = moons;
}
if (!makeClones)
return moons;
const result = [];
for (const moon of moons)
result.push(clone(moon));
return result;
}
// Note: The span considered is +/- 30 seconds of the specified time *in Universal Time*.
//
getMoonEventsForOneMinuteSpan(time_JDU, longFormat = false) {
const events = new MoonEvents();
const t0 = utToTdt(time_JDU - 0.5 / 1440);
const t1 = utToTdt(time_JDU + 0.5 / 1440);
const pos0 = this.getMoonPositions(t0);
const pos1 = this.getMoonPositions(t1);
const sunPos0 = this.getMoonPositions(t0, AS_SEEN_FROM_SUN);
const sunPos1 = this.getMoonPositions(t1, AS_SEEN_FROM_SUN);
const nmoons = pos0.length;
events.events = [];
events.shadowEvents = [];
events.searchΔT = (!this.v_max ? 1 : 120); // At the very most, put off more event checking for two hours.
events.t0 = t0;
events.t1 = t1;
if (this.v_max) {
let Y1;
let d;
let ΔT;
findSearchTime: for (let i = 0; i < nmoons; ++i) {
for (let j = 0; j < 4; ++j) {
let pos;
switch (j) {
case 0:
pos = pos0;
break;
case 1:
pos = pos1;
break;
case 2:
pos = sunPos0;
break;
case 3:
default:
pos = sunPos1;
break;
}
Y1 = pos[i].Y * this.flattening;
d = sqrt(pos[i].X * pos[i].X + Y1 ** 2);
if (d > 1)
d -= 1;
else
d = 1 - d;
ΔT = max(floor(d / this.v_max[i] * 0.75), 1);
events.searchΔT = min(ΔT, events.searchΔT);
if (ΔT === 1)
break findSearchTime;
}
}
}
for (let i = 0; i < nmoons; ++i) {
const tr0 = pos0[i].inFrontOfDisc;
const tr1 = pos1[i].inFrontOfDisc;
const oc0 = pos0[i].behindDisc;
const oc1 = pos1[i].behindDisc;
const ec0 = sunPos0[i].behindDisc;
const ec1 = sunPos1[i].behindDisc;
const sh0 = sunPos0[i].inFrontOfDisc;
const sh1 = sunPos1[i].inFrontOfDisc;
if (!tr0 && tr1) {
++events.count;
events.events[i] = MoonEvent.TR_I;
}
else if (tr0 && !tr1) {
++events.count;
events.events[i] = MoonEvent.TR_E;
}
else if (!oc0 && oc1 && !ec0) {
++events.count;
events.events[i] = MoonEvent.OC_D;
}
else if (oc0 && !oc1 && !ec1) {
++events.count;
events.events[i] = MoonEvent.OC_R;
}
else if (!ec0 && ec1 && !oc0) {
++events.count;
events.events[i] = MoonEvent.EC_D;
}
else if (ec0 && !ec1 && !oc1) {
++events.count;
events.events[i] = MoonEvent.EC_R;
}
if (!sh0 && sh1) {
++events.count;
events.shadowEvents[i] = MoonEvent.SH_I;
}
else if (sh0 && !sh1) {
++events.count;
events.shadowEvents[i] = MoonEvent.SH_E;
}
}
if (events.count > 0) {
let eventNames;
const pad = [];
let maxNumLen = 0;
let eventText;
if (longFormat)
eventNames = moonEventsLong;
else
eventNames = moonEvents;
for (let i = 0; i < nmoons; ++i)
maxNumLen = max(moonNumbers[i + 1].length, maxNumLen);
if (!longFormat) {
for (let i = 0; i < nmoons; ++i)
pad.push(' '.repeat(maxNumLen - moonNumbers[i + 1].length));
}
for (let i = 0; i < nmoons; ++i) {
let moonName;
if (longFormat)
moonName = PlanetaryMoons.getMoonName(FIRST_JUPITER_MOON + i);
else
moonName = pad[i] + moonNumbers[i + 1];
for (let j = 0; j < 2; ++j) {
const list = (j === 0 ? events.events : events.shadowEvents);
if (list[i]) {
eventText = eventNames[list[i] - 1];
eventText = eventText.replace('{0}', moonName);
events.text = extendDelimited(events.text, eventText);
}
}
}
}
return events;
}
static getMoonName(moonIndex, getShadow = MOON_ITSELF) {
for (const mni of PlanetaryMoons.namesList) {
if (mni.first <= moonIndex && moonIndex <= mni.last) {
moonIndex -= mni.first;
return (getShadow ? mni.shadowNames[moonIndex] : mni.names[moonIndex]);
}
}
return undefined;
}
static getMoonNumber(moonIndex) {
moonIndex %= 1000;
if (moonIndex <= 0 || moonIndex >= moonNumbers.length)
return String(moonIndex);
else
return moonNumbers[moonIndex];
}
static getMoonByName(moonName) {
moonName = moonName.toLowerCase();
for (const mni of this.namesList) {
for (let j = 0; j < mni.names.length; ++j) {
if (mni.names[j].toLowerCase() === moonName)
return mni.first + j;
}
}
return NO_MATCH;
}
static registerMoonNames(first, last, names, shadowNames) {
const mni = {};
mni.first = first;
mni.last = last;
mni.names = names;
mni.shadowNames = shadowNames;
this.namesList.push(mni);
}
}
PlanetaryMoons.namesList = [];
//# sourceMappingURL=planetary-moons.js.map