Skip to content

OMM — Orbit Mean-elements Message

An Orbit Mean-elements Message (OMM) is the CCSDS-standardized representation of TLE/GP data — the same orbital elements traditionally distributed as Two-Line Element sets, in a structured, self-describing format. Data sources like CelesTrak and Space-Track distribute GP data as OMM. The typical workflow is to parse an OMM and initialize an SGP4 propagator.

Parse and Propagate with SGP4

Extract OMM mean elements and TLE parameters to create an SGPPropagator:

import brahe as bh
from brahe.ccsds import OMM

bh.initialize_eop()

# Parse OMM
omm = OMM.from_file("test_assets/ccsds/omm/OMMExample1.txt")
print(f"Object: {omm.object_name} ({omm.object_id})")
print(f"Theory: {omm.mean_element_theory}")
print(f"Epoch:  {omm.epoch}")

# Extract mean elements for SGP4
# The epoch string is needed in ISO format for from_omm_elements
d = omm.to_dict()
epoch_str = d["mean_elements"]["epoch"]

# Initialize SGP propagator from OMM elements
prop = bh.SGPPropagator.from_omm_elements(
    epoch=epoch_str,
    mean_motion=omm.mean_motion,
    eccentricity=omm.eccentricity,
    inclination=omm.inclination,
    raan=omm.ra_of_asc_node,
    arg_of_pericenter=omm.arg_of_pericenter,
    mean_anomaly=omm.mean_anomaly,
    norad_id=omm.norad_cat_id,
    object_name=omm.object_name,
    object_id=omm.object_id,
    classification=omm.classification_type,
    bstar=omm.bstar,
    mean_motion_dot=omm.mean_motion_dot,
    mean_motion_ddot=omm.mean_motion_ddot,
    ephemeris_type=omm.ephemeris_type,
    element_set_no=omm.element_set_no,
    rev_at_epoch=omm.rev_at_epoch,
)

print("\nSGP Propagator created:")
print(f"  NORAD ID: {prop.norad_id}")
print(f"  Name:     {prop.satellite_name}")
print(f"  Epoch:    {prop.epoch}")

# Propagate 1 day forward
target = prop.epoch + 86400.0
state = prop.state(target)
print(f"\nState after 1 day ({target}):")
print(
    f"  Position: [{state[0] / 1e3:.3f}, {state[1] / 1e3:.3f}, {state[2] / 1e3:.3f}] km"
)
print(f"  Velocity: [{state[3]:.3f}, {state[4]:.3f}, {state[5]:.3f}] m/s")

# Propagate to several epochs
print("\nState every 6 hours:")
for hours in range(0, 25, 6):
    t = prop.epoch + hours * 3600.0
    s = prop.state(t)
    r = (s[0] ** 2 + s[1] ** 2 + s[2] ** 2) ** 0.5
    print(f"  +{hours:2d}h: r={r / 1e3:.1f} km")
use brahe as bh;
use brahe::ccsds::OMM;
use brahe::traits::SStateProvider;

