Astro~/0.4
** Conformance / ODM * PAGE 25 / 30
** Astro * Conformance
** /conformance/odm

Orbit Data Messages

ICS proforma: what this package implements, item by item.

Conformance Statement for pkg/odm, CCSDS 502.0-B-3

CCSDS 502.0-B-3 annex A ships an Implementation Conformance Statement proforma. This fills in all four requirements lists: the Orbit Parameter Message of A2.5.1, the Orbit Mean Elements Message of A2.5.2, the Orbit Ephemeris Message of A2.5.3 and the Orbit Comprehensive Message of A2.5.4.


A2.1 IDENTIFICATION OF ICS

FieldValue
Date of Statement (DD/MM/YYYY)04/09/2026
ICS Serial NumberASTRO-ODM-ICS-001
System Conformance Statement Cross-ReferenceThis document

A2.2 IDENTIFICATION OF IMPLEMENTATION UNDER TEST

FieldValue
Implementation Nameastro/pkg/odm
Implementation VersionSee go.mod / latest commit on main
Special ConfigurationNone
Other InformationGo library reading and writing all four orbit data messages, in both the 'keyword = value' notation of section 7 and the XML form of section 8. No orbital mechanics: nothing propagates, converts frames, interpolates, or derives one element set from another.

A2.3 IDENTIFICATION OF SUPPLIER

FieldValue
SupplierRavi Suhag
Contact Point for QueriesGitHub, github.com/ravisuhag/astro
Implementation Name(s) and Version(s)astro/pkg/odm (Go package)
System Name(s)Astro

A2.4 DOCUMENT VERSIONS

FieldValue
SpecificationCCSDS 502.0-B-3 (Orbit Data Messages, Blue Book, April 2023), also published as ISO 26900
Time formatsCCSDS 301.0-B-4 ASCII time codes A and B, via pkg/tcf
Have any exceptions been required?Yes [X] No [ ], see A2.6

A2.5 XML FORM

FeatureReferenceStatusSupport
Root element and its id and version attributes8.3, 8.8.2–8.8.4MY: one per message type
Schema instance namespace, exactly as given505.0-B-3 clause 4.3.3MY: http, not https — the string names a namespace
Header, body, segment, metadata, data505.0-B-3 clauses 3.2–3.4MY: shared across all four navigation packages
One segment for OPM, OMM and OCM505.0-B-3 clause 3.3MY: the OCM has one metadata section, per clause 6.2.4.3
One or more segments for OEM505.0-B-3 clause 3.4MY
Keyword tags in upper case8.10.9MY
Units as an attribute, matching section 58.10.10, 8.10.11OY
Block elements for the logical blocks8.8.12–8.8.15, 8.9, 8.10.13MY
Each ephemeris line as a named stateVector8.10.14MY
OEM covariance with the OPM's named keywords8.10.19MY
USER_DEFINED with the name in an attribute8.10, annex GOY: a parameter with an empty name attribute is refused, since its key-value form would be the bare USER_DEFINED_
OCM section tags and data line tags8.11.13, table 8-9MY: traj, phys, cov, man, pert, od, user and their trajLine, covLine, manLine
OCM data lines as xsd:string8.11.15MY: kept as rows, split by the reader, as the clause intends
NDM combined instantiation8.12OY: implemented by pkg/ndm, since a combined file may hold messages from other standards too

A2.5.1 Orbit Parameter Message Requirements List

Status is the standard's own: M mandatory, O optional, C conditional.

