# CCSDS Data Formats

The Consultative Committee for Space Data Systems (CCSDS) defines standard formats for exchanging orbital data between space agencies, satellite operators, and ground systems. Brahe parses and writes all three Orbit Data Message (ODM) types from the [CCSDS 502.0-B-3 standard](https://ccsds.org/Pubs/502x0b3e1.pdf), with automatic format detection and SI unit conversion. The typical workflow is: parse a file, access the data, and feed it into a propagator or trajectory.

## The Core Workflow

The most common pattern is to parse an OEM file — received from another system or generated by a propagator — and convert it into an `OrbitTrajectory` for interpolation and analysis:

```python
import brahe as bh
from brahe.ccsds import OEM

bh.initialize_eop()

# Parse an OEM file
oem = OEM.from_file("test_assets/ccsds/oem/OEMExample5.txt")
seg = oem.segments[0]
print(f"Segment: {seg.object_name}, {seg.num_states} states, frame={seg.ref_frame}")

# Convert segment 0 to an OrbitTrajectory
traj = oem.segment_to_trajectory(0)
print(f"\nTrajectory: {len(traj)} states")
print(f"  Frame: {traj.frame}")
print(f"  Start: {traj.start_epoch()}")
print(f"  End:   {traj.end_epoch()}")
print(f"  Span:  {traj.timespan():.0f} seconds")

# Access states by index
epc, state = traj.get(0)
print("\nFirst state:")
print(f"  Epoch: {epc}")
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")

# Interpolate at an arbitrary epoch between states
epc0, _ = traj.get(0)
epc1, _ = traj.get(1)
mid_epoch = epc0 + (epc1 - epc0) / 2.0
interp_state = traj.interpolate(mid_epoch)
print(f"\nInterpolated state at {mid_epoch}:")
print(
    f"  Position: [{interp_state[0] / 1e3:.3f}, {interp_state[1] / 1e3:.3f}, {interp_state[2] / 1e3:.3f}] km"
)

# Convert all segments at once
oem_multi = OEM.from_file("test_assets/ccsds/oem/OEMExample1.txt")
trajs = oem_multi.to_trajectories()
print(f"\nMulti-segment OEM: {len(trajs)} trajectories")
for i, t in enumerate(trajs):
    print(f"  [{i}] {len(t)} states, span={t.timespan():.0f}s")
```


This three-step pattern — parse, convert, interpolate — applies across the library. OEM data becomes trajectories, OMM data feeds SGP4 propagators, and OPM data initializes numerical propagators.

## Choosing a Message Type

**OEM** (Orbit Ephemeris Message) carries time-ordered state vectors and is the standard format for ephemeris exchange. Use it when you have or want to produce a full trajectory — conjunction screening, trajectory handoffs between teams, or archiving definitive ephemerides. See the [OEM detail page](oem.md).

**OMM** (Orbit Mean-elements Message) is the CCSDS-standardized representation of TLE/GP data. Data sources like CelesTrak and Space-Track distribute orbital elements as OMM, making it the modern successor to fixed-width TLE format. Use it when working with GP data and SGP4 propagation. See the [OMM detail page](omm.md).

**OPM** (Orbit Parameter Message) carries a single state snapshot at one epoch, optionally with Keplerian elements, spacecraft parameters, covariance, and maneuvers. Use it when handing off initial conditions for propagation or documenting a maneuver plan. See the [OPM detail page](opm.md).

**CDM** (Conjunction Data Message) describes a close approach between two space objects, providing state vectors, covariance matrices, and collision probability at the Time of Closest Approach (TCA). Use it when processing conjunction screening data from services like the 18th Space Defense Squadron. See the [CDM detail page](cdm.md).

**OCM Not Yet Supported**
The Orbit Comprehensive Message (OCM) is not yet implemented. OCM combines features of OEM, OMM, and OPM into a single flexible format and will be added in a future release.

## Format Detection

Each message type can be encoded as KVN (`.oem`, `.omm`, `.opm`, `.txt`), XML (`.xml`), or JSON (`.json`). Brahe auto-detects the format when parsing by examining the first non-whitespace character of the content:

- Starts with `<` or `<?xml` → XML
- Starts with `{` or `[` → JSON
- Otherwise → KVN

## Unit Conventions

CCSDS files use **km** and **km/s** for position and velocity. Brahe automatically converts these to **SI base units** (meters, m/s) on parse, and converts back when writing. This means:

- All position values returned by brahe are in **meters**
- All velocity values returned by brahe are in **m/s**
- Covariance matrices are in **m$^2$**, **m$^2$/s**, and **m$^2$/s$^2$**
- Angles in OMM and OPM Keplerian elements remain in **degrees** (matching CCSDS/TLE convention)
- GM values are converted from km$^3$/s$^2$ to **m$^3$/s$^2$**

**Unit Conversion**
If you compare brahe output directly to values in a CCSDS file, remember the factor of 1000 for position/velocity and 10$^9$ for GM. Covariance elements scale by 10$^6$ (km$^2$ → m$^2$).

---

## See Also

- [OEM — Orbit Ephemeris Message](oem.md)
- [OMM — Orbit Mean-elements Message](omm.md)
- [OPM — Orbit Parameter Message](opm.md)
- [CDM — Conjunction Data Message](cdm.md)
- [API Reference](../../library_api/ccsds/index.md) — Python API documentation