Earth's gravitational field is the dominant force acting on satellites and space debris. While a simple point-mass model provides a useful first approximation, the real Earth's non-spherical mass distribution creates additional gravitational effects that must be modeled for accurate orbit prediction.
The simplest model treats Earth (or any celestial body) as a point mass with all mass concentrated at its center. The gravitational acceleration is:
\[ \mathbf{a} = -\frac{GM}{r^3} \mathbf{r} \]
where:
\(GM\) is the gravitational parameter (m³/s²)
\(\mathbf{r}\) is the position vector from the central body's center (m)
\(r = |\mathbf{r}|\) is the distance
This model for gravity is computationally efficient and works well for modeling the effect of third-body perturbations from other planets and moons. This is discussed further in the Third-Body Perturbations section.
Newton's law is excellent since it allows us to analytically solve the two-body problem. However, for Earth-orbiting satellites, the point-mass assumption is insufficient due to the planet's non-uniform shape and mass distribution. The Earth's equatorial bulge, polar flattening, and irregular mass distribution cause the gravitational attraction to vary with location. These variations are modeled using spherical harmonics - a mathematical expansion in terms of Legendre polynomials.
\(\mathbf{J}_2\) (the \(C_{2,0}\right\) harmonic) models Earth's oblateness and is ~1000× larger than any other term. It causes orbital precession, that is regression of the ascending node and rotation of the argument of perigee, which make sun-synchronous orbits possible.
\(\mathbf{J}_{2,2}\) (the \(C_{2,2}, S_{2,2}\right\) harmonics) model Earth's ellipticity (difference between equatorial radii). Creates tesseral perturbations.
Higher-order terms: Become important for precise orbit determination and long-term propagation, especially for low Earth orbit satellites.
Spherical harmonic evaluation involves recursive computation of Legendre polynomials and requires rotation between Earth-fixed and inertial frames. The computational cost scales as O(n²) where n is the maximum degree.
For real-time applications or long propagations with many time steps, limiting the degree and order to only what's necessary for the required accuracy is important for performance.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Define satellite position in ECI frame (LEO satellite at 500 km altitude)# Using Keplerian elements and converting to Cartesiana=bh.R_EARTH+500e3# Semi-major axis (m)e=0.001# Eccentricityi=97.8# Inclination (deg)raan=0.0# RAAN (deg)argp=0.0# Argument of perigee (deg)nu=0.0# True anomaly (deg)# Convert to Cartesian stateoe=np.array([a,e,i,raan,argp,nu])state=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)r_sat=state[0:3]# Position vector (m)print("Satellite position (ECI, m):")print(f" x = {r_sat[0]:.3f}")print(f" y = {r_sat[1]:.3f}")print(f" z = {r_sat[2]:.3f}")# Compute point-mass gravitational acceleration# For Earth-centered case, central body is at originr_earth=np.array([0.0,0.0,0.0])accel=bh.accel_point_mass_gravity(r_sat,r_earth,bh.GM_EARTH)print("\nPoint-mass gravity acceleration (m/s²):")print(f" ax = {accel[0]:.6f}")print(f" ay = {accel[1]:.6f}")print(f" az = {accel[2]:.6f}")# Compute magnitudeaccel_mag=np.linalg.norm(accel)print(f"\nAcceleration magnitude: {accel_mag:.6f} m/s²")# Compare to theoretical value: GM/r²r_mag=np.linalg.norm(r_sat)accel_theoretical=bh.GM_EARTH/(r_mag**2)print(f"Theoretical magnitude: {accel_theoretical:.6f} m/s²")
//! nalgebra = "0.33"//! ```//! Compute point-mass gravitational acceleration for an Earth satellite#[allow(unused_imports)]usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Define satellite position in ECI frame (LEO satellite at 500 km altitude)// Using Keplerian elements and converting to Cartesianleta=bh::constants::R_EARTH+500e3;// Semi-major axis (m)lete=0.001;// Eccentricityleti=97.8;// Inclination (deg)letraan=0.0;// RAAN (deg)letargp=0.0;// Argument of perigee (deg)letnu=0.0;// True anomaly (deg)// Convert to Cartesian stateletoe=na::SVector::<f64,6>::new(a,e,i,raan,argp,nu);letstate=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letr_sat=na::Vector3::new(state[0],state[1],state[2]);// Position vector (m)println!("Satellite position (ECI, m):");println!(" x = {:.3}",r_sat[0]);println!(" y = {:.3}",r_sat[1]);println!(" z = {:.3}",r_sat[2]);// Compute point-mass gravitational acceleration// For Earth-centered case, central body is at originletr_earth=na::Vector3::<f64>::zeros();letaccel=bh::orbit_dynamics::accel_point_mass_gravity(r_sat,r_earth,bh::constants::GM_EARTH);println!("\nPoint-mass gravity acceleration (m/s²):");println!(" ax = {:.6}",accel[0]);println!(" ay = {:.6}",accel[1]);println!(" az = {:.6}",accel[2]);// Compute magnitudeletaccel_mag=accel.norm();println!("\nAcceleration magnitude: {:.6} m/s²",accel_mag);// Compare to theoretical value: GM/r²letr_mag=r_sat.norm();letaccel_theoretical=bh::constants::GM_EARTH/(r_mag*r_mag);println!("Theoretical magnitude: {:.6} m/s²",accel_theoretical);}
importbraheasbhimportnumpyasnpbh.initialize_eop()# Create an epoch for frame transformationsepoch=bh.Epoch.from_datetime(2024,1,1,12,0,0.0,0.0,bh.TimeSystem.UTC)# Define satellite position in ECI frame (LEO satellite at 500 km altitude)a=bh.R_EARTH+500e3# Semi-major axis (m)e=0.001# Eccentricityi=97.8# Inclination (deg)raan=45.0# RAAN (deg)argp=30.0# Argument of perigee (deg)nu=60.0# True anomaly (deg)# Convert to Cartesian stateoe=np.array([a,e,i,raan,argp,nu])state_eci=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)r_eci=state_eci[0:3]# Position vector (m)print("Satellite position (ECI, m):")print(f" x = {r_eci[0]:.3f}")print(f" y = {r_eci[1]:.3f}")print(f" z = {r_eci[2]:.3f}")# Load gravity model (GGM05S - degree/order 180)gravity_model=bh.GravityModel.from_model_type(bh.GravityModelType.GGM05S)print(f"\nGravity model: GGM05S (max degree {gravity_model.n_max}, max order {gravity_model.m_max})")# For spherical harmonics, we need the ECI to body-fixed rotation matrix# This rotates from ECI (inertial) to ECEF (Earth-fixed) frameR_eci_ecef=bh.rotation_eci_to_ecef(epoch)# Compute spherical harmonic acceleration (degree 10, order 10)n_max=10m_max=10accel_sh=bh.accel_gravity_spherical_harmonics(r_eci,R_eci_ecef,gravity_model,n_max,m_max)print(f"\nSpherical harmonic acceleration (degree {n_max}, order {m_max}):")print(f" ax = {accel_sh[0]:.9f} m/s²")print(f" ay = {accel_sh[1]:.9f} m/s²")print(f" az = {accel_sh[2]:.9f} m/s²")# Compute point-mass for comparisonaccel_pm=bh.accel_point_mass_gravity(r_eci,np.array([0.0,0.0,0.0]),bh.GM_EARTH)print("\nPoint-mass acceleration:")print(f" ax = {accel_pm[0]:.9f} m/s²")print(f" ay = {accel_pm[1]:.9f} m/s²")print(f" az = {accel_pm[2]:.9f} m/s²")# Compute difference (perturbation due to non-spherical Earth)accel_pert=accel_sh-accel_pmprint("\nPerturbation (spherical harmonics - point mass):")print(f" Δax = {accel_pert[0]:.9f} m/s²")print(f" Δay = {accel_pert[1]:.9f} m/s²")print(f" Δaz = {accel_pert[2]:.9f} m/s²")print(f" Magnitude: {np.linalg.norm(accel_pert):.9f} m/s²")
//! nalgebra = "0.33"//! ```//! Compute spherical harmonic gravitational acceleration for an Earth satellite#[allow(unused_imports)]usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Create an epoch for frame transformationsletepoch=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);// Define satellite position in ECI frame (LEO satellite at 500 km altitude)leta=bh::constants::R_EARTH+500e3;// Semi-major axis (m)lete=0.001;// Eccentricityleti=97.8;// Inclination (deg)letraan=45.0;// RAAN (deg)letargp=30.0;// Argument of perigee (deg)letnu=60.0;// True anomaly (deg)// Convert to Cartesian stateletoe=na::SVector::<f64,6>::new(a,e,i,raan,argp,nu);letstate_eci=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);letr_eci=na::Vector3::new(state_eci[0],state_eci[1],state_eci[2]);// Position vector (m)println!("Satellite position (ECI, m):");println!(" x = {:.3}",r_eci[0]);println!(" y = {:.3}",r_eci[1]);println!(" z = {:.3}",r_eci[2]);// Load gravity model (GGM05S - degree/order 180)letgravity_model=bh::orbit_dynamics::GravityModel::from_model_type(&bh::orbit_dynamics::GravityModelType::GGM05S).unwrap();println!("\nGravity model: GGM05S (max degree {}, max order {})",gravity_model.n_max,gravity_model.m_max);// For spherical harmonics, we need the ECI to body-fixed rotation matrix// This rotates from ECI (inertial) to ECEF (Earth-fixed) frameletr_eci_ecef=bh::rotation_eci_to_ecef(epoch);// Compute spherical harmonic acceleration (degree 10, order 10)letn_max=10;letm_max=10;letaccel_sh=bh::orbit_dynamics::accel_gravity_spherical_harmonics(r_eci,r_eci_ecef,&gravity_model,n_max,m_max);println!("\nSpherical harmonic acceleration (degree {}, order {}):",n_max,m_max);println!(" ax = {:.9} m/s²",accel_sh[0]);println!(" ay = {:.9} m/s²",accel_sh[1]);println!(" az = {:.9} m/s²",accel_sh[2]);// Compute point-mass for comparisonletaccel_pm=bh::orbit_dynamics::accel_point_mass_gravity(r_eci,na::Vector3::<f64>::zeros(),bh::constants::GM_EARTH);println!("\nPoint-mass acceleration:");println!(" ax = {:.9} m/s²",accel_pm[0]);println!(" ay = {:.9} m/s²",accel_pm[1]);println!(" az = {:.9} m/s²",accel_pm[2]);// Compute difference (perturbation due to non-spherical Earth)letaccel_pert=accel_sh-accel_pm;println!("\nPerturbation (spherical harmonics - point mass):");println!(" Δax = {:.9} m/s²",accel_pert[0]);println!(" Δay = {:.9} m/s²",accel_pert[1]);println!(" Δaz = {:.9} m/s²",accel_pert[2]);println!(" Magnitude: {:.9} m/s²",accel_pert.norm());}