Skip to content

Moon

The satkit.moon module provides functions for computing the moon's position, illumination fraction, and phase, moonrise and moonset times (rise_set), and the times of the principal phases (phase_times, next_phase). The position is Vallado (2013) Algorithm 31 (§5.2.3, accurate to ~0.3° in ecliptic longitude); for higher precision use jplephem, or pass use_jpl=True to the rise/set and phase-time functions. The Sun & Moon Rise/Set tutorial has worked examples.

moon

Astrodynamic calculations related to the moon

moonphase

Enum representing moon phases

Each value covers a range of the moon phase angle (see :func:phase), given here in degrees.

NewMoon class-attribute

New Moon (phase angle 0 - 22.5 degrees, or 337.5 - 360 degrees)

WaxingCrescent class-attribute

Waxing Crescent (phase angle 22.5 - 67.5 degrees)

FirstQuarter class-attribute

First Quarter (phase angle 67.5 - 112.5 degrees)

WaxingGibbous class-attribute

Waxing Gibbous (phase angle 112.5 - 157.5 degrees)

FullMoon class-attribute

Full Moon (phase angle 157.5 - 202.5 degrees)

WaningGibbous class-attribute

Waning Gibbous (phase angle 202.5 - 247.5 degrees)

LastQuarter class-attribute

Last Quarter (phase angle 247.5 - 292.5 degrees)

WaningCrescent class-attribute

Waning Crescent (phase angle 292.5 - 337.5 degrees)

pos_gcrf(time)

pos_gcrf(time: TimeScalar) -> npt.NDArray[np.float64]
pos_gcrf(time: TimeArrayLike) -> npt.NDArray[np.float64]

Approximate Moon position in the GCRF Frame

Algorithm 31 from Vallado for the moon in Mean of Date (MOD), then rotated from MOD to GCRF via Equations 3-88 and 3-89 in Vallado

Parameters:

Name Type Description Default
time time

time at which to compute position

required

Returns:

Type Description
NDArray[float64]

npt.NDArray[np.float64]: 3-element numpy array representing moon position in GCRF frame

NDArray[float64]

at given time. Units are meters

Notes

Accurate to about 0.3 degree in ecliptic longitude (0.36 degree worst case against JPL DE440 over 1950-2100), 0.2 degree in ecliptic latitude, and 1275 km in range

Example
import numpy as np
t = satkit.time(2024, 1, 1)
moon = satkit.moon.pos_gcrf(t)
print(f"Moon distance: {np.linalg.norm(moon)/1e3:.0f} km")

pos_mod(time)

pos_mod(time: TimeScalar) -> npt.NDArray[np.float64]
pos_mod(time: TimeArrayLike) -> npt.NDArray[np.float64]

Approximate Moon position in the Mean-of-Date (MOD) Frame

Algorithm 31 from Vallado for the moon in Mean of Date (MOD)

pos_gcrf(t) = satkit.frametransform.qmod2gcrf(t) * pos_mod(t)

Parameters:

Name Type Description Default
time time

time at which to compute position

required

Returns:

Type Description
NDArray[float64]

npt.NDArray[np.float64]: 3-element numpy array representing moon position in MOD frame

NDArray[float64]

at given time. Units are meters

Notes

Accurate to about 0.3 degree in ecliptic longitude (0.36 degree worst case against JPL DE440 over 1950-2100), 0.2 degree in ecliptic latitude, and 1275 km in range

Useful when comparing with Vallado's worked examples, which are given in MOD coordinates

Example
import numpy as np
t = satkit.time(2024, 1, 1)
moon = satkit.moon.pos_mod(t)
print(f"Moon distance: {np.linalg.norm(moon)/1e3:.0f} km")

illumination(time)

illumination(time: TimeScalar) -> float
illumination(time: TimeArrayLike) -> list[float]

Fractional illumination of moon

Parameters:

Name Type Description Default
time time | datetime

scalar time at which to compute illumination

required

Returns:

Name Type Description
float float

fractional illumination of moon at the given time, unitless, range 0.0 to 1.0

Example
t = satkit.time(2024, 1, 1)
illum = satkit.moon.illumination(t)
print(f"Moon illumination: {illum*100:.1f}%")

phase(time)

phase(time: TimeScalar) -> float
phase(time: TimeArrayLike) -> list[float]

Phase of moon in radians

Parameters:

Name Type Description Default
time TimeArrayLike

list or numpy array of times at which to compute phase

required

Returns:

Type Description
list[float]

list[float]: moon phase in radians at each given time

phase_name(time)

phase_name(time: TimeScalar) -> moonphase
phase_name(time: TimeArrayLike) -> list[moonphase]

Phase name of moon

Parameters:

Name Type Description Default
time TimeArrayLike

list or numpy array of times at which to compute phase name

required

Returns:

Type Description
list[moonphase]

list[moonphase]: moon phase name at each given time

rise_set(time, coord, *, use_jpl=False)

Moonrise and moonset times on a calendar date at the given location.

The input selects the UTC calendar date (its time of day is ignored); the returned moonrise and moonset are those of that date at the location's longitude, i.e. in the local mean day from 0h UTC - longitude / 15 hours to 24 hours later, as UTC times. At far-west longitudes an event can fall on the next UTC date (and at far-east longitudes on the previous one). These are the same day semantics as satkit.sun.rise_set.