fn main() {
    bh::initialize_eop().unwrap();

    // Parse OMM
    let omm = OMM::from_file("test_assets/ccsds/omm/OMMExample1.txt").unwrap();
    println!(
        "Object: {} ({})",
        omm.metadata.object_name, omm.metadata.object_id
    );
    println!("Theory: {}", omm.metadata.mean_element_theory);
    println!("Epoch:  {}", omm.mean_elements.epoch);

    // Format epoch as ISO string for from_omm_elements
    let epoch_str = bh::ccsds::common::format_ccsds_datetime(&omm.mean_elements.epoch);

    // Extract TLE parameters
    let tle = omm.tle_parameters.as_ref().unwrap();

    // Initialize SGP propagator from OMM elements
    let prop = bh::SGPPropagator::from_omm_elements(
        &epoch_str,
        omm.mean_elements.mean_motion.unwrap(),
        omm.mean_elements.eccentricity,
        omm.mean_elements.inclination,
        omm.mean_elements.ra_of_asc_node,
        omm.mean_elements.arg_of_pericenter,
        omm.mean_elements.mean_anomaly,
        tle.norad_cat_id.unwrap() as u64,
        60.0, // step_size
        Some(omm.metadata.object_name.as_str()),
        Some(omm.metadata.object_id.as_str()),
        tle.classification_type,
        tle.bstar,
        tle.mean_motion_dot,
        tle.mean_motion_ddot,
        tle.ephemeris_type.map(|v| v as u8),
        tle.element_set_no.map(|v| v as u64),
        tle.rev_at_epoch.map(|v| v as u64),
    )
    .unwrap();

    println!("\nSGP Propagator created:");
    println!("  NORAD ID: {}", prop.norad_id);
    println!(
        "  Name:     {}",
        prop.satellite_name.as_deref().unwrap_or_default()
    );
    println!("  Epoch:    {}", prop.epoch);

    // Propagate 1 day forward
    let target = prop.epoch + 86400.0;
    let state = prop.state(target).unwrap();
    println!("\nState after 1 day ({}):", target);
    println!(
        "  Position: [{:.3}, {:.3}, {:.3}] km",
        state[0] / 1e3,
        state[1] / 1e3,
        state[2] / 1e3
    );
    println!(
        "  Velocity: [{:.3}, {:.3}, {:.3}] m/s",
        state[3], state[4], state[5]
    );

    // Propagate to several epochs
    println!("\nState every 6 hours:");
    for hours in (0..=24).step_by(6) {
        let t = prop.epoch + hours as f64 * 3600.0;
        let s = prop.state(t).unwrap();
        let r = (s[0].powi(2) + s[1].powi(2) + s[2].powi(2)).sqrt();
        println!("  +{:2}h: r={:.1} km", hours, r / 1e3);
    }
}
Output
Object: GOES 9 (1995-025A)
Theory: SGP/SGP4
Epoch:  2007-03-05 10:34:41.426 UTC

SGP Propagator created:
  NORAD ID: 23581
  Name:     GOES 9
  Epoch:    2007-03-05 10:34:41.426 UTC

State after 1 day (2007-03-06 10:34:41.426 UTC):
  Position: [-22455.711, 35678.856, 1446.264] km
  Velocity: [-2597.006, -1638.752, 125.747] m/s

State every 6 hours:
  + 0h: r=42181.9 km
  + 6h: r=42174.0 km
  +12h: r=42145.6 km
  +18h: r=42153.8 km
  +24h: r=42182.1 km
Object: GOES 9 (1995-025A)
Theory: SGP/SGP4
Epoch:  2007-03-05 10:34:41.426 UTC

SGP Propagator created:
  NORAD ID: 23581
  Name:     GOES 9
  Epoch:    2007-03-05 10:34:41.426 UTC

State after 1 day (2007-03-06 10:34:41.426 UTC):
  Position: [-22455.711, 35678.856, 1446.264] km
  Velocity: [-2597.006, -1638.752, 125.747] m/s

State every 6 hours:
  + 0h: r=42181.9 km
  + 6h: r=42174.0 km
  +12h: r=42145.6 km
  +18h: r=42153.8 km
  +24h: r=42182.1 km

Accessing Mean Elements and TLE Parameters

Parse from file or string, then access metadata, mean elements, and TLE parameters. The message carries two main data sections: mean elements (epoch, mean motion, eccentricity, inclination, RAAN, argument of pericenter, mean anomaly) and TLE parameters (NORAD catalog ID, classification, element set number, revolution count, \(B^*\) drag term, mean motion derivatives):

import brahe as bh
from brahe.ccsds import OMM

bh.initialize_eop()

# Parse OMM file
omm = OMM.from_file("test_assets/ccsds/omm/OMMExample1.txt")

# Header
print(f"Format version: {omm.format_version}")
print(f"Originator:     {omm.originator}")
print(f"Creation date:  {omm.creation_date}")

# Metadata
print(f"\nObject name:          {omm.object_name}")
print(f"Object ID:            {omm.object_id}")
print(f"Center name:          {omm.center_name}")
print(f"Ref frame:            {omm.ref_frame}")
print(f"Time system:          {omm.time_system}")
print(f"Mean element theory:  {omm.mean_element_theory}")

