Skip to content

Moon

This example computes the Moon's position in ECI coordinates, moonrise and moonset times for a ground observer, phase and illumination data, and angular size. Use these APIs when planning night observations or modeling lunar interference for optical sensors.

ts
import {
  calcGmst,
  Degrees,
  eci2lla,
  GroundStation,
  Kilometers,
  Moon,
} from 'ootk';

Run it

bash
npm run build
npx tsx ./examples/moon.ts

Moon position

Moon.eci returns the Moon's position as a Vector3D<Kilometers> in Earth-centered inertial coordinates, and Moon.getDistanceFromEarth returns the geocentric distance. Combining the ECI vector with calcGmst and eci2lla gives the sub-lunar point in geodetic coordinates.

ts
console.log('=== Example 1: Moon Position ===\n');

const date = new Date('2024-01-28T12:00:00.000Z');

// Moon.eci returns the Moon's position as a Vector3D in Earth-centered
// inertial coordinates
const moonEci = Moon.eci(date);

console.log('Moon Position (ECI):');
console.log(`  X: ${moonEci.x.toFixed(2)} km`);
console.log(`  Y: ${moonEci.y.toFixed(2)} km`);
console.log(`  Z: ${moonEci.z.toFixed(2)} km`);

// Distance from Earth center
const distance = Moon.getDistanceFromEarth(date);

console.log(`\nDistance from Earth: ${distance.toFixed(2)} km`);
console.log(`                     ${(distance / 384400).toFixed(4)} x mean lunar distance`);

// Convert to latitude/longitude (sub-lunar point)
const gmst = calcGmst(date);
const moonLla = eci2lla(moonEci, gmst.gmst);

console.log('\nSub-Lunar Point:');
console.log(`  Latitude: ${moonLla.lat.toFixed(4)} deg`);
console.log(`  Longitude: ${moonLla.lon.toFixed(4)} deg`);

Rise and set times

Moon.getMoonTimes takes a GroundObject (here a GroundStation with degrees and kilometers) and searches one day for horizon crossings. Either rise or set can be null when that event does not occur inside the search window, and the alwaysUp/alwaysDown flags cover polar cases.

ts
console.log('\n=== Example 2: Moon Rise/Set Times ===\n');

const observer = new GroundStation({
  name: 'Cape Cod',
  lat: 41 as Degrees,
  lon: -71 as Degrees,
  alt: 0 as Kilometers,
});

const moonTimes = Moon.getMoonTimes(date, observer, true);

console.log(`Observer Location: ${observer.lat} deg N, ${Math.abs(observer.lon)} deg W`);
console.log(`\nMoon Times for ${date.toDateString()}:`);

if (moonTimes.rise) {
  console.log(`  Moonrise: ${moonTimes.rise.toISOString()}`);
} else {
  console.log('  Moonrise: No moonrise today');
}

if (moonTimes.set) {
  console.log(`  Moonset:  ${moonTimes.set.toISOString()}`);
} else {
  console.log('  Moonset:  No moonset today');
}

Phase and illumination

Moon.getPhase returns the illuminated fraction, a phaseValue from 0 (new) through 0.5 (full) back to 1 (new), and a named phase bucket with an emoji. Moon.getPhaseAngle returns the phase angle in degrees, where 0 is new moon and 180 is full moon.

ts
console.log('\n=== Example 3: Moon Phase and Illumination ===\n');

// getPhase returns the illuminated fraction, the phase value (0 = new,
// 0.5 = full), a named phase bucket, and the times of the next phase events
const phaseInfo = Moon.getPhase(date);

console.log(`Phase: ${(phaseInfo.phaseValue * 100).toFixed(2)}%`);
console.log('  0% = New Moon');
console.log('  25% = First Quarter');
console.log('  50% = Full Moon');
console.log('  75% = Last Quarter');

console.log(`\nIllumination: ${(phaseInfo.fraction * 100).toFixed(2)}%`);
console.log(`Phase Angle: ${Moon.getPhaseAngle(date).toFixed(2)} deg`);
console.log(`Moon Phase: ${phaseInfo.phase.name} ${phaseInfo.phase.emoji}`);

Angular size

Moon.getAngularDiameterDeg computes the apparent diameter using the true Earth-Moon distance at the given time, so it varies between roughly 29 and 34 arcminutes across the anomalistic month.

