Skip to content

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:

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("\nInitial 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"\nSpacecraft 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"\nEstimated 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("\nAfter 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!("\nInitial 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!(
        "\nSpacecraft 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!("\nEstimated 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!("\nAfter 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
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:

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"\nObject 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"\nEpoch:    {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"\nHas 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"\nMass:           {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"\nManeuvers: {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!("\nObject 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!("\nEpoch:    {}", 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!("\nKeplerian 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!("\nMass:           {} 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!("\nManeuvers: {}", 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
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:

"""

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"\nPropagating to {target}...")
prop.propagate_to(target)

# Report final state
final = prop.current_state()
print(f"\nFinal 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"\nEvent 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!("\nPropagating to {}...", target);
    prop.propagate_to(target);

    // Report final state
    let final_state = prop.current_state();
    println!("\nFinal 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!("\nExample completed successfully!");
}
Output


KVN Format Example

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