# Mean orbital elements (CCSDS/TLE-native units)
print(f"\nEpoch:               {omm.epoch}")
print(f"Mean motion:         {omm.mean_motion} rev/day")
print(f"Eccentricity:        {omm.eccentricity}")
print(f"Inclination:         {omm.inclination} deg")
print(f"RAAN:                {omm.ra_of_asc_node} deg")
print(f"Arg of pericenter:   {omm.arg_of_pericenter} deg")
print(f"Mean anomaly:        {omm.mean_anomaly} deg")
print(f"GM:                  {omm.gm:.4e} m³/s²")

# TLE parameters
print(f"\nNORAD catalog ID:    {omm.norad_cat_id}")
print(f"Classification:      {omm.classification_type}")
print(f"Ephemeris type:      {omm.ephemeris_type}")
print(f"Element set no:      {omm.element_set_no}")
print(f"Rev at epoch:        {omm.rev_at_epoch}")
print(f"BSTAR:               {omm.bstar}")
print(f"Mean motion dot:     {omm.mean_motion_dot} rev/day²")
print(f"Mean motion ddot:    {omm.mean_motion_ddot} rev/day³")

# Serialization
d = omm.to_dict()
print(f"\nDict keys: {list(d.keys())}")
use brahe as bh;
use brahe::ccsds::OMM;

fn main() {
    bh::initialize_eop().unwrap();

    // Parse OMM file
    let omm = OMM::from_file("test_assets/ccsds/omm/OMMExample1.txt").unwrap();

    // Header
    println!("Format version: {}", omm.header.format_version);
    println!("Originator:     {}", omm.header.originator);
    println!("Creation date:  {}", omm.header.creation_date);

    // Metadata
    println!("\nObject name:          {}", omm.metadata.object_name);
    println!("Object ID:            {}", omm.metadata.object_id);
    println!("Center name:          {}", omm.metadata.center_name);
    println!("Ref frame:            {}", omm.metadata.ref_frame);
    println!("Time system:          {}", omm.metadata.time_system);
    println!(
        "Mean element theory:  {}",
        omm.metadata.mean_element_theory
    );

    // Mean orbital elements (CCSDS/TLE-native units)
    println!("\nEpoch:               {}", omm.mean_elements.epoch);
    println!(
        "Mean motion:         {} rev/day",
        omm.mean_elements.mean_motion.unwrap_or(0.0)
    );
    println!("Eccentricity:        {}", omm.mean_elements.eccentricity);
    println!("Inclination:         {} deg", omm.mean_elements.inclination);
    println!(
        "RAAN:                {} deg",
        omm.mean_elements.ra_of_asc_node
    );
    println!(
        "Arg of pericenter:   {} deg",
        omm.mean_elements.arg_of_pericenter
    );
    println!("Mean anomaly:        {} deg", omm.mean_elements.mean_anomaly);
    println!(
        "GM:                  {:.4e} m³/s²",
        omm.mean_elements.gm.unwrap_or(0.0)
    );

    // TLE parameters
    if let Some(ref tle) = omm.tle_parameters {
        println!(
            "\nNORAD catalog ID:    {}",
            tle.norad_cat_id.unwrap_or(0)
        );
        println!(
            "Classification:      {}",
            tle.classification_type.unwrap_or('U')
        );
        println!("Ephemeris type:      {}", tle.ephemeris_type.unwrap_or(0));
        println!(
            "Element set no:      {}",
            tle.element_set_no.unwrap_or(0)
        );
        println!("Rev at epoch:        {}", tle.rev_at_epoch.unwrap_or(0));
        println!("BSTAR:               {}", tle.bstar.unwrap_or(0.0));
        println!(
            "Mean motion dot:     {} rev/day²",
            tle.mean_motion_dot.unwrap_or(0.0)
        );
        println!(
            "Mean motion ddot:    {} rev/day³",
            tle.mean_motion_ddot.unwrap_or(0.0)
        );
    }

    println!("\nParsing completed successfully.");
}
Output
Format version: 3.0
Originator:     NOAA/USA
Creation date:  2007-03-06 16:00:00.000 UTC

