OPM — Orbit Parameter Message An Orbit Parameter Message (OPM) carries a single spacecraft state at one epoch — position, velocity, and optionally Keplerian elements, spacecraft parameters, maneuvers, and covariance. It is the standard format for handing off initial conditions for propagation or documenting a maneuver plan.
Parse and Initialize a Propagator Extract position, velocity, epoch, and spacecraft parameters from an OPM to initialize a NumericalOrbitPropagator:
Python Rust
import brahe as bh
import numpy as np
from brahe.ccsds import OPM
bh . initialize_eop ()
bh . initialize_sw ()
# Parse OPM — use Example1 which has spacecraft mass
opm = OPM . from_file ( "test_assets/ccsds/opm/OPMExample1.txt" )
print ( f "Object: { opm . object_name } ( { opm . object_id } )" )
print ( f "Epoch: { opm . epoch } " )
print ( f "Frame: { opm . ref_frame } " )
# Extract initial conditions from OPM via .state property
initial_state = opm . state # numpy array [x, y, z, vx, vy, vz]
print ( " \n Initial state (ECI):" )
print (
f " Position: [ { initial_state [ 0 ] / 1e3 : .3f } , { initial_state [ 1 ] / 1e3 : .3f } , { initial_state [ 2 ] / 1e3 : .3f } ] km"
)
print (
f " Velocity: [ { initial_state [ 3 ] : .3f } , { initial_state [ 4 ] : .3f } , { initial_state [ 5 ] : .3f } ] m/s"
)
# Build spacecraft parameters from OPM
mass = opm . mass or 500.0
drag_area = opm . drag_area or 2.0
drag_coeff = opm . drag_coeff or 2.2
srp_area = opm . solar_rad_area or 2.0
srp_coeff = opm . solar_rad_coeff or 1.3
params = np . array ([ mass , drag_area , drag_coeff , srp_area , srp_coeff ])
print ( f " \n Spacecraft params: mass= { mass } kg, Cd= { drag_coeff } , Cr= { srp_coeff } " )
# Initialize propagator from OPM state
# Note: OPM frame is ITRF2000; we convert to ECI for propagation
# The propagator expects ECI coordinates
state_eci = bh . state_ecef_to_eci ( opm . epoch , initial_state )
prop = bh . NumericalOrbitPropagator (
opm . epoch ,
state_eci ,
bh . NumericalPropagationConfig . default (),
bh . ForceModelConfig . default (),
params ,
)
# Propagate for 1 orbit period (approximately)
r = np . linalg . norm ( initial_state [: 3 ])
period = 2 * np . pi * np . sqrt ( r ** 3 / bh . GM_EARTH )
print ( f " \n Estimated period: { period : .0f } s ( { period / 60 : .1f } min)" )
target_epoch = opm . epoch + period
prop . propagate_to ( target_epoch )
# Check final state
final_state = prop . current_state ()
print ( " \n After 1 orbit:" )
print ( f " Epoch: { prop . current_epoch () } " )
print (
f " Position: [ { final_state [ 0 ] / 1e3 : .3f } , { final_state [ 1 ] / 1e3 : .3f } , { final_state [ 2 ] / 1e3 : .3f } ] km"
)
print (
f " Velocity: [ { final_state [ 3 ] : .3f } , { final_state [ 4 ] : .3f } , { final_state [ 5 ] : .3f } ] m/s"
)
use brahe as bh ;
use bh :: ccsds :: OPM ;
use bh :: traits :: DStatePropagator ;
use nalgebra as na ;
fn main () {
bh :: initialize_eop (). unwrap ();
bh :: initialize_sw (). unwrap ();
// Parse OPM — use Example1 which has spacecraft mass
let opm = OPM :: from_file ( "test_assets/ccsds/opm/OPMExample1.txt" ). unwrap ();
println! (
"Object: {} ({})" ,
opm . metadata . object_name , opm . metadata . object_id
);
println! ( "Epoch: {}" , opm . state_vector . epoch );
println! ( "Frame: {}" , opm . metadata . ref_frame );
// Extract initial conditions from OPM
let pos = opm . state_vector . position ;
let vel = opm . state_vector . velocity ;
let initial_state =
na :: SVector :: < f64 , 6 > :: new ( pos [ 0 ], pos [ 1 ], pos [ 2 ], vel [ 0 ], vel [ 1 ], vel [ 2 ]);
println! ( " \n Initial state:" );
println! (
" Position: [{:.3}, {:.3}, {:.3}] km" ,
pos [ 0 ] / 1e3 ,
pos [ 1 ] / 1e3 ,
pos [ 2 ] / 1e3
);
println! (
" Velocity: [{:.3}, {:.3}, {:.3}] m/s" ,
vel [ 0 ], vel [ 1 ], vel [ 2 ]
);
// Build spacecraft parameters from OPM
let sc = opm . spacecraft_parameters . as_ref ();
let mass = sc . and_then ( | s | s . mass ). unwrap_or ( 500.0 );
let drag_area = sc . and_then ( | s | s . drag_area ). unwrap_or ( 2.0 );
let drag_coeff = sc . and_then ( | s | s . drag_coeff ). unwrap_or ( 2.2 );
let srp_area = sc . and_then ( | s | s . solar_rad_area ). unwrap_or ( 2.0 );
let srp_coeff = sc . and_then ( | s | s . solar_rad_coeff ). unwrap_or ( 1.3 );
let params = na :: DVector :: from_vec ( vec! [ mass , drag_area , drag_coeff , srp_area , srp_coeff ]);
println! (
" \n Spacecraft params: mass={}kg, Cd={}, Cr={}" ,
mass , drag_coeff , srp_coeff
);
// Convert from ITRF to ECI for propagation
let state_eci = bh :: state_ecef_to_eci ( opm . state_vector . epoch , initial_state );
let state_dyn = na :: DVector :: from_column_slice ( state_eci . as_slice ());
// Initialize propagator from OPM state
let mut prop = bh :: DNumericalOrbitPropagator :: new (
opm . state_vector . epoch ,
state_dyn ,
bh :: NumericalPropagationConfig :: default (),
bh :: ForceModelConfig :: default (),
Some ( params ),
None ,
None ,
None ,
)
. unwrap ();
// Propagate for ~1 orbit period
let r = ( pos [ 0 ]. powi ( 2 ) + pos [ 1 ]. powi ( 2 ) + pos [ 2 ]. powi ( 2 )). sqrt ();
let period = 2.0 * std :: f64 :: consts :: PI * ( r . powi ( 3 ) / bh :: GM_EARTH ). sqrt ();
println! ( " \n Estimated period: {:.0}s ({:.1} min)" , period , period / 60.0 );
let target_epoch = opm . state_vector . epoch + period ;
prop . propagate_to ( target_epoch );
// Check final state
let final_state = prop . current_state ();
println! ( " \n After 1 orbit:" );
println! ( " Epoch: {}" , prop . current_epoch ());
println! (
" Position: [{:.3}, {:.3}, {:.3}] km" ,
final_state [ 0 ] / 1e3 ,
final_state [ 1 ] / 1e3 ,
final_state [ 2 ] / 1e3
);
println! (
" Velocity: [{:.3}, {:.3}, {:.3}] m/s" ,
final_state [ 3 ], final_state [ 4 ], final_state [ 5 ]
);
}
Output Python Rust
Object: GODZILLA 5 (1998-999A)
Epoch: 1998-12-18 14:28:15.117 UTC
Frame: ITRF2000
Initial state (ECI):
Position: [6503.514, 1239.647, -717.490] km
Velocity: [-873.160, 8740.420, -4191.076] m/s
Spacecraft params: mass=3000.0kg, Cd=2.5, Cr=1.0
Estimated period: 5408s (90.1 min)
After 1 orbit:
Epoch: 1998-12-18 15:58:23.416 UTC
Position: [3145.350, 24713.420, -7071.733] km
Velocity: [-2068.116, 3184.124, 57.693] m/s
Object: GODZILLA 5 (1998-999A)
Epoch: 1998-12-18 14:28:15.117 UTC
Frame: ITRF2000
Initial state:
Position: [6503.514, 1239.647, -717.490] km
Velocity: [-873.160, 8740.420, -4191.076] m/s
Spacecraft params: mass=3000kg, Cd=2.5, Cr=1
Estimated period: 5408s (90.1 min)
After 1 orbit:
Epoch: 1998-12-18 15:58:23.416 UTC
Position: [3145.350, 24713.420, -7071.733] km
Velocity: [-2068.116, 3184.124, 57.693] m/s
Accessing OPM Data Parse from file or string, then access the state vector, optional Keplerian elements, spacecraft parameters, covariance, and maneuvers:
Python Rust
import brahe as bh
from brahe.ccsds import OPM
bh . initialize_eop ()
# Parse OPM with Keplerian elements and maneuvers
opm = OPM . from_file ( "test_assets/ccsds/opm/OPMExample2.txt" )
# Header
print ( f "Format version: { opm . format_version } " )
print ( f "Originator: { opm . originator } " )
print ( f "Creation date: { opm . creation_date } " )
# Metadata
print ( f " \n Object name: { opm . object_name } " )
print ( f "Object ID: { opm . object_id } " )
print ( f "Center name: { opm . center_name } " )
print ( f "Ref frame: { opm . ref_frame } " )
print ( f "Time system: { opm . time_system } " )
# State vector (SI units: meters, m/s)
print ( f " \n Epoch: { opm . epoch } " )
pos = opm . position
vel = opm . velocity
print ( f "Position: [ { pos [ 0 ] / 1e3 : .4f } , { pos [ 1 ] / 1e3 : .4f } , { pos [ 2 ] / 1e3 : .4f } ] km" )
print ( f "Velocity: [ { vel [ 0 ] : .8f } , { vel [ 1 ] : .8f } , { vel [ 2 ] : .8f } ] m/s" )
# Keplerian elements
print ( f " \n Has Keplerian: { opm . has_keplerian_elements } " )
if opm . has_keplerian_elements :
print ( f " Semi-major axis: { opm . semi_major_axis / 1e3 : .4f } km" )
print ( f " Eccentricity: { opm . eccentricity : .9f } " )
print ( f " Inclination: { opm . inclination : .6f } deg" )
print ( f " RAAN: { opm . ra_of_asc_node : .6f } deg" )
print ( f " Arg of pericenter: { opm . arg_of_pericenter : .6f } deg" )
print ( f " True anomaly: { opm . true_anomaly : .6f } deg" )
print ( f " GM: { opm . gm : .4e } m³/s²" )
# Spacecraft parameters
print ( f " \n Mass: { opm . mass } kg" )
print ( f "Solar rad area: { opm . solar_rad_area } m²" )
print ( f "Solar rad coef: { opm . solar_rad_coeff } " )
print ( f "Drag area: { opm . drag_area } m²" )
print ( f "Drag coeff: { opm . drag_coeff } " )
# Maneuvers
print ( f " \n Maneuvers: { len ( opm . maneuvers ) } " )
for i , man in enumerate ( opm . maneuvers ):
print ( f " \n Maneuver { i } :" )
print ( f " Epoch ignition: { man . epoch_ignition } " )
print ( f " Duration: { man . duration } s" )
print ( f " Delta mass: { man . delta_mass } kg" )
print ( f " Ref frame: { man . ref_frame } " )
dv = man . dv
print ( f " Delta-V: [ { dv [ 0 ] : .5f } , { dv [ 1 ] : .5f } , { dv [ 2 ] : .5f } ] m/s" )
use brahe as bh ;
use brahe :: ccsds :: OPM ;
fn main () {
bh :: initialize_eop (). unwrap ();
// Parse OPM with Keplerian elements and maneuvers
let opm = OPM :: from_file ( "test_assets/ccsds/opm/OPMExample2.txt" ). unwrap ();
// Header
println! ( "Format version: {}" , opm . header . format_version );
println! ( "Originator: {}" , opm . header . originator );
println! ( "Creation date: {}" , opm . header . creation_date );
// Metadata
println! ( " \n Object name: {}" , opm . metadata . object_name );
println! ( "Object ID: {}" , opm . metadata . object_id );
println! ( "Center name: {}" , opm . metadata . center_name );
println! ( "Ref frame: {}" , opm . metadata . ref_frame );
println! ( "Time system: {}" , opm . metadata . time_system );
// State vector (SI units: meters, m/s)
println! ( " \n Epoch: {}" , opm . state_vector . epoch );
let pos = opm . state_vector . position ;
let vel = opm . state_vector . velocity ;
println! (
"Position: [{:.4}, {:.4}, {:.4}] km" ,
pos [ 0 ] / 1e3 ,
pos [ 1 ] / 1e3 ,
pos [ 2 ] / 1e3
);
println! (
"Velocity: [{:.8}, {:.8}, {:.8}] m/s" ,
vel [ 0 ], vel [ 1 ], vel [ 2 ]
);
// Keplerian elements
if let Some ( ref kep ) = opm . keplerian_elements {
println! ( " \n Keplerian elements:" );
println! ( " Semi-major axis: {:.4} km" , kep . semi_major_axis / 1e3 );
println! ( " Eccentricity: {:.9}" , kep . eccentricity );
println! ( " Inclination: {:.6} deg" , kep . inclination );
println! ( " RAAN: {:.6} deg" , kep . ra_of_asc_node );
println! (
" Arg of pericenter: {:.6} deg" ,
kep . arg_of_pericenter
);
if let Some ( ta ) = kep . true_anomaly {
println! ( " True anomaly: {:.6} deg" , ta );
}
println! ( " GM: {:.4e} m³/s²" , kep . gm . unwrap_or ( 0.0 ));
}
// Spacecraft parameters
if let Some ( ref sc ) = opm . spacecraft_parameters {
println! ( " \n Mass: {} kg" , sc . mass . unwrap_or ( 0.0 ));
println! (
"Solar rad area: {} m²" ,
sc . solar_rad_area . unwrap_or ( 0.0 )
);
println! (
"Solar rad coef: {}" ,
sc . solar_rad_coeff . unwrap_or ( 0.0 )
);
println! ( "Drag area: {} m²" , sc . drag_area . unwrap_or ( 0.0 ));
println! ( "Drag coeff: {}" , sc . drag_coeff . unwrap_or ( 0.0 ));
}
// Maneuvers
println! ( " \n Maneuvers: {}" , opm . maneuvers . len ());
for ( i , man ) in opm . maneuvers . iter (). enumerate () {
println! ( " \n Maneuver {}:" , i );
println! ( " Epoch ignition: {}" , man . epoch_ignition );
println! ( " Duration: {} s" , man . duration );
println! (
" Delta mass: {} kg" ,
man . delta_mass . map_or ( "None" . to_string (), | m | format! ( "{}" , m ))
);
println! ( " Ref frame: {}" , man . ref_frame );
println! (
" Delta-V: [{:.5}, {:.5}, {:.5}] m/s" ,
man . dv [ 0 ], man . dv [ 1 ], man . dv [ 2 ]
);
}
}
Output Python Rust
Format version: 3.0
Originator: GSOC
Creation date: 2000-06-03 05:33:00.000 UTC
Object name: EUTELSAT W4
Object ID: 2000-028A
Center name: EARTH
Ref frame: TOD
Time system: UTC
Epoch: 2006-06-03 00:00:00.000 UTC
Position: [6655.9942, -40218.5751, -82.9177] km
Velocity: [3115.48208000, 470.42605000, -1.01495000] m/s
Has Keplerian: True
Semi-major axis: 41399.5123 km
Eccentricity: 0.020842611
Inclination: 0.117746 deg
RAAN: 17.604721 deg
Arg of pericenter: 218.242943 deg
True anomaly: 41.922339 deg
GM: 3.9860e+14 m³/s²
Mass: 1913.0 kg
Solar rad area: 10.0 m²
Solar rad coef: 1.3
Drag area: 10.0 m²
Drag coeff: 2.3
Maneuvers: 2
Maneuver 0:
Epoch ignition: 2000-06-03 09:00:34.100 UTC
Duration: 132.6 s
Delta mass: -18.418 kg
Ref frame: J2000
Delta-V: [-23.25700, 16.83160, -8.93444] m/s
Maneuver 1:
Epoch ignition: 2000-06-05 18:59:21.000 UTC
Duration: 0.0 s
Delta mass: -1.469 kg
Ref frame: RTN
Delta-V: [1.01500, -1.87300, 0.00000] m/s
Format version: 3
Originator: GSOC
Creation date: 2000-06-03 05:33:00.000 UTC
Object name: EUTELSAT W4
Object ID: 2000-028A
Center name: EARTH
Ref frame: TOD
Time system: UTC
Epoch: 2006-06-03 00:00:00.000 UTC
Position: [6655.9942, -40218.5751, -82.9177] km
Velocity: [3115.48208000, 470.42605000, -1.01495000] m/s
Keplerian elements:
Semi-major axis: 41399.5123 km
Eccentricity: 0.020842611
Inclination: 0.117746 deg
RAAN: 17.604721 deg
Arg of pericenter: 218.242943 deg
True anomaly: 41.922339 deg
GM: 3.9860e14 m³/s²
Mass: 1913 kg
Solar rad area: 10 m²
Solar rad coef: 1.3
Drag area: 10 m²
Drag coeff: 2.3
Maneuvers: 2
Maneuver 0:
Epoch ignition: 2000-06-03 09:00:34.100 UTC
Duration: 132.6 s
Delta mass: -18.418 kg
Ref frame: J2000
Delta-V: [-23.25700, 16.83160, -8.93444] m/s
Maneuver 1:
Epoch ignition: 2000-06-05 18:59:21.000 UTC
Duration: 0 s
Delta mass: -1.469 kg
Ref frame: RTN
Delta-V: [1.01500, -1.87300, 0.00000] m/s
What an OPM Contains Every OPM has a header (version, creation date, originator), metadata (object identity, center body, reference frame, time system), and a state vector (epoch plus position and velocity). Beyond these required parts, four optional sections can be present.
Keplerian elements duplicate the state vector information in orbital-element form — semi-major axis, eccentricity, inclination, RAAN, argument of pericenter, and true or mean anomaly, plus \(GM\) . The redundancy is intentional: elements are easier for humans to review at a glance, and some receiving systems prefer them as input.
Spacecraft parameters record physical properties relevant to force modeling — mass, drag area and coefficient (\(C_D\) ), and solar radiation pressure area and coefficient (\(C_R\) ). These feed directly into atmospheric drag and SRP force models during numerical propagation.
Maneuvers describe planned or executed burns. Each maneuver specifies an ignition epoch, duration, delta-mass, reference frame, and three delta-V components. Multiple maneuvers are allowed, and the reference frame can differ between them (e.g., RTN for in-plane burns, EME2000 for inertial targeting).
Covariance provides a 6\(\times\) 6 symmetric position-velocity covariance matrix with an optional reference frame override relative to the state vector frame.
Maneuver Propagation Read OPM maneuvers and apply them as impulsive delta-V events during propagation:
Python Rust
"""
import brahe as bh
import numpy as np
from brahe.ccsds import OPM
bh.initialize_eop()
bh.initialize_sw()
# Parse OPM with maneuvers
opm = OPM.from_file("test_assets/ccsds/opm/OPMExample2.txt")
print(f"Object: {opm.object_name}")
print(f"Epoch: {opm.epoch}")
print(f"Maneuvers: {len(opm.maneuvers)}")
# Extract initial state (OPM is in TOD frame, convert to ECI)
state_eci = bh.state_ecef_to_eci(opm.epoch, opm.state)
# Spacecraft parameters from OPM
mass = opm.mass or 500.0
params = np.array(
[
mass,
opm.drag_area or 10.0,
opm.drag_coeff or 2.3,
opm.solar_rad_area or 10.0,
opm.solar_rad_coeff or 1.3,
]
)
# Create propagator
prop = bh.NumericalOrbitPropagator(
opm.epoch,
state_eci,
bh.NumericalPropagationConfig.default(),
bh.ForceModelConfig.default(),
params,
)
# Add event detectors for each maneuver with inertial delta-V
for i, man in enumerate(opm.maneuvers):
dv = man.dv # [dvx, dvy, dvz] in m/s in the maneuver's ref frame
frame = man.ref_frame
# For this example, only apply inertial-frame maneuvers (J2000/EME2000)
# RTN maneuvers would require frame rotation which adds complexity
if frame in ("J2000", "EME2000"):
def make_callback(dv_vec, man_idx):
""" Create a closure that applies the delta - V . """
def apply_dv(epoch, state):
new_state = state.copy()
new_state[3] += dv_vec[0]
new_state[4] += dv_vec[1]
new_state[5] += dv_vec[2]
dv_mag = np.linalg.norm(dv_vec)
print(f" Applied maneuver {man_idx} at {epoch} : |dv|= {dv_mag:.3f} m/s")
return (new_state, bh.EventAction.CONTINUE)
return apply_dv
event = bh.TimeEvent(man.epoch_ignition, f"Maneuver- {i} ")
event = event.with_callback(make_callback(dv, i))
prop.add_event_detector(event)
print(
f" Registered maneuver {i} : epoch= {man.epoch_ignition} , frame= {frame} , "
f"|dv|={np.linalg.norm(dv):.3f} m/s"
)
else:
print(f" Skipping maneuver {i} (RTN frame — requires frame rotation)")
# Propagate past all maneuvers
last_man = opm.maneuvers[-1]
target = last_man.epoch_ignition + 3600.0 # 1 hour after last maneuver
print(f" \n Propagating to {target} ...")
prop.propagate_to(target)
# Report final state
final = prop.current_state()
print(f" \n Final state at {prop.current_epoch()}:")
print(
f" Position: [{final[0] / 1e3:.3f}, {final[1] / 1e3:.3f}, {final[2] / 1e3:.3f}] km"
)
print(f" Velocity: [ {final[3]:.3f} , {final[4]:.3f} , {final[5]:.3f} ] m/s")
# Check event log
events = prop.event_log()
print(f" \n Event log: {len(events)} events triggered")
for e in events:
print(f" {e} ")
use brahe as bh ;
use bh :: ccsds :: OPM ;
use bh :: events ::{ DTimeEvent , EventAction };
use bh :: traits :: DStatePropagator ;
use nalgebra as na ;
fn main () {
bh :: initialize_eop (). unwrap ();
bh :: initialize_sw (). unwrap ();
// Parse OPM with maneuvers
let opm = OPM :: from_file ( "test_assets/ccsds/opm/OPMExample2.txt" ). unwrap ();
println! ( "Object: {}" , opm . metadata . object_name );
println! ( "Epoch: {}" , opm . state_vector . epoch );
println! ( "Maneuvers: {}" , opm . maneuvers . len ());
// Extract initial state (OPM is in TOD frame, convert to ECI)
let pos = opm . state_vector . position ;
let vel = opm . state_vector . velocity ;
let initial_state =
na :: SVector :: < f64 , 6 > :: new ( pos [ 0 ], pos [ 1 ], pos [ 2 ], vel [ 0 ], vel [ 1 ], vel [ 2 ]);
let state_eci = bh :: state_ecef_to_eci ( opm . state_vector . epoch , initial_state );
// Spacecraft parameters
let sc = opm . spacecraft_parameters . as_ref ();
let mass = sc . and_then ( | s | s . mass ). unwrap_or ( 500.0 );
let params = na :: DVector :: from_vec ( vec! [
mass ,
sc . and_then ( | s | s . drag_area ). unwrap_or ( 10.0 ),
sc . and_then ( | s | s . drag_coeff ). unwrap_or ( 2.3 ),
sc . and_then ( | s | s . solar_rad_area ). unwrap_or ( 10.0 ),
sc . and_then ( | s | s . solar_rad_coeff ). unwrap_or ( 1.3 ),
]);
// Create propagator
let mut prop = bh :: DNumericalOrbitPropagator :: new (
opm . state_vector . epoch ,
na :: DVector :: from_column_slice ( state_eci . as_slice ()),
bh :: NumericalPropagationConfig :: default (),
bh :: ForceModelConfig :: default (),
Some ( params ),
None ,
None ,
None ,
)
. unwrap ();
// Add event detectors for inertial-frame maneuvers
let mut last_man_epoch = opm . state_vector . epoch ;
for ( i , man ) in opm . maneuvers . iter (). enumerate () {
last_man_epoch = man . epoch_ignition ;
let frame_str = format! ( "{}" , man . ref_frame );
// Only apply inertial-frame maneuvers (J2000/EME2000)
if frame_str == "J2000" || frame_str == "EME2000" {
let dv = man . dv ;
let dv_mag = ( dv [ 0 ]. powi ( 2 ) + dv [ 1 ]. powi ( 2 ) + dv [ 2 ]. powi ( 2 )). sqrt ();
let idx = i ;
let callback : bh :: events :: DEventCallback = Box :: new (
move | _t : bh :: Epoch ,
state : & na :: DVector < f64 > ,
_params : Option <& na :: DVector < f64 >>|
-> ( Option < na :: DVector < f64 >> , Option < na :: DVector < f64 >> , EventAction ) {
let mut new_state = state . clone ();
new_state [ 3 ] += dv [ 0 ];
new_state [ 4 ] += dv [ 1 ];
new_state [ 5 ] += dv [ 2 ];
println! (
" Applied maneuver {}: |dv|={:.3} m/s" ,
idx , dv_mag
);
( Some ( new_state ), None , EventAction :: Continue )
},
);
let event = DTimeEvent :: new ( man . epoch_ignition , format! ( "Maneuver-{}" , i ))
. with_callback ( callback );
prop . add_event_detector ( Box :: new ( event ));
println! (
" Registered maneuver {}: epoch={}, frame={}, |dv|={:.3} m/s" ,
i , man . epoch_ignition , frame_str , dv_mag
);
} else {
println! ( " Skipping maneuver {} (RTN frame)" , i );
}
}
// Propagate past all maneuvers
let target = last_man_epoch + 3600.0 ;
println! ( " \n Propagating to {}..." , target );
prop . propagate_to ( target );
// Report final state
let final_state = prop . current_state ();
println! ( " \n Final state at {}:" , prop . current_epoch ());
println! (
" Position: [{:.3}, {:.3}, {:.3}] km" ,
final_state [ 0 ] / 1e3 ,
final_state [ 1 ] / 1e3 ,
final_state [ 2 ] / 1e3
);
println! (
" Velocity: [{:.3}, {:.3}, {:.3}] m/s" ,
final_state [ 3 ], final_state [ 4 ], final_state [ 5 ]
);
println! ( " \n Example completed successfully!" );
}
Output An OPM KVN file with a state vector, Keplerian elements, and a maneuver:
CCSDS_OPM_VERS = 3.0
CREATION_DATE = 2024-01-15T00:00:00
ORIGINATOR = EXAMPLE
OBJECT_NAME = MY SATELLITE
OBJECT_ID = 2024-001A
CENTER_NAME = EARTH
REF_FRAME = EME2000
TIME_SYSTEM = UTC
EPOCH = 2024-01-15T00:00:00
X = 6878.137 [km]
Y = 0.000 [km]
Z = 0.000 [km]
X_DOT = 0.000 [km/s]
Y_DOT = 7.612 [km/s]
Z_DOT = 0.000 [km/s]
SEMI_MAJOR_AXIS = 6878.137 [km]
ECCENTRICITY = 0.001
INCLINATION = 0.0 [deg]
RA_OF_ASC_NODE = 0.0 [deg]
ARG_OF_PERICENTER = 0.0 [deg]
TRUE_ANOMALY = 0.0 [deg]
GM = 398600.4415 [km**3/s**2]
MAN_EPOCH_IGNITION = 2024-01-15T01:00:00
MAN_DURATION = 60.0 [s]
MAN_DELTA_MASS = -5.0 [kg]
MAN_REF_FRAME = RTN
MAN_DV_1 = 0.010 [km/s]
MAN_DV_2 = 0.000 [km/s]
MAN_DV_3 = 0.000 [km/s]
Note the optional unit annotations in square brackets ([km], [deg]). Brahe strips these during parsing.
See Also