ItemFeatureKeywordStatusSupport
ODM-1OPM HeaderN/AM
ODM-2OPM VersionCCSDS_OPM_VERSM
ODM-3CommentCOMMENTO
ODM-4Message classificationCLASSIFICATIONO
ODM-5Message creation date and timeCREATION_DATEM
ODM-6Message originatorORIGINATORM
ODM-7Unique message identifierMESSAGE_IDO
ODM-8OPM MetadataN/AM
ODM-9CommentCOMMENTO
ODM-10Name of space objectOBJECT_NAMEM
ODM-11Identifier of space objectOBJECT_IDM
ODM-12Orbit centerCENTER_NAMEM
ODM-13Reference frameREF_FRAMEM
ODM-14Epoch of reference frameREF_FRAME_EPOCHC
ODM-15Time systemTIME_SYSTEMM
ODM-16OPM DataN/AM
ODM-17State Vector logical blockN/AM
ODM-18CommentCOMMENTO
ODM-19Epoch of the state vectorEPOCHM
ODM-20Position componentsX, Y, ZM
ODM-21Velocity componentsX_DOT, Y_DOT, Z_DOTM
ODM-22Keplerian Elements blockN/AO
ODM-23CommentCOMMENTO
ODM-24Semi-major axisSEMI_MAJOR_AXISC
ODM-25EccentricityECCENTRICITYC
ODM-26InclinationINCLINATIONC
ODM-27Right ascension of ascending nodeRA_OF_ASC_NODEC
ODM-28Argument of pericenterARG_OF_PERICENTERC
ODM-29True or mean anomalyTRUE_ANOMALY or MEAN_ANOMALYC
ODM-30Gravitational coefficientGMC
ODM-31Spacecraft Parameters blockN/AO
ODM-32CommentCOMMENTO
ODM-33MassMASSC
ODM-34Solar radiation areaSOLAR_RAD_AREAC
ODM-35Solar radiation coefficientSOLAR_RAD_COEFFC
ODM-36Drag areaDRAG_AREAC
ODM-37Drag coefficientDRAG_COEFFC
ODM-38Position/velocity covariance blockN/AO
ODM-39CommentCOMMENTO
ODM-40Covariance reference frameCOV_REF_FRAMEC
ODM-41Covariance matrix, 21 lower triangular elementsCX_XCZ_DOT_Z_DOTC
ODM-42Maneuver Parameters blockN/AO
ODM-43CommentCOMMENTO
ODM-44Time of maneuver startMAN_EPOCH_IGNITIONO
ODM-45Duration of maneuverMAN_DURATIONO
ODM-46Change of massMAN_DELTA_MASSO
ODM-47Maneuver reference frameMAN_REF_FRAMEO
ODM-48Velocity increment componentsMAN_DV_1, MAN_DV_2, MAN_DV_3O
ODM-49User-Defined Parameters blockN/AO
ODM-50User-defined parameterUSER_DEFINED_xO

A2.5.2 Orbit Mean Elements Message Requirements List

The three paired slots of table 4-3 are the part of this list worth reading. Each accepts two keyword names, which name applies is decided by MEAN_ELEMENT_THEORY, and the two halves carry different units.

ItemFeatureKeywordStatusSupport
OMM HeaderCCSDS_OMM_VERS, COMMENT, CLASSIFICATION, CREATION_DATE, ORIGINATOR, MESSAGE_IDM/OY: same table as the OPM's 3-1
MetadataOBJECT_NAME, OBJECT_ID, CENTER_NAME, REF_FRAME, TIME_SYSTEMMY
Epoch of reference frameREF_FRAME_EPOCHCY
Mean element theoryMEAN_ELEMENT_THEORYMY: also decides which paired keywords apply
EpochEPOCHMY
Orbit sizeSEMI_MAJOR_AXIS or MEAN_MOTIONMY: which arrived is recorded; both is refused
Eccentricity, inclination, RAAN, argument of pericenter, mean anomalyECCENTRICITYMEAN_ANOMALYMY
Gravitational coefficientGMOY: optional here, unlike the OPM
Spacecraft parametersMASSDRAG_COEFFOY: same block as table 3-3
TLE blockEPHEMERIS_TYPE, CLASSIFICATION_TYPE, NORAD_CAT_ID, ELEMENT_SET_NO, REV_AT_EPOCHOY: defaults of 0 and "U" applied per clause 4.2.4.7
Drag termBSTAR or BTERMCY: which arrived is recorded; both is refused
First derivative of mean motionMEAN_MOTION_DOTCY
Second derivative or solar radiationMEAN_MOTION_DDOT or AGOMCY: which arrived is recorded; both is refused
Covariance matrixCOV_REF_FRAME, CX_XCZ_DOT_Z_DOTOY: 21 named keywords, unlike the OEM's positional rows
User-defined parametersUSER_DEFINED_xOY

The four conventions clause 4.2.4.6 fixes for a TLE-based OMM — EARTH, TEME, UTC and MEAN_MOTION — are enforced, and clause 4.2.4.9's converse rule that TEME may be used for nothing else is enforced too.


A2.5.3 Orbit Ephemeris Message Requirements List

