CDM — Conjunction Data Message A Conjunction Data Message (CDM) describes a close approach between two space objects, providing state vectors, covariance matrices, and collision probability data at the Time of Closest Approach (TCA). It is the standard format used by conjunction assessment services (e.g., 18th Space Defense Squadron) to communicate collision risk to satellite operators.
Parsing a CDM Parse from KVN, XML, or JSON files and access conjunction data:
Python Rust
from brahe.ccsds import CDM
cdm = CDM . from_file ( "conjunction.cdm" )
# Conjunction-level data
print ( f "TCA: { cdm . tca } " )
print ( f "Miss distance: { cdm . miss_distance } m" )
print ( f "Collision probability: { cdm . collision_probability } " )
# Object states (in meters and m/s)
print ( f "Object 1: { cdm . object1_name } " )
print ( f "Object 1 state: { cdm . object1_state } " )
print ( f "Object 2: { cdm . object2_name } " )
print ( f "Object 2 state: { cdm . object2_state } " )
# Covariance matrix (6x6, in m², m²/s, m²/s²)
cov = cdm . object1_covariance
print ( f "Position variance (R,R): { cov [ 0 ][ 0 ] } m²" )
use brahe :: ccsds :: CDM ;
let cdm = CDM :: from_file ( "conjunction.cdm" ). unwrap ();
println! ( "TCA: {:?}" , cdm . tca ());
println! ( "Miss distance: {} m" , cdm . miss_distance ());
println! ( "Collision probability: {:?}" , cdm . collision_probability ());
// Object states as 6-element vectors [x, y, z, vx, vy, vz]
let s1 = cdm . object1_state ();
let s2 = cdm . object2_state ();
// 6x6 RTN covariance submatrix
let cov = cdm . object1_rtn_covariance_6x6 ();
Creating a CDM Build a CDM programmatically by constructing state vectors, covariance matrices, and object metadata, then combining them into a message:
Python Rust
import numpy as np
import brahe as bh
from brahe.ccsds import CDM , CDMObject , CDMRTNCovariance , CDMStateVector
# Define state vectors at TCA for both objects (meters, m/s)
sv1 = CDMStateVector (
position = [ bh . R_EARTH + 500e3 , 0.0 , 0.0 ],
velocity = [ 0.0 , 7612.0 , 0.0 ],
)
sv2 = CDMStateVector (
position = [ bh . R_EARTH + 500.5e3 , 10.0 , - 5.0 ],
velocity = [ 0.0 , - 7612.0 , 0.0 ],
)
# Define 6x6 RTN covariance matrices (m², m²/s, m²/s²)
cov1 = CDMRTNCovariance ( matrix = ( np . eye ( 6 ) * 1e4 ) . tolist ())
cov2 = CDMRTNCovariance ( matrix = ( np . eye ( 6 ) * 2e4 ) . tolist ())
# Build object metadata + data
obj1 = CDMObject (
designator = "12345" ,
catalog_name = "SATCAT" ,
name = "SATELLITE A" ,
international_designator = "2020-001A" ,
ephemeris_name = "NONE" ,
covariance_method = "CALCULATED" ,
maneuverable = "YES" ,
ref_frame = "EME2000" ,
state_vector = sv1 ,
rtn_covariance = cov1 ,
)
obj2 = CDMObject (
designator = "67890" ,
catalog_name = "SATCAT" ,
name = "DEBRIS FRAGMENT" ,
international_designator = "2019-050ZZ" ,
ephemeris_name = "NONE" ,
covariance_method = "CALCULATED" ,
maneuverable = "NO" ,
ref_frame = "EME2000" ,
state_vector = sv2 ,
rtn_covariance = cov2 ,
)
# Create CDM message
tca = bh . Epoch . from_datetime ( 2024 , 6 , 15 , 14 , 30 , 0.0 , 0.0 , bh . TimeSystem . UTC )
cdm = CDM (
originator = "BRAHE_EXAMPLE" ,
message_id = "CDM-2024-001" ,
tca = tca ,
miss_distance = 502.3 ,
object1 = obj1 ,
object2 = obj2 ,
)
# Set optional collision probability
cdm . collision_probability = 1.5e-04
cdm . collision_probability_method = "FOSTER-1992"
print ( f "CDM: { cdm . object1_name } vs { cdm . object2_name } " )
print ( f "Miss distance: { cdm . miss_distance } m" )
print ( f "Collision probability: { cdm . collision_probability } " )
# Write to KVN
kvn = cdm . to_string ( "KVN" )
print ( f " \n KVN output ( { len ( kvn ) } chars)" )
# Verify round-trip
cdm2 = CDM . from_str ( kvn )
print ( f "Round-trip: { cdm2 . object1_name } vs { cdm2 . object2_name } " )
use brahe as bh ;
use brahe :: ccsds ::{
CCSDSFormat , CCSDSRefFrame , CDM , CDMObject , CDMObjectMetadata , CDMRTNCovariance ,
CDMStateVector ,
};
use nalgebra as na ;
fn main () {
// Define state vectors at TCA for both objects (meters, m/s)
let sv1 = CDMStateVector :: new (
[ bh :: R_EARTH + 500e3 , 0.0 , 0.0 ],
[ 0.0 , 7612.0 , 0.0 ],
);
let sv2 = CDMStateVector :: new (
[ bh :: R_EARTH + 500.5e3 , 10.0 , - 5.0 ],
[ 0.0 , - 7612.0 , 0.0 ],
);
// Define 6x6 RTN covariance matrices (m², m²/s, m²/s²)
let cov1 = CDMRTNCovariance :: from_6x6 ( na :: SMatrix :: < f64 , 6 , 6 > :: identity () * 1e4 );
let cov2 = CDMRTNCovariance :: from_6x6 ( na :: SMatrix :: < f64 , 6 , 6 > :: identity () * 2e4 );
// Build object metadata
let meta1 = CDMObjectMetadata :: new (
"OBJECT1" . to_string (),
"12345" . to_string (),
"SATCAT" . to_string (),
"SATELLITE A" . to_string (),
"2020-001A" . to_string (),
"NONE" . to_string (),
"CALCULATED" . to_string (),
"YES" . to_string (),
CCSDSRefFrame :: EME2000 ,
);
let meta2 = CDMObjectMetadata :: new (
"OBJECT2" . to_string (),
"67890" . to_string (),
"SATCAT" . to_string (),
"DEBRIS FRAGMENT" . to_string (),
"2019-050ZZ" . to_string (),
"NONE" . to_string (),
"CALCULATED" . to_string (),
"NO" . to_string (),
CCSDSRefFrame :: EME2000 ,
);
let obj1 = CDMObject :: new ( meta1 , sv1 , cov1 );
let obj2 = CDMObject :: new ( meta2 , sv2 , cov2 );
// Create CDM message
let tca = bh :: Epoch :: from_datetime ( 2024 , 6 , 15 , 14 , 30 , 0.0 , 0.0 , bh :: TimeSystem :: UTC );
let mut cdm = CDM :: new (
"BRAHE_EXAMPLE" . to_string (),
"CDM-2024-001" . to_string (),
tca ,
502.3 ,
obj1 ,
obj2 ,
);
// Set optional collision probability
cdm . relative_metadata . collision_probability = Some ( 1.5e-04 );
cdm . relative_metadata . collision_probability_method = Some ( "FOSTER-1992" . to_string ());
println! (
"CDM: {} vs {}" ,
cdm . object1 . metadata . object_name , cdm . object2 . metadata . object_name
);
println! ( "Miss distance: {} m" , cdm . miss_distance ());
println! ( "Collision probability: {:?}" , cdm . collision_probability ());
// Write to KVN
let kvn = cdm . to_string ( CCSDSFormat :: KVN ). unwrap ();
println! ( " \n KVN output ({} chars)" , kvn . len ());
// Verify round-trip
let cdm2 = CDM :: from_str ( & kvn ). unwrap ();
println! (
"Round-trip: {} vs {}" ,
cdm2 . object1 . metadata . object_name , cdm2 . object2 . metadata . object_name
);
}
Output What a CDM Contains Every CDM has a header (version, creation date, originator, message ID), relative metadata (TCA, miss distance, optional collision probability and screening volume), and exactly two object sections .
Each object section contains metadata (object identity, reference frame, covariance method, force model info), OD parameters (observation spans, residuals), additional parameters (mass, drag/SRP areas, hard-body radius), a state vector at TCA, and a covariance matrix in the RTN frame.
The covariance matrix is always specified in the Radial-Transverse-Normal (RTN) frame centered on the object. The standard 6\(\times\) 6 matrix covers position and velocity uncertainty. CDM also supports extended 7\(\times\) 7 through 9\(\times\) 9 matrices that include drag coefficient, solar radiation pressure, and thrust uncertainty correlations.
CDM supports three encoding formats:
KVN (.cdm, .txt) — keyword=value text, the most common format from conjunction screening services XML (.xml) — structured XML following the CCSDS NDM XML schema JSON — programmatic convenience format (not in the CCSDS standard) All three formats are auto-detected on parse. Specify the format explicitly when writing:
cdm = CDM . from_file ( "conjunction.cdm" ) # Auto-detect
kvn = cdm . to_string ( "KVN" )
xml = cdm . to_string ( "XML" )
json_str = cdm . to_string ( "JSON" )
See Also