Astro~/0.4
** Conformance / ADM * PAGE 28 / 30
** Astro * Conformance
** /conformance/adm

Attitude Data Messages

Coverage matrix: what this package implements, clause by clause.

Conformance Statement for pkg/adm, CCSDS 504.0-B-2

All three messages are implemented: the Attitude Parameter Message of section 3, the Attitude Ephemeris Message of section 4 and the Attitude Comprehensive Message of section 5, each in both the key-value notation of section 6 and the XML form of section 7.


A1 IDENTIFICATION

FieldValue
Date of Statement (DD/MM/YYYY)04/09/2026
ICS Serial NumberASTRO-ADM-ICS-001
Implementation Nameastro/pkg/adm
Implementation VersionSee go.mod / latest commit on main
SupplierRavi Suhag
Contact Point for QueriesGitHub, github.com/ravisuhag/astro
SpecificationCCSDS 504.0-B-2 (Attitude Data Messages, Blue Book, January 2024)
Time formatsCCSDS 301.0-B-4 ASCII time codes A and B, via pkg/tcf
Have any exceptions been required?Yes [X] No [ ], see A3

A2 REQUIREMENTS

APM, section 3

FeatureReferenceStatusSupport
Header keywordsTable 3-1M/OY: including CLASSIFICATION, which the TDM's table lacks
Metadata: OBJECT_NAME, OBJECT_ID, TIME_SYSTEMTable 3-2MY
Metadata: CENTER_NAMETable 3-2OY: optional here, unlike the orbit messages
No metadata delimitersTable 3-2Y: the section ends at EPOCH
Only the keywords of table 3-2 in metadata3.2.3.2MY
EPOCHTable 3-3MY
Quaternion block, QUAT_START/QUAT_STOPTable 3-3OY: Q1QC mandatory, the four derivatives optional as a group
Euler block, EULER_START/EULER_STOPTable 3-3OY: EULER_ROT_SEQ and the three angles mandatory, the rates optional
Angular velocity blockTable 3-3OY
Spin blockTable 3-3OY: nutation and momentum groups each conditional as a whole
Inertia blockTable 3-3OY
Maneuver block, repeatableTable 3-3, 3.2.4OY: a second MAN_START appends
All mandatory elements present if a block is presentTable 3-3 headingsMY: ErrMissingKeyword
A stop must match its startTable 3-3MY: ErrUnexpectedDelimiter
Only the keywords of table 3-3 in data3.2.4.2MY: checked per block, so an inertia keyword inside a quaternion block is refused

AEM, section 4

FeatureReferenceStatusSupport
Header keywordsTable 4-1M/OY
META_START and META_STOP around each metadata groupTable 4-3MY
DATA_START and DATA_STOP around each data blockTable 4-3MY
One or more segments4.1MY: ErrNoSegment
Metadata: object, frames, time system, spanTable 4-3MY: REF_FRAME_A and REF_FRAME_B both required
USEABLE_START_TIME, USEABLE_STOP_TIMETable 4-3OY: read and preserved, never used to trim, see A3
ATTITUDE_TYPE, one of nine valuesTable 4-3, 4-4MY: ErrUnknownAttitudeType
EULER_ROT_SEQ when the type is EulerTable 4-3CY: ErrEulerRotSeqMissing
ANGVEL_FRAME when the type pairs an attitude with angular velocityTable 4-3CY: read and preserved, not required
INTERPOLATION_DEGREE mandatory with INTERPOLATION_METHODTable 4-3CY
Data line width per attitude typeTable 4-4MY: every line checked; ErrAttitudeLineFields
Positional data lines, space separated4.2MY

ACM, section 5

The ACM's six sections hold something over a hundred keywords, so this lists them by section rather than one row apiece. The full tables are in pkg/adm/acm_keywords.go, in the order the Blue Book prints them, which is also the order clauses 5.3.3.5 and 5.3.4.1 require them to arrive in.

FeatureReferenceStatusSupport
Header keywordsTable 5-2M/OY: the same six as the APM's table 3-1
Metadata section, one only5.3.3.4MY
Metadata keywordsTable 5-3M/OY: OBJECT_NAME, TIME_SYSTEM and EPOCH_TZERO are mandatory
Section orderTable 5-1, 5.3.1.2MY: a section out of order is refused
Keyword order within a section5.3.3.5, 5.3.4.1MY: a keyword out of its table's order is refused
Attitude state sectionsTable 5-4, 5.3.5.4OY: any number
ATT_TYPE and RATE_TYPE element countsAnnex B4MY: QUATERNION 4, EULER_ANGLES 3, DCM 9; ANGVEL 3, Q_DOT 4, EULER_RATE 3, GYRO_BIAS 3, NONE 0
NUMBER_STATES agrees with the typesTable 5-4MY: a disagreement is refused, not resolved either way
Attitude data line widthTable 5-4MY: one time tag plus NUMBER_STATES values
EULER_ROT_SEQ when the type is Euler anglesTable 5-4CY
Physical characteristics section, one only5.3.6.3OY
CP_REF_FRAME present if CP isTable 5-5CY
Covariance sectionsTable 5-6, 5.3.7.3OY: any number
COV_TYPE matrix dimensionsAnnex B6MY: all six values
Covariance line is the main diagonal5.3.7.6MY: one time tag plus the dimension
Covariance time ordered increasing5.3.7.5MY: the attitude section has no such rule and is not held to one
Manoeuvre sectionsTable 5-7, 5.3.8.4OY: any number
MAN_END_TIME or MAN_DURATION, not bothTable 5-7CY
TARGET_MOM_FRAME present if TARGET_MOMENTUM isTable 5-7CY
Vector component countsTables 5-5, 5-7MY: CP and TARGET_MOMENTUM three, TARGET_ATTITUDE four
Attitude determination section, one only5.3.9.2OY
AD_METHOD estimator typesAnnex B5OY: all six of EKF, TRIAD, QUEST, BATCH, Q_METHOD, FILTER_SMOOTHER
Sensor sub-blocks, delimited and unique5.3.9.5, 5.3.9.6, Table 5-8OY: SENSOR_START to SENSOR_STOP inside the section and nowhere else; numbers must be unique
SENSOR_NOISE_STDDEV matches NUMBER_SENSOR_NOISE_COVARIANCETable 5-8OY
User-defined section, one only, at least one parameter5.3.10.4, Table 5-9OY
Relative or absolute time tags5.3.4.3MY: DataRow.TimeTag resolves either against EPOCH_TZERO
No duplicate time tags in a block5.3.4.4MY
One time tag kind per block5.3.4.5MY