ItemFeatureKeywordStatusSupport
ODM-51OEM HeaderN/AMY
ODM-52OEM VersionCCSDS_OEM_VERSMY
ODM-53CommentCOMMENTOY: header comments only immediately after the version keyword
ODM-54Message classificationCLASSIFICATIONOY
ODM-55Message creation date and timeCREATION_DATEMY
ODM-56Message originatorORIGINATORMY
ODM-57Unique message identifierMESSAGE_IDOY
ODM-58Metadata logical blockN/AMY: several per message, per clause 5.2.3.3
ODM-59Start of OEM MetadataMETA_STARTMY
ODM-60CommentCOMMENTOY
ODM-61Name of space objectOBJECT_NAMEMY
ODM-62Identifier of space objectOBJECT_IDMY
ODM-63Orbit centerCENTER_NAMEMY: may be a spacecraft, which table 5-3 allows and the OPM's table does not
ODM-64Reference frameREF_FRAMEMY: clause 3.2.3.3 values not enforced, see A2.6
ODM-65Epoch of reference frameREF_FRAME_EPOCHCY
ODM-66Time systemTIME_SYSTEMMY: a change between groups is refused, per clause 5.2.4.5
ODM-67Start of TOTAL time spanSTART_TIMEMY
ODM-68Start of useable spanUSEABLE_START_TIMEOY: read and preserved, never used to trim, see A2.6
ODM-69End of useable spanUSEABLE_STOP_TIMEOY: as above
ODM-70End of TOTAL time spanSTOP_TIMEMY
ODM-71Recommended interpolation methodINTERPOLATIONOY: carried, not acted on, see A2.6
ODM-72Recommended interpolation degreeINTERPOLATION_DEGREECY: absence alongside a method is refused, per table 5-3
ODM-73End of OEM MetadataMETA_STOPMY
ODM-74OEM Data logical blockN/AMY
ODM-75Ephemeris data linespositionalMY: 7 or 10 fields, per clauses 5.2.4.1 and 5.2.4.2
ODM-76OEM Covariance logical blockN/AOY
ODM-77Start of OEM CovarianceCOVARIANCE_STARTMY
ODM-78Epoch of the covarianceEPOCHCY: required, since it is what separates one matrix from the next
ODM-79Reference frame of the covarianceCOV_REF_FRAMECY: omitted when the same as the ephemeris frame
ODM-80Covariance matrix linespositionalOY: 21 lower triangular values, over any number of lines
ODM-81End of OEM CovarianceCOVARIANCE_STOPMY

A2.5.4 Orbit Comprehensive Message Requirements List

The OCM's eight sections hold something over two hundred keywords, so this lists them by section rather than one row apiece. The full tables are in pkg/odm/ocm_keywords.go, in the order the Blue Book prints them, which is also the order clause 6.2.2.1 requires them to arrive in.

ItemFeatureKeywordStatusSupport
OCM HeaderCCSDS_OCM_VERS, COMMENT, CLASSIFICATION, CREATION_DATE, ORIGINATOR, MESSAGE_IDM/O
Metadata sectionMETA_STARTMETA_STOPM
Metadata keywords48 keywords of table 6-3M/O/C
Spacecraft clock keywordsSCLK_OFFSET_AT_EPOCH, SCLK_SEC_PER_SI_SECC
Trajectory sectionsTRAJ_STARTTRAJ_STOPO
Trajectory keywords18 keywords of table 6-4M/O/C
Trajectory data linespositionalM
Physical characteristics sectionPHYS_STARTPHYS_STOPO
Physical characteristics keywords50 keywords of table 6-5O/C
Covariance sectionsCOV_STARTCOV_STOPO
Covariance keywords13 keywords of table 6-6M/O/C
Covariance data linespositionalM
Covariance orderingsLTM, UTM, FULL, LTMWCC, UTMWCCM
Manoeuvre sectionsMAN_STARTMAN_STOPO
Manoeuvre keywords30 keywords of table 6-7M/O/C
Manoeuvre compositionMAN_COMPOSITIONM
Manoeuvre data linespositionalM
Perturbations sectionPERT_STARTPERT_STOPC
Perturbations keywords29 keywords of table 6-10O
Orbit determination sectionOD_STARTOD_STOPO
Orbit determination keywords29 keywords of table 6-11M/O
User-defined sectionUSER_STARTUSER_STOPO
User-defined parameterUSER_DEFINED_xM
Section ordertable 6-1M
Keyword order within a section6.2.2.1M
Relative or absolute time tags6.2.2.3M
No duplicate time tags in a block6.2.2.4M
One time tag kind per block6.2.2.5M
Monotonic time in trajectory and covariance blocks6.2.5.6, 6.2.7.6M
A message with no data blocks6.2.1.1 noteO

A2.6 EXCEPTIONS AND UNSUPPORTED FEATURES

Both notations are implemented for all four messages: the key-value form of section 7 and the XML form of section 8, with the structure of CCSDS 505.0-B-3. Clause 1.1 leaves the choice to the exchanging parties, so both are needed.

An OCM row's width is not checked, except for a manoeuvre. A trajectory row's columns come from TRAJ_TYPE and a covariance row's from COV_TYPE, and clauses 6.2.5.11 and 6.2.7.12.1 draw both from the SANA registry rather than from the Blue Book. Nothing here says how many numbers a CARTPV row holds, so the rows are carried as text fields and the caller reads the columns. The exception is MAN_COMPOSITION, whose field names are printed in tables 6-8 and 6-9: those are checked, and a manoeuvre row whose width disagrees with its composition is refused.

The Blue Book's own figure G-15 shows why this cannot be tightened. It leaves TRAJ_TYPE out, so the default CARTPV applies, and then prints rows of nine values — a position, a velocity and an acceleration, which is CARTPVA. A reader that trusted the registry would have to refuse a published example.

