The KeplerianPropagator provides fast, analytical two-body orbital propagation using Kepler's equations. It assumes only gravitational attraction from a central body (Earth) with no perturbations, making it ideal for rapid trajectory generation, high-altitude orbits, or when perturbations are negligible.
importbraheasbhimportnumpyasnpbh.initialize_eop()epoch=bh.Epoch.from_datetime(2024,1,1,12,0,0.0,0.0,bh.TimeSystem.UTC)# Define Cartesian state in ECI frame [x, y, z, vx, vy, vz]# Convert from Keplerian elements for this exampleelements=np.array([bh.R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0])state_eci=bh.state_koe_to_eci(elements,bh.AngleFormat.DEGREES)# Create propagator from ECI stateprop=bh.KeplerianPropagator.from_eci(epoch,state_eci,60.0)print(f"Initial position magnitude: {np.linalg.norm(state_eci[:3])/1e3:.1f} km")# Initial position magnitude: 6873.3 km
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();letepoch=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);// Define Cartesian state in ECI frame [x, y, z, vx, vy, vz]// Convert from Keplerian elements for this exampleletelements=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0);letstate_eci=bh::state_koe_to_eci(elements,bh::AngleFormat::Degrees);// Create propagator from ECI statelet_prop=bh::KeplerianPropagator::from_eci(epoch,state_eci,60.0);println!("Initial position magnitude: {:.1} km",state_eci.fixed_rows::<3>(0).norm()/1e3);// Initial position magnitude: 6873.3 km}
Initialize from position and velocity vectors in the Earth-Centered Earth-Fixed (ECEF) frame. The propagator will automatically convert to ECI internally.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Required for ECEF ↔ ECI transformationsepoch=bh.Epoch.from_datetime(2024,1,1,12,0,0.0,0.0,bh.TimeSystem.UTC)# Get state in ECI, then convert to ECEF for demonstrationelements=np.array([bh.R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0])state_eci=bh.state_koe_to_eci(elements,bh.AngleFormat.DEGREES)state_ecef=bh.state_eci_to_ecef(epoch,state_eci)# Create propagator from ECEF stateprop=bh.KeplerianPropagator.from_ecef(epoch,state_ecef,60.0)print(f"ECEF position magnitude: {np.linalg.norm(state_ecef[:3])/1e3:.1f} km")# ECEF position magnitude: 6873.3 km
usebraheasbh;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Required for ECEF ↔ ECI transformationsletepoch=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);// Get state in ECI, then convert to ECEF for demonstrationletelements=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0);letstate_eci=bh::state_koe_to_eci(elements,bh::AngleFormat::Degrees);letstate_ecef=bh::state_eci_to_ecef(epoch,state_eci);// Create propagator from ECEF statelet_prop=bh::KeplerianPropagator::from_ecef(epoch,state_ecef,60.0);println!("ECEF position magnitude: {:.1} km",state_ecef.fixed_rows::<3>(0).norm()/1e3);// ECEF position magnitude: 6873.3 km}
One of the primary functions of propagators is to step forward in time, generating new states at regular intervals. There are several methods to advance the propagator's internal state. Each stepping operation adds new state(s) to the internal trajectory.
The StateProvider trait allows computing states at arbitrary epochs without building a trajectory. This is useful for sparse sampling or parallel batch computation.
importbraheasbhimportnumpyasnpbh.initialize_eop()# Required for frame transformationsepoch=bh.Epoch.from_datetime(2024,1,1,0,0,0.0,0.0,bh.TimeSystem.UTC)elements=np.array([bh.R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0])prop=bh.KeplerianPropagator.from_keplerian(epoch,elements,bh.AngleFormat.DEGREES,60.0)# Query state 1 hour later (doesn't add to trajectory)query_epoch=epoch+3600.0state_native=prop.state(query_epoch)# Native format of propagator internal state (Keplerian)state_eci=prop.state_eci(query_epoch)# ECI Cartesianstate_ecef=prop.state_ecef(query_epoch)# ECEF Cartesianstate_kep=prop.state_koe_osc(query_epoch,bh.AngleFormat.DEGREES)print(f"Native state (Keplerian): a={state_native[0]/1e3:.1f} km")# Native state (Keplerian): a=6878.1 kmprint(f"ECI position magnitude: {np.linalg.norm(state_eci[:3])/1e3:.1f} km")# ECI position magnitude: 6877.7 kmprint(f"ECEF position magnitude: {np.linalg.norm(state_ecef[:3])/1e3:.1f} km")# ECEF position magnitude: 6877.7 km
usebraheasbh;usebh::utils::{DStateProvider,DOrbitStateProvider};usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();// Required for frame transformationsletepoch=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letelements=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0);letprop=bh::KeplerianPropagator::from_keplerian(epoch,elements,bh::AngleFormat::Degrees,60.0);// Query state 1 hour later (doesn't add to trajectory)letquery_epoch=epoch+3600.0;letstate_native=prop.state(query_epoch).unwrap();// Native format of propagator internal state (Keplerian)letstate_eci=prop.state_eci(query_epoch).unwrap();// ECI Cartesianletstate_ecef=prop.state_ecef(query_epoch).unwrap();// ECEF Cartesianlet_state_kep=prop.state_koe_osc(query_epoch,bh::AngleFormat::Degrees).unwrap();println!("Native state (Keplerian): a={:.1} km",state_native[0]/1e3);// Native state (Keplerian): a=6878.1 kmprintln!("ECI position magnitude: {:.1} km",state_eci.fixed_rows::<3>(0).norm()/1e3);// ECI position magnitude: 6877.7 kmprintln!("ECEF position magnitude: {:.1} km",state_ecef.fixed_rows::<3>(0).norm()/1e3);// ECEF position magnitude: 6877.7 km}
importbraheasbhimportnumpyasnpbh.initialize_eop()epoch=bh.Epoch.from_datetime(2024,1,1,0,0,0.0,0.0,bh.TimeSystem.UTC)elements=np.array([bh.R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0])prop=bh.KeplerianPropagator.from_keplerian(epoch,elements,bh.AngleFormat.DEGREES,60.0)# Keep only 100 most recent statesprop.set_eviction_policy_max_size(100)# Propagate many stepsprop.propagate_steps(500)print(f"Trajectory length: {len(prop.trajectory)}")# Will be 100# Trajectory length: 100# Alternative: Keep only states within 1 hour of current timeprop.reset()prop.set_eviction_policy_max_age(3600.0)# 3600 seconds = 1 hourprop.propagate_steps(500)print(f"Trajectory length after age policy: {len(prop.trajectory)}")# Trajectory length after age policy: 61
usebraheasbh;usebh::traits::{SStatePropagator,Trajectory};usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();letepoch=bh::Epoch::from_datetime(2024,1,1,0,0,0.0,0.0,bh::TimeSystem::UTC);letelements=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0);letmutprop=bh::KeplerianPropagator::from_keplerian(epoch,elements,bh::AngleFormat::Degrees,60.0);// Keep only 100 most recent statesprop.set_eviction_policy_max_size(100).unwrap();// Propagate many stepsprop.propagate_steps(500);println!("Trajectory length: {}",prop.trajectory.len());// Will be 100// Trajectory length: 100// Alternative: Keep only states within 1 hour of current timeprop.reset();prop.set_eviction_policy_max_age(3600.0).unwrap();// 3600 seconds = 1 hourprop.propagate_steps(500);println!("Trajectory length after age policy: {}",prop.trajectory.len());// Trajectory length after age policy: 61}
Finally, the IdentifiableStateProvider trait allows you to set and get identity information for the propagator. This can be useful when managing multiple propagators in an application.
Track propagators with names, IDs, or UUIDs for multi-satellite scenarios.