To get the events of a local date, pass a timezone-aware datetime at local noon: for time zones UTC-11 to UTC+11 local noon falls on the same UTC date, while local midnight falls on the previous UTC date east of Greenwich.

The Moon rises about 50 minutes later each day, so about once a month there is no moonrise on a date (and on another no moonset): that event is None. At high latitudes the Moon can stay up or down all day, giving (None, None), and a day can hold two moonrises (or moonsets): the first is returned.

Notes
  • Rise and set are when the Moon's upper limb touches a sea-level horizon with 34 arcmin of refraction (the USNO / Astronomical Almanac definition): the topocentric altitude of the Moon's centre is -34' - asin(R_moon / d), with d the topocentric distance and R_moon = 1737.4 km. Parallax is exact; altitude is measured from the plane normal to the geodetic vertical, with no dip correction for an elevated site.
  • The altitude is sampled every 10 minutes (and near-horizon extrema checked for a grazing Moon), and events refined to 0.05 s.
  • Accuracy against Skyfield (DE440s) over 2024, 35 S to 62 N: with use_jpl, 0.1 s (1.6 s for a grazing Moon at 62 N); built-in, 52 s at the equator, 89 s at 35 deg, 128 s at 52 deg, a few minutes at 62 deg. Real refraction varies with the weather by a minute or more of time.

Parameters:

Name Type Description Default
time time | datetime | datetime64

time whose UTC calendar date selects the day

required
coord itrfcoord

location at which to compute moonrise and moonset

required
use_jpl bool

use the JPL ephemeris (apparent position) and the full IERS 2010 Earth orientation instead of the built-in analytic Moon. Default False

False

Returns:

Type Description
time | None

tuple[satkit.time | None, satkit.time | None]: moonrise and moonset, UTC, each None

time | None

if it does not happen that day

Raises:

Type Description
RuntimeError

with use_jpl, if the JPL ephemeris is unavailable or does not cover the date

Example
from datetime import datetime, timedelta, timezone

# Moonrise and moonset on 2024-03-15 (local date) in New York
eastern = timezone(timedelta(hours=-4))
coord = satkit.itrfcoord(latitude_deg=40.71, longitude_deg=-74.01)
noon = datetime(2024, 3, 15, 12, tzinfo=eastern)
rise, set = satkit.moon.rise_set(noon, coord)
print(f"Moonrise: {rise.to_datetime().astimezone(eastern) if rise else None}")
print(f"Moonset:  {set.to_datetime().astimezone(eastern) if set else None}")

phase_times(start, end, *, use_jpl=False)

Times of the principal Moon phases in a time interval

Returns every New Moon, First Quarter, Full Moon and Last Quarter in [start, end), in time order.

Notes
  • The principal phases are when the Moon's geocentric apparent ecliptic longitude exceeds the Sun's by 0, 90, 180 and 270 degrees (the Astronomical Almanac / USNO definition). Full Moon is therefore not exactly the instant of greatest illuminated fraction.
  • With use_jpl, apparent JPL positions (light time and aberration, which move the Sun by 20 arcsec and the phase times by ~40 s) in the mean ecliptic of date; otherwise the built-in analytic Sun and Moon (see phase)
  • Accuracy over 2024: with use_jpl, 0.1 s of Skyfield (DE440s) and 40 s of USNO's minute-rounded times; built-in, up to 22 minutes (the analytic Moon's 0.36 deg worst-case longitude error allows 42)

Parameters:

Name Type Description Default
start time | datetime | datetime64

start of the interval (inclusive)

required
end time | datetime | datetime64

end of the interval (exclusive)

required
use_jpl bool

use apparent JPL positions instead of the built-in analytic Sun and Moon. Default False

False

Returns:

Type Description
list[tuple[moonphase, time]]

list[tuple[moonphase, satkit.time]]: (phase, time) pairs in time order; empty if end <= start

Raises:

Type Description
RuntimeError

with use_jpl, if the JPL ephemeris is unavailable or does not cover the interval

Example
# The full moons of 2024
for phase, t in satkit.moon.phase_times(satkit.time(2024, 1, 1), satkit.time(2025, 1, 1)):
    if phase == satkit.moon.moonphase.FullMoon:
        print(t)

next_phase(time, phase, *, use_jpl=False)

Time of the next occurrence of a principal Moon phase

Returns the first time at or after time when the Moon reaches phase. See phase_times for the definition and accuracy.

Parameters:

Name Type Description Default
time time | datetime | datetime64

time from which to search

required
phase moonphase

NewMoon, FirstQuarter, FullMoon or LastQuarter

required
use_jpl bool

use apparent JPL positions instead of the built-in analytic Sun and Moon. Default False

False

Returns:

Type Description
time

satkit.time: time of the phase, UTC

Raises:

Type Description
ValueError

if phase is not one of the four principal phases (e.g. WaxingCrescent, which spans a range of phases)

RuntimeError

with use_jpl, if the JPL ephemeris is unavailable or does not cover the search

Example
full = satkit.moon.next_phase(satkit.time(2024, 1, 1), satkit.moon.moonphase.FullMoon)
print(f"Next full moon: {full}")