The LTMWCC and UTMWCC covariance matrices are not made symmetric. Clauses 6.2.7.12.3.4 and 6.2.7.12.3.5 put correlations rather than covariances in one triangle of each, so the matrix is not symmetric. CovMatrix returns those two as they were written. Mirroring them the way an LTM is mirrored would silently scale half the entries by the product of two standard deviations.

An OCM's keywords are not typed. Its sections are held as ordered keyword lists with typed accessors for the keywords that change how the data must be read. There are over two hundred keywords, most optional and most drawn from the SANA registry, so there is nothing to parse a value into and no way for a caller to see an unfamiliar keyword if it were dropped. Get reaches anything; the values are carried as text.

Interpolation is not performed. INTERPOLATION and INTERPOLATION_DEGREE are read, preserved and reported, and nothing here acts on them. Interpolating an ephemeris is orbital mechanics, and clause 5.2.4.6 attaches a rule to it this package cannot enforce on a caller's behalf: a consumer must not interpolate across a metadata group boundary. OEM.Blocks keeps the groups separate so that a caller can respect that; whether it does is the caller's business.

The useable span is not applied. USEABLE_START_TIME and USEABLE_STOP_TIME are read and preserved. Records outside them are not dropped, because table 5-3 makes those bounds advice to the consumer about where a producer padded the data with fictitious interpolation nodes, and silently discarding records would change what the file says.

Enumerated values are not enforced. Clauses 3.2.3.2, 3.2.3.3 and 3.2.4.11 list the expected values for TIME_SYSTEM, REF_FRAME and the manoeuvre and covariance frames, and each says values outside the set "should be documented in an ICD". Refusing them would refuse conforming messages, so an unrecognised value is carried through unchanged and the caller decides.

Registry values are not checked. ORIGINATOR, OBJECT_NAME, OBJECT_ID and CENTER_NAME point at the SANA registries and the UN Office of Outer Space Affairs designator index. Checking them would mean shipping a copy of a registry that changes without this package, and the tables recommend rather than require those sources.

The TLE derivative scaling is not applied. Note 2 under clause 4.2.4.7 says that if MEAN_MOTION_DOT and MEAN_MOTION_DDOT came from a TLE, or are intended to be used as one, they must be divided by 2 and 6 respectively to match the SGP Taylor series terms. Nothing in a message records whether that has been done, so this package carries the values as written and leaves the question to the interface control document.

Nothing is validated against physics. Clause 1.2 puts orbit accuracy outside the standard. A message whose eccentricity exceeds one, or whose position is inside the central body, is structurally valid and is read without complaint.

Clause 7.5.7(b) is not enforced on read. The sub-clause requires a floating-point mantissa to carry its decimal point in the second position, so 1.5E+03 conforms and 15.0E+02 does not. Real messages break this constantly. Reading is lenient; writing produces the conforming form.

Re-encoding does not reproduce the input octets. Clause 7.5.6 makes trailing zeroes optional and clauses 7.4.5 to 7.4.7 make the surrounding white space insignificant, so one message has many spellings. Values round-trip; spelling does not.


A2.7 IMPLEMENTATION LIMITS

LimitValueSource
Line length, OPM, OMM and OEM254 characters
Line length, OCMunbounded
Integer range−2 147 483 648 to 2 147 483 647
Digits in a non-integer value16
Line terminators acceptedCR, LF, CR/LF, LF/CR
Character setPrintable ASCII and blanks
Maneuvers per messagebounded by the input
Ephemeris records per messagebounded by the input
Metadata groups per OEMbounded by the input
Data sections per OCMbounded by the input
Covariance matrix size in an OCMderived from the row

Wire test vectors

The files backing this statement live in the vector corpus — 11 decode vectors and 11 corpus files.

File
odm/messages.json11 vectors
odm/opm-*.kvn, odm/omm-*.kvn, odm/oem-*.kvn, odm/ocm-*.kvnthe annex G examples as readable files
odm/ocm-xml.xmlfigure G-20, the same OCM in the XML form of section 8

Both are published text rather than derived values: annex G of the Blue Book prints them, so a second working group wrote them.

The vectors assert the text and integer content — version, originator, object identifiers, frames, epoch, and the counts. For the OEM and the OCM the counts carry most of the weight: how many metadata groups, how many records, whether a record has acceleration, and how many covariance matrices are what a consumer must read correctly before any single number matters. The same goes for an OCM, whose section shape is what says how to read its rows at all, and whose vectors also assert the defaults clause 6.2.1.3 lets a producer leave out. The numeric state vector is not asserted there, because a vector field has no float accessor and pinning floats as formatted strings would test this package's number formatting rather than the standard. Those values are checked in pkg/odm against the same published text.

See CONTRACT.md for how to consume these, and how this is verified for what rests on a published vector versus a reading of the clause.