Physics
Gravity harmonics & third body
GGM03S spherical-harmonic geopotential, J2/J3 fallback, gravity-gradient torque, and Sun/Moon point-mass perturbations. The Moon can come from a JPL ephemeris file.
Source: cpp/src/orbit/gravity.cpp, third_body.cpp, frames.cpp, attitude/integrate.cpp.
Spherical-harmonic geopotential
- Coefficients: GRACE GGM03S (
data/GGM03S.txt). The header supplies R, μ, ω and nmax. - Reference model: Basilisk
SphericalHarmonicsGravityModel, Autonomous Vehicle Systems Laboratory, University of Colorado Boulder (AVSLab/basilisk, ISC). The plant does not link Basilisk.tests/basilisk_ref/compares the two fields. Cite Basilisk when you reuse this evaluator. - v2.7.5 stores the GGM03S coefficients fully normalised and evaluates fully normalised associated Legendre functions, so a 70×70 field stays finite. Degrees 2, 8 and 24 match the previous unnormalised evaluator to roundoff.
- Degree cap
SH_MAX_DEGREE = 70. Presets stay atfast2,standard4,high8.gravity.model: twobodyforces degree 0. - Frame: the gravity is evaluated in ECEF after a GMST rotation (
accel_gravity_N) and rotated back. Earth orientation is GMST-only. - Gradient: spherical components (∂U/∂r, ∂U/∂φ, ∂U/∂λ) mapped to Cartesian, with a pole guard (cos φ < 1e-14).
- Fallback when no GGM file is found: two-body + closed-form J2 + J3 (J2 = 1.0826e-3, J3 = −2.5327e-6).
ArlamxV2Envrefuses the silent fallback when a GGM path was requested but failed to load.
| YAML | SimParams | Default |
|---|---|---|
gravity.model | — | ggm03s | twobody |
gravity.degree | sh_degree | 4 (0–70) |
gravity.lunisolar | lunisolar | false (high: true) |
gravity.gradient_torque | gravity_gradient | true |
Magnitude guide at 400 km
| Term | |a| (m/s²) |
|---|---|
| Two-body | 8.7 |
| J2 | ≈ 1e-2 |
| degree 3–8 terms | 1e-5 … 1e-6 |
| Moon third body | 0.6–1.2e-6 (computed with cpp.accel_third_body) |
| Sun third body | 0.3–0.5e-6 |
| Drag on the 0.625 kg SolarCat, face-on, ρ = 3e-12 kg/m³ | ≈ 2e-4 |
| SRP on the same sail, face-on | ≈ 9e-6 |
Gravity-gradient torque
It uses the full inertia tensor, and it is on by default (gravity.gradient_torque). The mean over the step is reported as tau_gg_mean.
Third-body perturbation
- This is the direct form (Montenbruck & Gill §3.3). It is accurate here because |r|/|r_k| is tiny, so there is no cancellation problem worth a Battin f(q) formulation at LEO.
- With
lunisolar: true, Sun and Moon positions are computed once per advisor step (300 s) and held. The Moon moves ≈ 0.04° in that time, which is negligible. - Presets: only
highenables lunisolar.
Moon: analytic vs ephemeris file
Ephemeris::moon_pos(jd) returns the CSPICE spkezr_c("MOON", et, "J2000", "NONE", "EARTH") position when kernels are loaded. Otherwise it falls back to a truncated Meeus series (5 longitude terms, 2 latitude terms, parallax distance).
| Source | Moon direction error | Moon distance error | Effect on a_3B at 400 km |
|---|---|---|---|
| Analytic Meeus (current build) | 0.25° mean, 0.44° max | up to ≈ 3000 km | ≈ 3e-8 m/s² (≈ 3 % of the lunar term) |
| DE440/DE430 via CSPICE | < 1e-6° (kernel accuracy) | metres | negligible |
The analytic errors above were measured for this page against astropy's built-in ephemeris over 40 epochs in 2026 (TETE frame).
- The shipped
.sowas built without CSPICE (CSPICE_INCLUDEandCSPICE_LIBare empty inbuild/CMakeCache.txt).load_spicetherefore returnsFalseand does nothing. - Nothing in
env.py,physics.pyor the YAMLs callsload_spice, and there is noephemeris:config key. - The time argument is converted as
et = (jd − 2451545.0)·86400, which treats the UTC Julian date as TDB. That is ≈ 69 s off in 2026, or ≈ 70 km of lunar motion. - SPICE returns J2000 axes, while the plant's inertial axes are effectively of-date (GMST rotation, analytic Sun of date). That is a ≈ 0.36° precession mismatch in 2026 for the Sun direction used in SRP and eclipse once SPICE is on. It is harmless for third-body accelerations but visible in eclipse entry/exit timing (up to a few seconds).
How to use an ephemeris file today (no code changes)
- Rebuild with CSPICE (instructions) and confirm
ARLAMX: CSPICE enabledin the configure output. - Download an SPK, for example
de440s.bsp(1849–2150, ≈ 32 MB) from NAIFgeneric_kernels/spk/planets/. - In your own driver script, after the env is built:
With SubprocVecEnv each worker must call it, so wrap it inenv = ArlamxV2Env(variant="v10", physics="high") # high => lunisolar: true ok = env.sim.load_spice(["/data/kernels/de440s.bsp"]) # True only if CSPICE is linked assert ok, "CSPICE not linked or kernel failed to load"make_envor anenv_kwhook. SPICE state is process-global. - For a standard-physics run with the Moon on, pass
--physicsa copy ofphysics.yamlwithgravity.lunisolar: true.
A proper ephemeris: {kernels: [...]} YAML key, the TDB conversion and a J2000→of-date rotation are small follow-ups. They are listed in Validation & known limits.
Validation
tests/orbit/test_gravity.py: J2/J3 closed form vs SH, finite-difference gradient of U.tests/basilisk_ref/test_gravity_vs_basilisk.py: SH acceleration vs BasiliskcomputeField(passes here; skipped without Basilisk).tests/orbit/test_third_body.py: hand-evaluated third-body case, eclipse, analytic Sun.ACEnv/Reports/verify_gravity.md: audit record.
References
- Tapley, B. et al. (2007). GGM03 — CSR GRACE gravity model.
- Montenbruck, O. & Gill, E. (2000). Satellite Orbits, §3.2–3.3.
- Kaula, W. M. (1966). Theory of Satellite Geodesy.
- Meeus, J. (1998). Astronomical Algorithms, ch. 47.
- Acton, C. H. (1996). NAIF SPICE. Planet. Space Sci. 44(1).