XML form, section 7

FeatureReferenceStatusSupport
Root element with id and version attributes7.4.2.8–7.4.2.10MY: version 2.0
Schema instance namespace, exactly as given7.4.2.3MY
Master schema for the ADM7.4.2.5OY: ndmxml-4.0.0-master-4.0.xsd, not the ODM's 3.0.0
quaternionState wrapping frames, quaternion and quaternionDot7.5.11, 7.5.12MY
eulerAngleState, angularVelocity, spin, inertia, maneuverParameters7.5.11OY
attitudeState with the type's own inner element7.6.11, Table 7-5MY: all nine; a disagreement between type and element is refused
Units as attributes7.4, 7.7.10–7.7.12OY
ACM root, one segment, one metadata section7.7.1–7.7.7MY
ACM section tags and data line tagsTable 7-7MY: att, phys, cov, man, ad, user and their attLine, covLine
ACM data lines as xsd:string7.7.13.3MY: kept as rows, split by the reader, as the clause intends
sensorData elements inside ad7.7.14OY: shown by example rather than stated; one outside the attitude determination block is refused
NDM combined instantiation7.8OY: implemented by pkg/ndm, since a combined file may hold messages from other standards too

A3 EXCEPTIONS AND UNSUPPORTED FEATURES

An ACM'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 something over a hundred of them across six sections, most optional, so there would be little 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. That is the same choice pkg/odm makes for the OCM.

An ACM covariance carries only its diagonal. Clause 5.3.7.6 puts the main diagonal on the line and nothing else, and clause 5.3.7.7 sends anyone who needs the off-diagonal terms to a user-defined block. That is a limit of the format, not of this package: CovarianceCount reports the dimension, and there is no matrix to rebuild.

No attitude mathematics. Nothing normalises a quaternion, converts between representations, composes rotations, or interpolates. INTERPOLATION_METHOD and INTERPOLATION_DEGREE are carried and acted on by nobody. The conventions those operations depend on are in annex F rather than in the wire format, and a library that guessed at them would be guessing about spacecraft attitude.

A quaternion is not normalised or checked. A message whose four components do not describe a unit quaternion is structurally valid and is read without complaint. Whether to normalise, and how to treat one that is far from unit, is a decision for whatever uses the numbers.

The useable span is not applied. As in the OEM, USEABLE_START_TIME and USEABLE_STOP_TIME are read and preserved and no records are dropped.

AttitudeLine.Values is not unpacked into named fields. Table 4-4 gives the same position different meanings under different types — the fourth value is QC for a quaternion and SPIN_ANGLE_VEL for a spin state. Naming them would mean naming eight of the nine layouts wrongly. AttitudeType.Fields reports the width and the caller reads the table.

Enumerated values are not enforced beyond ATTITUDE_TYPE. Frame names, EULER_ROT_SEQ axis orders and interpolation methods have value sets in annex B. They are carried as written, because annex B is a registry that changes without this package.


A4 IMPLEMENTATION LIMITS

LimitValueSource
Line length, APM and AEM254 charactersClause 6.6.1
Line length, ACMunboundedClause 6.6.2 exempts the ACM outright, the same split CCSDS 502.0-B-3 makes for the OCM
Digits in a non-integer value16Clause 6.5
Maneuvers per APMbounded by the inputNo ceiling imposed
Records per AEM blockbounded by the inputRead into memory rather than streamed
Attitude types, AEM9Table 4-4
Attitude and rate types, ACM3 and 5Annex B4
Covariance types, ACM6Annex B6
Data sections per ACMbounded by the inputNo ceiling on the repeating sections; the rest are limited to one by the standard
Sensor blocks per ACMbounded by the inputClause 5.3.9.5 asks for as many as there are sensors

Wire test vectors

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

File
adm/attitude.json8 vectors
adm/apm-*.kvn, adm/aem-*.kvn, adm/acm-*.kvnthe annex G examples as readable files
adm/acm-xml.xmlthe ACM of figure G-12 in the XML form of section 7

All are published text rather than derived values: annex G of the Blue Book prints them.

The vectors assert the shape — which blocks an APM carried, the frames a rotation goes between, and for an AEM the attitude type together with the line width it implies. For an ACM the shape is self-checking, and the vectors carry every part of it: the types, the NUMBER_STATES the producer declared, and the row width all three have to agree on. The numbers are floats, which a vector field cannot hold, and are checked in pkg/adm 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.