LRO Lunar Orbit¶
In this example we'll set up an LRO-like low lunar science orbit and propagate it with brahe's lunar force model. The Lunar Reconnaissance Orbiter (LRO) has flown a polar, near-frozen low lunar science orbit around the Moon since 2009, mapping the surface at high resolution; this example uses an LRO-like ~30 x 180 km class orbit rather than reproducing its exact orbital history. We'll compare a full-fidelity propagation against a point-mass Moon to quantify how the Moon's lumpy gravity field ("mascons" - dense, gravitationally anomalous regions beneath several lunar maria) perturbs a low lunar orbit, then visualize the trajectory in 3D around a textured Moon.
Why Lunar Orbits Are Different¶
Unlike Earth, the Moon's gravity field is dominated by large, irregular mass concentrations rather than a smooth oblateness term. Orbit designers counter the resulting perturbations by choosing a "frozen orbit" - an inclination and argument of perilune where the long-period perturbation from the gravity field's dominant harmonics averages to zero, so eccentricity and perilune altitude stay bounded over many orbits instead of drifting monotonically. Even so, these mascon-driven gravity anomalies are strong enough that most low lunar orbits are unstable on timescales of weeks to months without station-keeping. Proper modeling of the high-order gravity dynamics enable design of station-keeping maneuvers that minimize the growth of these instabilities, conserving fuel and extending mission-lifetime.
Orbit Setup¶
The propagator integrates in the Moon-Centered Inertial (LCI) frame, whose axes are ICRF-aligned: the LCI z-axis is the ICRF pole, which sits about 22 degrees from the Moon's spin pole. Passing the elements straight to state_koe_to_eci would measure the 85.2 degree inclination and the south-pole perilune against the ICRF pole, not the lunar equator. state_koe_to_inertial_for_body instead references the elements to the body's mean equator at J2000 - the plane normal to the body's IAU pole, with the x-axis on the ascending node of that equator on the ICRF equator - and returns the state directly in the body-centered inertial frame (LCI for the Moon). It takes a CentralBody (which supplies both the Moon's gravitational parameter and its pole), so the orbit is placed against the lunar equator with no manual basis construction. We also evaluate the lunar mean pole at J2000 (the third row of the ICRF-to-lunar-body-fixed rotation) to confirm the geometry below:
With the standard preamble in place, the next step sets up the frozen orbit geometry.
Propagation¶
We propagate the same initial state under two force models: lunar_default() (50x50 GRGM660PRIM gravity, SRP occulted by the Moon and Earth, and Earth/Sun third-body perturbations) and a point-mass Moon via ForceModelConfig.for_body with GravityConfiguration.point_mass(). Both propagators integrate in the Moon-Centered Inertial (LCI) frame:
state_bci vs. state_eci
state_bci (API) returns the propagator's native state in the central body's body-centered inertial frame (LCI for a Moon-centered propagator). state_eci (API) instead always returns an Earth-centered state - for a Moon-centered propagator it adds the Moon's Earth-relative position, which would report altitudes near the Earth-Moon distance rather than above the lunar surface. Use state_bci whenever you need the distance from the body being orbited.
Altitude Comparison¶
Sampling both trajectories over the 7-day propagation and computing altitude above R_MOON from the Moon-centered radius, then plotting the difference between the full-gravity and point-mass solutions, shows how steadily the mascons perturb the orbit:
Over 7 days, the difference between the full and point-mass solutions grows to about 17 km at its peak - a substantial fraction of the orbit's own 150 km altitude range between perilune and apolune - while the full-model orbit's perilune altitude stays above 26 km, remaining bound to the Moon.
3D Visualization¶
plot_trajectory_3d accepts central_body="moon" to render an interactive 3D view of the trajectory around a textured Moon. Non-Earth central bodies plot the trajectory in that body's centered-inertial frame, converting through the reference frame router with to_frame() when the trajectory is declared in another frame; a Moon-centered NumericalOrbitPropagator's .trajectory is already in CelestialFrame.LCI, so no conversion is needed. We plot the final 12 hours of the full-gravity trajectory:
Body textures: Solar System Scope, CC BY 4.0.
Full Code Example¶
Full Code
| lro_lunar_orbit.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
See Also¶
- Numerical Orbit Propagation - Propagator fundamentals
- Force Models - Configuring force models, including
lunar_default() - 3D Trajectory Plotting - Advanced options for trajectory visualization, including non-Earth central bodies