This guide covers the fundamental operations of the NumericalOrbitPropagator: creating propagators, stepping through time, accessing states, and managing trajectories.
The NumericalOrbitPropagator requires an initial epoch, state, propagation configuration, force model configuration, and optional parameters. The propagator state vector follows a standard layout with the 6D orbital state in the first elements:
State Vector (6+ elements)
├── [0] x - Position X (m, ECI)
├── [1] y - Position Y (m, ECI)
├── [2] z - Position Z (m, ECI)
├── [3] vx - Velocity X (m/s, ECI)
├── [4] vy - Velocity Y (m/s, ECI)
├── [5] vz - Velocity Z (m/s, ECI)
└── [6+] - Extended state (optional, if user-defined additional_dynamics set)
All force models read from the first 6 elements and contribute accelerations to indices 3-5. Extended state elements (index 6+) are available for user-defined dynamics such as mass depletion, battery state, attitude dynamics, or other user-defined states.
importnumpyasnpimportbraheasbh# Initialize EOP and space weather data (required for NRLMSISE-00 drag model)bh.initialize_eop()bh.initialize_sw()# Create initial epochepoch=bh.Epoch.from_datetime(2024,1,1,12,0,0.0,0.0,bh.TimeSystem.UTC)# Define orbital elements: [a, e, i, raan, argp, M] in SI units# LEO satellite: 500 km altitude, near-circular, sun-synchronous inclinationoe=np.array([bh.R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0])state=bh.state_koe_to_eci(oe,bh.AngleFormat.DEGREES)# Parameters: [mass, drag_area, Cd, srp_area, Cr]params=np.array([500.0,2.0,2.2,2.0,1.3])# Create propagator with default configurationprop=bh.NumericalOrbitPropagator(epoch,state,bh.NumericalPropagationConfig.default(),bh.ForceModelConfig.default(),params,)# Propagate for 1 hourprop.propagate_to(epoch+3600.0)# Get final statefinal_epoch=prop.current_epoch()final_state=prop.current_state()# Validate propagation completedassertfinal_epoch==epoch+3600.0assertlen(final_state)==6assertnp.linalg.norm(final_state[:3])>bh.R_EARTH# Still in orbitprint(f"Initial epoch: {epoch}")print(f"Final epoch: {final_epoch}")print(f"Position (km): [{final_state[0]/1e3:.3f}, {final_state[1]/1e3:.3f}, {final_state[2]/1e3:.3f}]")print(f"Velocity (m/s): [{final_state[3]:.3f}, {final_state[4]:.3f}, {final_state[5]:.3f}]")print("Example validated successfully!")
usebraheasbh;usebh::traits::DStatePropagator;usenalgebraasna;fnmain(){// Initialize EOP and space weather data (required for NRLMSISE-00 drag model)bh::initialize_eop().unwrap();bh::initialize_sw().unwrap();// Create initial epochletepoch=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);// Define orbital elements: [a, e, i, raan, argp, M] in SI units// LEO satellite: 500 km altitude, near-circular, sun-synchronous inclinationletoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0,);letstate=bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees);// Parameters: [mass, drag_area, Cd, srp_area, Cr]letparams=na::DVector::from_vec(vec![500.0,2.0,2.2,2.0,1.3]);// Create propagator with default configurationletmutprop=bh::DNumericalOrbitPropagator::new(epoch,na::DVector::from_column_slice(state.as_slice()),bh::NumericalPropagationConfig::default(),bh::ForceModelConfig::default(),Some(params),None,// No additional dynamicsNone,// No control inputNone,// No initial covariance).unwrap();// Propagate for 1 hourprop.propagate_to(epoch+3600.0);// Get final stateletfinal_epoch=prop.current_epoch();letfinal_state=prop.current_state();// Validate propagation completedassert_eq!(final_epoch,epoch+3600.0);assert_eq!(final_state.len(),6);assert!(final_state.fixed_rows::<3>(0).norm()>bh::R_EARTH);println!("Initial epoch: {}",epoch);println!("Final epoch: {}",final_epoch);println!("Position (km): [{:.3}, {:.3}, {:.3}]",final_state[0]/1e3,final_state[1]/1e3,final_state[2]/1e3);println!("Velocity (m/s): [{:.3}, {:.3}, {:.3}]",final_state[3],final_state[4],final_state[5]);println!("Example validated successfully!");}
Initial epoch: 2024-01-01 12:00:00.000 UTC
Final epoch: 2024-01-01 13:00:00.000 UTC
Position (km): [3351.511, 1720.297, -5776.689]
Velocity (m/s): [6336.225, 1132.109, 4031.027]
Example validated successfully!
Initial epoch: 2024-01-01 12:00:00.000 UTC
Final epoch: 2024-01-01 13:00:00.000 UTC
Position (km): [3351.511, 1720.297, -5776.689]
Velocity (m/s): [6336.225, 1132.109, 4031.027]
Example validated successfully!
DNumericalOrbitPropagator::builder() is a idiomatic Rust alternative to new() that lets you name only the fields you care about and omit the rest. This is particularly useful when only a subset of optional fields are needed, keeping construction readable without sacrificing access to the full configuration surface.
//! nalgebra = "0.33"//! ```//! Constructing a NumericalOrbitPropagator using the typestate builder API.//!//! The builder enforces at compile time that all three required fields — epoch,//! state, and force_config — are set before build() is available. Optional//! fields such as initial_covariance default to None when omitted.usebraheasbh;usebh::traits::DStatePropagator;usenalgebraasna;fnmain(){bh::initialize_eop().unwrap();letepoch=bh::Epoch::from_datetime(2024,1,1,12,0,0.0,0.0,bh::TimeSystem::UTC);letoe=na::SVector::<f64,6>::new(bh::R_EARTH+500e3,0.001,97.8,15.0,30.0,45.0);letstate=na::DVector::from_column_slice(bh::state_koe_to_eci(oe,bh::AngleFormat::Degrees).as_slice(),);// Minimal: only the three required fieldsletmutprop=bh::DNumericalOrbitPropagator::builder().epoch(epoch).state(state.clone()).force_config(bh::ForceModelConfig::earth_gravity()).build().unwrap();prop.propagate_to(epoch+3600.0);println!("Minimal builder — epoch: {}",prop.current_epoch());// With optional fields: custom propagation config and initial covarianceletp0=na::DMatrix::<f64>::identity(6,6)*1e6;letmutprop_with_cov=bh::DNumericalOrbitPropagator::builder().epoch(epoch).state(state).force_config(bh::ForceModelConfig::earth_gravity()).propagation_config(bh::NumericalPropagationConfig::high_precision()).initial_covariance(p0).build().unwrap();prop_with_cov.propagate_to(epoch+3600.0);assert_eq!(DStatePropagator::state_dim(&prop_with_cov),6);assert!(prop_with_cov.current_covariance().is_some());println!("Builder with covariance — epoch: {}",prop_with_cov.current_epoch());println!("Example validated successfully!");}
The StateProvider trait enables state queries at any epoch:
state(epoch) - State in the propagator's native format
state_eci(epoch) - Cartesian state in ECI frame
state_ecef(epoch) - Cartesian state in ECEF frame
state_koe_osc(epoch, angle_format) - Keplerian orbital elements
Propagator Advancement and State Queries
When querying states at epochs beyond the current propagated time, the propagator MUST have already been advanced to at least that epoch using one of the propagation methods, such as propagate_to() or step_past(), before calling the state query methods otherwise an error will be raised.
For epochs within the propagated trajectory, interpolation is used. For epochs beyond the trajectory, the propagator advances to that epoch.