ts
console.log('\n=== Example 4: Moon Angular Size ===\n');

// getAngularDiameterDeg accounts for the actual Earth-Moon distance at the
// given time
const angularDiameterDeg = Moon.getAngularDiameterDeg(date);
const angularDiameterArcmin = angularDiameterDeg * 60;

console.log(`Angular Diameter: ${angularDiameterArcmin.toFixed(2)} arcminutes`);
console.log(`                  ${angularDiameterDeg.toFixed(4)} deg`);
console.log('\nMean angular diameter: ~31 arcminutes');

Position over a day

Sampling Moon.eci every 6 hours shows the distance changing slowly while the sub-lunar longitude sweeps westward with Earth's rotation.

ts
console.log('\n=== Example 5: Moon Position Every 6 Hours ===\n');

for (let hour = 0; hour < 24; hour += 6) {
  const timePoint = new Date(date);

  timePoint.setUTCHours(hour, 0, 0, 0);

  const moonPos = Moon.eci(timePoint);
  const dist = moonPos.magnitude();

  const gmstTime = calcGmst(timePoint);
  const lla = eci2lla(moonPos, gmstTime.gmst);

  console.log(`${timePoint.toUTCString()}:`);
  console.log(`  Distance: ${dist.toFixed(2)} km`);
  console.log(`  Sub-lunar point: ${lla.lat.toFixed(2)} deg N, ${lla.lon.toFixed(2)} deg E`);
  console.log('');
}

Next phase events

The next field of getPhase precomputes the next occurrence of each principal phase (new, first quarter, full, third quarter) plus the nearest upcoming event, so there is no need to scan day by day.

ts
console.log('=== Example 6: Next Phase Events ===\n');

// getPhase precomputes the next occurrence of each principal phase
const next = phaseInfo.next;

console.log(`From ${date.toISOString()}:`);
console.log(`  Next New Moon:      ${next.newMoon.date}`);
console.log(`  Next First Quarter: ${next.firstQuarter.date}`);
console.log(`  Next Full Moon:     ${next.fullMoon.date}`);
console.log(`  Next Third Quarter: ${next.thirdQuarter.date}`);
console.log(`\nNearest upcoming event: ${next.type} at ${next.date}`);

Output

txt
=== Example 1: Moon Position ===

Moon Position (ECI):
  X: -375309.83 km
  Y: 130111.11 km
  Z: 81275.72 km

Distance from Earth: 405452.97 km
                     1.0548 x mean lunar distance

Sub-Lunar Point:
  Latitude: 11.5648 deg
  Longitude: -146.3782 deg

=== Example 2: Moon Rise/Set Times ===

Observer Location: 41 deg N, 71 deg W

Moon Times for Sun Jan 28 2024:
  Moonrise: No moonrise today
  Moonset:  2024-01-28T13:41:13.643Z

=== Example 3: Moon Phase and Illumination ===

Phase: 58.70%
  0% = New Moon
  25% = First Quarter
  50% = Full Moon
  75% = Last Quarter

Illumination: 92.72%
Phase Angle: 210.28 deg
Moon Phase: Waning Gibbous 🌖

=== Example 4: Moon Angular Size ===

Angular Diameter: 29.46 arcminutes
                  0.4910 deg

Mean angular diameter: ~31 arcminutes

=== Example 5: Moon Position Every 6 Hours ===

Sun, 28 Jan 2024 00:00:00 GMT:
  Distance: 404996.44 km
  Sub-lunar point: 14.11 deg N, 28.61 deg E

... (truncated)

Sun, 28 Jan 2024 18:00:00 GMT:
  Distance: 405608.30 km
  Sub-lunar point: 10.25 deg N, 126.08 deg E

=== Example 6: Next Phase Events ===

From 2024-01-28T12:00:00.000Z:
  Next New Moon:      2024-02-09T19:59:47.844Z
  Next First Quarter: 2024-02-17T05:10:48.538Z
  Next Full Moon:     2024-02-24T14:21:49.233Z
  Next Third Quarter: 2024-02-02T10:48:47.149Z

Nearest upcoming event: thirdQuarter at 2024-02-02T10:48:47.149Z

Released under the AGPL-3.0 License.