Object name:          GOES 9
Object ID:            1995-025A
Center name:          EARTH
Ref frame:            TEME
Time system:          UTC
Mean element theory:  SGP/SGP4

Epoch:               2007-03-05 10:34:41.426 UTC
Mean motion:         1.00273272 rev/day
Eccentricity:        0.0005013
Inclination:         3.0539 deg
RAAN:                81.7939 deg
Arg of pericenter:   249.2363 deg
Mean anomaly:        150.1602 deg
GM:                  3.9860e+14 m³/s²

NORAD catalog ID:    23581
Classification:      U
Ephemeris type:      0
Element set no:      925
Rev at epoch:        4316
BSTAR:               0.0001
Mean motion dot:     -1.13e-06 rev/day²
Mean motion ddot:    0.0 rev/day³

Dict keys: ['header', 'metadata', 'mean_elements', 'tle_parameters']
Format version: 3
Originator:     NOAA/USA
Creation date:  2007-03-06 16:00:00.000 UTC

Object name:          GOES 9
Object ID:            1995-025A
Center name:          EARTH
Ref frame:            TEME
Time system:          UTC
Mean element theory:  SGP/SGP4

Epoch:               2007-03-05 10:34:41.426 UTC
Mean motion:         1.00273272 rev/day
Eccentricity:        0.0005013
Inclination:         3.0539 deg
RAAN:                81.7939 deg
Arg of pericenter:   249.2363 deg
Mean anomaly:        150.1602 deg
GM:                  3.9860e14 m³/s²

NORAD catalog ID:    23581
Classification:      U
Ephemeris type:      0
Element set no:      925
Rev at epoch:        4316
BSTAR:               0.0001
Mean motion dot:     -0.00000113 rev/day²
Mean motion ddot:    0 rev/day³

Parsing completed successfully.

Unit Convention for OMM

Mean motion, angles, and TLE drag terms are kept in their CCSDS/TLE-native units (rev/day, degrees, etc.) because these values are needed as-is for TLE generation and SGP4 initialization. Only GM is converted to SI (m\(^3\)/s\(^2\)).

OMM and GPRecord

Brahe's GPRecord type — returned by both CelestrakClient and SpaceTrackClient when querying GP data — has a bidirectional relationship with OMM. A GPRecord can be converted to an OMM via to_omm() for CCSDS-compliant export, and an OMM can be converted to a GPRecord via to_gp_record() for use with brahe's ephemeris infrastructure.

This means you can move freely between the two representations: query CelesTrak for a satellite, get a GPRecord, and export it as a standards-compliant OMM file for distribution. Or parse an OMM file received from an external system and convert it to a GPRecord to use the same downstream code you would with a CelesTrak or Space-Track query. Both conversions preserve all shared fields, so switching between formats introduces no data loss.

KVN Format Example

A minimal OMM KVN file:

CCSDS_OMM_VERS = 3.0
CREATION_DATE = 2024-01-15T00:00:00
ORIGINATOR = EXAMPLE

OBJECT_NAME = ISS (ZARYA)
OBJECT_ID = 1998-067A
CENTER_NAME = EARTH
REF_FRAME = TEME
TIME_SYSTEM = UTC
MEAN_ELEMENT_THEORY = SGP/SGP4

EPOCH = 2024-01-15T12:00:00
MEAN_MOTION = 15.50100000
ECCENTRICITY = 0.0006180
INCLINATION = 51.6413
RA_OF_ASC_NODE = 289.5820
ARG_OF_PERICENTER = 36.5102
MEAN_ANOMALY = 323.6298

EPHEMERIS_TYPE = 0
CLASSIFICATION_TYPE = U
NORAD_CAT_ID = 25544
ELEMENT_SET_NO = 999
REV_AT_EPOCH = 43210
BSTAR = 0.000035000
MEAN_MOTION_DOT = 0.00001200
MEAN_MOTION_DDOT = 0.0

Note that OMM KVN does not use META_START/META_STOP markers — all keywords appear in a flat sequence.


See Also