Astro~/0.3
** Conformance / TM Space Data Link (ECSS) * PAGE 07 / 23
** Astro * Conformance
** /conformance/tmdl-ecss

TM Space Data Link (ECSS)

ECSS-E-ST-50-03C conformance statement for the TM frame protocol.

Conformance Statement for pkg/tmdl, ECSS-E-ST-50-03C


A1 GENERAL INFORMATION

A1.1 Identification

FieldValue
Date of Statement (DD/MM/YYYY)25/08/2026
Serial NumberASTRO-ECSS5003C-CONF-001
System Conformance Statement Cross-ReferenceThis document

A1.2 Identification of Implementation Under Test

FieldValue
Implementation Nameastro/pkg/tmdl
Implementation VersionSee go.mod / latest commit on main
Special ConfigurationNone
Other InformationGo library implementing the TM Space Data Link Protocol. This document audits it against the European profile rather than against CCSDS 132.0-B; the CCSDS statement is the CCSDS 132.0-B-3 PICS.

A1.3 Identification of Supplier

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

A1.4 Identification of Specification

FieldValue
SpecificationECSS-E-ST-50-03C, Space data links, Telemetry transfer frame protocol, 31 July 2008
Relationship to CCSDSThe European profile of the CCSDS TM Space Data Link Protocol (CCSDS 132.0-B). It defines no new frame; it adopts the CCSDS frame and constrains its options.
Obtained fromhttps://ecss.nl/wp-content/uploads/standards/ecss-e/ECSS-E-ST-50-03C31July2008.pdf, public, no registration required
Have any exceptions been required?No, the five gaps of the first pass were closed on 23/08/2026, and the idle-packet fill gap the second pass found was closed on 25/08/2026; see A3

A note on the version. Plan 024 called for "ECSS-E-ST-50-03C Rev.1". No such revision is published: the active document on ecss.nl is ECSS-E-ST-50-03C dated 31 July 2008, whose own change log records the 2008 issue as editorial renumbering of the 6 November 2007 text. This audit is against that document.


A2 REQUIREMENTS MATRIX

Every numbered clause of the normative sections (clause 5.1 to clause 5.6) appears below, one row each. Section 4 is the informative overview and contains no numbered requirements.

Inventory total: 135 clauses. Matrix rows: 135.

Verdicts use four values:

VerdictMeaning
conformsthe code always behaves as the clause requires
configurablethe code can be operated within the profile; the row states the configuration
gapthe code cannot satisfy the clause as written
out-of-scopethe clause targets a layer this package does not own

Requirement text is paraphrased. ECSS documents are copyrighted; clause identifiers plus a paraphrase are enough to find the original.

5.1 General

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.1aThe TM Transfer Frame shall encompass the major fields, positioned contiguously if present, in the sequence shown in...field order in the frameframe.go EncodeWithoutFEC assembles primary header, secondary header, data field, OCF in order; frame.go EncodeWithConfig appends the FECF lastconforms
5.1bThe maximum length for a TM Transfer Frame shall be 2048 octets.maximum frame length 2048 octetsphysical.go ChannelConfig.Validate rejects a FrameLength above MaxFrameLength (2048), but nothing calls it automatically, a CCSDS-only mission may exceed the European ceiling. Set 2048 or less and call Validateconfigurable
5.1cThe TM Transfer Frame shall be of constant length throughout a specific mission phase.constant frame length per mission phaseset ChannelConfig.FrameLength: frame.go EncodeWithConfig and DecodeTMTransferFrameWithConfig then reject any other length with ErrFrameLengthMismatch, and the VCP/VCA services build to that size. The legacy FrameLength: 0 path still emits one variable-length frame per packet, so constancy holds only under the fixed-length configurationconfigurable
5.1dThe TM Transfer Frame length shall be in conformance with the specifications contained in the standard for telemetry...conformance with the channel coding standardcoding lives in pkg/tmsc (CCSDS 131.0-B); this package emits frames onlyout-of-scope
5.1eTM Transfer Frames shall be transferred over a physical channel at a constant rate.constant transmission ratea physical-layer property; no code owns it hereout-of-scope
5.1fIn order to assure correct decoding at the receiving end, the same telemetry channel coding options shall be applied...same coding options across the channelbelongs to pkg/tmscout-of-scope
5.1gAt the receiving end, TM Transfer Frames containing detected errors need not be delivered.errored frames need not be deliveredframe.go DecodeTMTransferFrame rejects a frame whose FECF does not verify, returning an error rather than the frameconforms
5.1hThe handling of TM Transfer Frames containing detected errors shall be specified for each mission or mission phase.mission-specified handling of errored framesthe caller decides what to do with the error from frame.go DecodeTMTransferFrameWithConfigconfigurable
5.1iAll TM Transfer Frames with the same Master Channel Identifier on a physical channel shall constitute a master...frames sharing an MCID form a master channelframe.go MCID(); channel.go MasterChannel keyed by SCID; physical.go PhysicalChannel.masterChannels maps MCID to master channelconforms
5.1jA master channel shall consist of between one to eight virtual channels.one to eight virtual channels per master channelframe.go PrimaryHeader.Validate rejects a VCID above 7, so at most eight existconforms
5.1kOn a physical channel that carries TM Transfer Frames, all the frames shall have the same Transfer Frame Version...one Transfer Frame Version Number per physical channelframe.go PrimaryHeader.Validate fixes the version at 0 for every frameconforms

5.2 Transfer Frame Primary Header

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.2.1aThe Transfer Frame Primary Header shall always be present in a TM Transfer Frame.primary header always presentframe.go TMTransferFrame.Header is a value, not a pointer; frame.go EncodeWithoutFEC always encodes itconforms
5.2.1bThe Transfer Frame Primary Header shall consist of six fields, positioned contiguously, in the following sequence:six fields in sequenceframe.go PrimaryHeader.Encode writes MCID, VCID, OCF flag, MC count, VC count, data field status in orderconforms
5.2.2.1aThe Master Channel Identifier shall always be present in a Transfer Frame Primary Header.Master Channel Identifier, bits 0-11, two subfieldsframe.go MCID() combines version and SCID; frame.go PrimaryHeader.Encode packs them into the first two octetsconforms
5.2.2.1bThe Master Channel Identifier shall be contained within bits 0-11 of the Transfer Frame Primary Header.Master Channel Identifier, bits 0-11, two subfieldsframe.go MCID() combines version and SCID; frame.go PrimaryHeader.Encode packs them into the first two octetsconforms
5.2.2.1cThe Master Channel Identifier shall consist of two fields, positioned contiguously, in the following sequence:Master Channel Identifier, bits 0-11, two subfieldsframe.go MCID() combines version and SCID; frame.go PrimaryHeader.Encode packs them into the first two octetsconforms
5.2.2.2aThe Transfer Frame Version Number shall always be present in a Master Channel Identifier.Transfer Frame Version Number present, bits 0-1frame.go PrimaryHeader.VersionNumber, packed at frame.go PrimaryHeader.Encodeconforms
5.2.2.2bThe Transfer Frame Version Number shall be contained within bits 0-1 of the Transfer Frame Primary Header.Transfer Frame Version Number present, bits 0-1frame.go PrimaryHeader.VersionNumber, packed at frame.go PrimaryHeader.Encodeconforms
5.2.2.2cThe Transfer Frame Version Number shall be set to '00'.version number set to '00'frame.go PrimaryHeader.Validate Validate rejects any value but 0conforms
5.2.2.3aThe Spacecraft Identifier shall always be present in a Master Channel Identifier.Spacecraft Identifier present, bits 2-11frame.go PrimaryHeader.SpacecraftID is a 10-bit field; frame.go PrimaryHeader.Validate rejects a value above 0x3FFconforms
5.2.2.3bThe Spacecraft Identifier shall be contained within bits 2-11 of the Transfer Frame Primary Header.Spacecraft Identifier present, bits 2-11frame.go PrimaryHeader.SpacecraftID is a 10-bit field; frame.go PrimaryHeader.Validate rejects a value above 0x3FFconforms
5.2.2.3cThe Spacecraft Identifier shall provide the identification of the spacecraft which is associated with the data...Spacecraft Identifier present, bits 2-11frame.go PrimaryHeader.SpacecraftID is a 10-bit field; frame.go PrimaryHeader.Validate rejects a value above 0x3FFconforms
5.2.2.3dThe Spacecraft Identifier shall be static throughout all mission phases.SCID static across all mission phasesthe caller passes the SCID to frame.go NewTMTransferFrame; keeping it constant is the caller'sconfigurable
5.2.3aThe Virtual Channel Identifier shall always be present in a Transfer Frame Primary Header.Virtual Channel Identifier present, bits 12-14frame.go PrimaryHeader.VirtualChannelID; packed at frame.go PrimaryHeader.Encode; range checked at frame.go PrimaryHeader.Validateconforms
5.2.3bThe Virtual Channel Identifier shall be contained within bits 12-14 of the Transfer Frame Primary Header.Virtual Channel Identifier present, bits 12-14frame.go PrimaryHeader.VirtualChannelID; packed at frame.go PrimaryHeader.Encode; range checked at frame.go PrimaryHeader.Validateconforms
5.2.3cThe Virtual Channel Identifier shall provide the identification of the virtual channel to which the TM Transfer...VCID identifies the virtual channelframe.go GVCID(); channel.go MasterChannel.AddFrame routes by virtual channelconforms
5.2.4aThe Operational Control Field Flag shall always be present in a Transfer Frame Primary Header.OCF Flag present, bit 15frame.go PrimaryHeader.OCFFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.4bThe Operational Control Field Flag shall be contained in bit 15 of the Transfer Frame Primary Header.OCF Flag present, bit 15frame.go PrimaryHeader.OCFFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.4cThe Operational Control Field Flag shall indicate the presence or absence of the Operational Control Field, as...OCF Flag signals presence of the OCFframe.go EncodeWithoutFEC emits the four OCF octets only when the flag is set, and errors if the field is not four octetsconforms
5.2.4dThe Operational Control Field Flag shall be static in the associated master channel or virtual channel throughout a...OCF Flag static per channel throughout a phasephysical.go ChannelConfig.HasOCF is fixed per channel; the caller must set the header flag to matchconfigurable
5.2.5aThe Master Channel Frame Count shall always be present in a Transfer Frame Primary Header.Master Channel Frame Count present, bits 16-23frame.go PrimaryHeader.MCFrameCount; packed at frame.go PrimaryHeader.Encodeconforms
5.2.5bThe Master Channel Frame Count shall be contained within bits 16-23 of the Transfer Frame Primary Header.Master Channel Frame Count present, bits 16-23frame.go PrimaryHeader.MCFrameCount; packed at frame.go PrimaryHeader.Encodeconforms
5.2.5cThe Master Channel Frame Count shall contain a sequential binary count (modulo 256) of each TM Transfer Frame...sequential count modulo 256 per master channelservice.go FrameCounter.Next increments a uint8, wrapping at 256, but the count is sequential across the master channel only when the caller wires one shared FrameCounter into every service on it and into MasterChannel.SetFrameCounter for idle frames. Nothing enforces the wiringconfigurable
5.2.5dThe Master Channel Frame Count shall not be reset before reaching 255 unless there is a major system reset.count not reset before 255service.go FrameCounter.Next only ever increments; nothing resets itconforms
5.2.6aThe Virtual Channel Frame Count shall always be present in a Transfer Frame Primary Header.Virtual Channel Frame Count present, bits 24-31frame.go PrimaryHeader.VCFrameCount; packed at frame.go PrimaryHeader.Encodeconforms
5.2.6bThe Virtual Channel Frame Count shall be contained within bits 24-31 of the Transfer Frame Primary Header.Virtual Channel Frame Count present, bits 24-31frame.go PrimaryHeader.VCFrameCount; packed at frame.go PrimaryHeader.Encodeconforms
5.2.6cThe Virtual Channel Frame Count shall contain a sequential binary count (modulo 256) of each TM Transfer Frame...sequential count modulo 256 per virtual channelservice.go FrameCounter keeps one wrapping uint8 per VCID, sequential only when the caller passes a counter to the channel's service and does not stamp counts by hand. Nothing enforces the wiringconfigurable
5.2.6dThe Virtual Channel Frame Count shall not be reset before reaching 255 unless there is a major system reset.count not reset before 255service.go FrameCounter.Nextconforms
5.2.7.1aThe Transfer Frame Data Field Status shall always be present in a Transfer Frame Primary Header.Data Field Status present, bits 32-47, five subfieldsframe.go PrimaryHeader (the five data-field-status fields) are the five subfields; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.1bThe Transfer Frame Data Field Status shall be contained within bits 32-47 of the Transfer Frame Primary Header.Data Field Status present, bits 32-47, five subfieldsframe.go PrimaryHeader (the five data-field-status fields) are the five subfields; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.1cThe Transfer Frame Data Field Status shall consist of five fields, positioned contiguously, in the following sequence:Data Field Status present, bits 32-47, five subfieldsframe.go PrimaryHeader (the five data-field-status fields) are the five subfields; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.2aThe Transfer Frame Secondary Header Flag shall always be present in a Transfer Frame Data Field Status.Secondary Header Flag present, bit 32frame.go PrimaryHeader.FSHFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.2bThe Transfer Frame Secondary Header Flag shall be contained in bit 32 of the Transfer Frame Primary Header.Secondary Header Flag present, bit 32frame.go PrimaryHeader.FSHFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.2cThe Transfer Frame Secondary Header Flag shall indicate the presence or absence of the Transfer Frame Secondary...flag signals presence of the secondary headerframe.go EncodeWithoutFEC encodes the secondary header only when FSHFlag is setconforms
5.2.7.2dThe Transfer Frame Secondary Header Flag shall be static in a specific master channel, throughout a mission phase,...flag static per master or virtual channelthe caller sets it per frame; frame.go NewTMTransferFrame sets it when secondary header data is suppliedconfigurable
5.2.7.2eThe Transfer Frame Secondary Header Flag shall be static in a specific virtual channel, throughout a mission phase,...flag static per master or virtual channelthe caller sets it per frame; frame.go NewTMTransferFrame sets it when secondary header data is suppliedconfigurable
5.2.7.3aThe Synchronization Flag shall always be present in a Transfer Frame Data Field Status.Synchronization Flag present, bit 33frame.go PrimaryHeader.SyncFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.3bThe Synchronization Flag shall be contained in bit 33 of the Transfer Frame Primary Header.Synchronization Flag present, bit 33frame.go PrimaryHeader.SyncFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.3cThe Synchronization Flag shall signal the formatting of the Transfer Frame Data Field, as follows:flag signals data field formattingservice.go emitFrame leaves it clear for packet service; service.go VirtualChannelAccessService.Send sets it for virtual channel accessconforms
5.2.7.3dThe Synchronization Flag shall be static in a specific virtual channel throughout a mission phase.flag static per virtual channel throughout a phasefixed by which service the caller runs on the channel: service.go VirtualChannelPacketService packets, service.go VirtualChannelAccessService accessconfigurable
5.2.7.4aThe Packet Order Flag shall always be present in a Transfer Frame Data Field Status.Packet Order Flag present, bit 34frame.go PrimaryHeader.PacketOrderFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.4bThe Packet Order Flag shall be contained in bit 34 of the Transfer Frame Primary Header.Packet Order Flag present, bit 34frame.go PrimaryHeader.PacketOrderFlag; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.4cIf the Synchronization Flag is set to '0', the Packet Order Flag shall be set to '0'.Sync Flag '0' forces Packet Order Flag '0'frame.go PrimaryHeader.Validate rejects the combination outrightconforms
5.2.7.5aThe Segment Length Identifier shall always be present in a Transfer Frame Data Field Status.Segment Length Identifier present, bits 35-36frame.go PrimaryHeader.SegmentLengthID; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.5bThe Segment Length Identifier shall be contained in bits 35-36 of the Transfer Frame Primary Header.Segment Length Identifier present, bits 35-36frame.go PrimaryHeader.SegmentLengthID; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.5cIf the Synchronization Flag is set to '0', the Segment Length Identifier shall be set to '11'.Sync Flag '0' forces Segment Length Identifier '11'frame.go PrimaryHeader.Validate rejects any other valueconforms
5.2.7.6aThe First Header Pointer shall always be present in a Transfer Frame Data Field Status.First Header Pointer present, bits 37-47frame.go PrimaryHeader.FirstHeaderPtr; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.6bThe First Header Pointer shall be contained in bits 37-47 of the Transfer Frame Primary Header.First Header Pointer present, bits 37-47frame.go PrimaryHeader.FirstHeaderPtr; packed at frame.go PrimaryHeader.Encodeconforms
5.2.7.6cIf the Synchronization Flag is set to '0', the First Header Pointer shall contain information on the data in the...FHP carries data field information when Sync Flag is '0'frame.go validate bounds it to 11 bits; construction pins it to 0x7FF when the Sync Flag is set, while decode accepts any value there, since the standard leaves the field undefined for a receiverconforms
5.2.7.6dIf at least one packet starts in the Transfer Frame Data Field, the First Header Pointer shall contain the location...FHP gives the location of the first packet headerservice.go emitFrame takes the pointer; service.go VirtualChannelPacketService.Receive reads from itconforms
5.2.7.6eThe locations of the octets in the Transfer Frame Data Field shall be numbered in ascending order starting with '0'.octets numbered from zeroservice.go VirtualChannelPacketService.Receive indexes the data field from 0conforms
5.2.7.6fIf no packet starts in the Transfer Frame Data Field, the First Header Pointer shall be set to '11111111111'.no packet starts, FHP '11111111111'service.go emitFrame is called with 0x7FF for a continuation frameconforms
5.2.7.6gIf the Transfer Frame Data Field contains only idle data, the First Header Pointer shall be set to '11111111110'.idle data only, FHP '11111111110'frame.go NewIdleFrame sets FHPOnlyIdleData (0x7FE); IsIdleFrame matches itconforms

5.3 Transfer Frame Secondary Header

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.3.1aIf present, the Transfer Frame Secondary Header shall follow, without gap, the Transfer Frame Primary Header.secondary header follows the primary without gapframe.go EncodeWithoutFECconforms
5.3.1bThe presence or absence of the Transfer Frame Secondary Header shall be signalled by the Transfer Frame Secondary...presence signalled by the Secondary Header Flagframe.go EncodeWithoutFECconforms
5.3.1cIf present, the Transfer Frame Secondary Header shall comprise an integral number of octets: between 2 and 64 octets.secondary header total 2 to 64 octetsframe.go Validate rejects a total above MaxSecondaryHeaderSize (64)conforms
5.3.1dThe Transfer Frame Secondary Header shall be associated with either a master channel or a virtual channel.associated with one channel, fixed length throughout a phasethe caller supplies the data at frame.go NewTMTransferFrame; keeping it fixed is the caller'sconfigurable
5.3.1eThe Transfer Frame Secondary Header shall have a fixed length in the associated master channel or in the associated...associated with one channel, fixed length throughout a phasethe caller supplies the data at frame.go NewTMTransferFrame; keeping it fixed is the caller'sconfigurable
5.3.1fThe Transfer Frame Secondary Header shall consist of two fields, positioned contiguously, in the following sequence:two fields: identification then dataframe.go SecondaryHeader.Encode writes the identification octet then the data fieldconforms
5.3.1gThe Transfer Frame Secondary Header shall be used to carry fixed length data defined at mission level.carries fixed-length mission-defined dataframe.go SecondaryHeader.DataField DataField is opaqueconforms
5.3.1hThe Transfer Frame Secondary Header may be used to provide an extended virtual channel frame count as specified in...may carry an extended virtual channel frame countpermitted, not required. The container exists at frame.go SecondaryHeader.DataField; see 5.3.4.2configurable
5.3.2.1aThe Transfer Frame Secondary Header Identification shall always be present in a Transfer Frame Secondary Header.identification present, bits 0-7, two subfieldsframe.go SecondaryHeader.Encode packs version and length into one octetconforms
5.3.2.1bThe Transfer Frame Secondary Header Identification shall be contained in bits 0-7 of the Transfer Frame Secondary...identification present, bits 0-7, two subfieldsframe.go SecondaryHeader.Encode packs version and length into one octetconforms
5.3.2.1cThe Transfer Frame Secondary Header Identification shall comprise two fields, positioned contiguously, in the...identification present, bits 0-7, two subfieldsframe.go SecondaryHeader.Encode packs version and length into one octetconforms
5.3.2.2aThe Transfer Frame Secondary Header Version Number shall always be present in a Transfer Frame Secondary Header...secondary header version present, bits 0-1frame.go SecondaryHeader.VersionNumber; packed at frame.go SecondaryHeader.Encodeconforms
5.3.2.2bThe Transfer Frame Secondary Header Version Number shall be contained in bits 0-1 of the Transfer Frame Secondary...secondary header version present, bits 0-1frame.go SecondaryHeader.VersionNumber; packed at frame.go SecondaryHeader.Encodeconforms
5.3.2.2cThe Transfer Frame Secondary Header Version Number shall be set to '00'.secondary header version set to '00'frame.go SecondaryHeader.Validate rejects any other valueconforms
5.3.2.3aThe Transfer Frame Secondary Header Length shall always be present in a Transfer Frame Secondary Header...length field present, bits 2-7frame.go SecondaryHeader.HeaderLength; packed at frame.go SecondaryHeader.Encodeconforms
5.3.2.3bThe Transfer Frame Secondary Header Length shall be contained in bits 2-7 of the Transfer Frame Secondary Header.length field present, bits 2-7frame.go SecondaryHeader.HeaderLength; packed at frame.go SecondaryHeader.Encodeconforms
5.3.2.3cThe Transfer Frame Secondary Header Length shall contain the total length of the Transfer Frame Secondary Header in...length field is TOTAL secondary header octets minus oneframe.go Validate requires the field to equal the data field length, i.e. the total minus one; Decode reads it the same wayconforms
5.3.2.3dThe value of the Transfer Frame Secondary Header Length shall be static within a specific master channel or a...length static per channel throughout a phasethe caller keeps the data field length fixedconfigurable
5.3.3aThe Transfer Frame Secondary Header Data Field shall always be present in a Transfer Frame Secondary Header.data field present, follows the identification, carries the dataframe.go SecondaryHeader.Encode and frame.go SecondaryHeader.Decodeconforms
5.3.3bThe Transfer Frame Secondary Header Data Field shall follow, without gap, the Transfer Frame Secondary Header...data field present, follows the identification, carries the dataframe.go SecondaryHeader.Encode and frame.go SecondaryHeader.Decodeconforms
5.3.3cThe Transfer Frame Secondary Header Data Field shall contain the Transfer Frame Secondary Header data.data field present, follows the identification, carries the dataframe.go SecondaryHeader.Encode and frame.go SecondaryHeader.Decodeconforms
5.3.4.2aThe length of the Transfer Frame Secondary Header shall be 32 bits.extended count needs a 32-bit secondary headerframe.go, closed together with 5.3.2.3c; a 4-octet header now writes 3conforms
5.3.4.2bThe Transfer Frame Secondary Header Data Field shall contain the 24-bit extension to the virtual channel frame count.24-bit extension counting roll-overs of the 8-bit countthe caller places the extension in frame.go SecondaryHeader.DataField DataField; the package does not maintain the roll-over countconfigurable
5.3.4.2cThe extension to the virtual channel frame count shall be a binary count of the roll-overs of the 8-bit value...24-bit extension counting roll-overs of the 8-bit countthe caller places the extension in frame.go SecondaryHeader.DataField DataField; the package does not maintain the roll-over countconfigurable
5.3.4.2dThe use of the extended virtual channel frame count shall be associated with either a master channel or a virtual...extended count associated with a channel and statica caller-side conventionconfigurable
5.3.4.2eThe use of the extended virtual channel frame count shall be static in the associated master channel or in the...extended count associated with a channel and statica caller-side conventionconfigurable

5.4 Transfer Frame Data Field

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.4.2aThe Transfer Frame Data Field shall always be present in a TM Transfer Frame.data field always presentframe.go TMTransferFrame.DataField; frame.go EncodeWithoutFEC always writes itconforms
5.4.2bThe Transfer Frame Data Field shall follow, without gap, one of the following:data field follows the secondary or primary header without gapframe.go EncodeWithoutFEC appends it directly after whichever header was writtenconforms
5.4.2cIf a Transfer Frame Secondary Header is present, the Transfer Frame Secondary.data field follows the secondary or primary header without gapframe.go EncodeWithoutFEC appends it directly after whichever header was writtenconforms
5.4.2dIf a Transfer Frame Secondary Header is not present, the Transfer Frame Primary.data field follows the secondary or primary header without gapframe.go EncodeWithoutFEC appends it directly after whichever header was writtenconforms
5.4.2eThe length of the Transfer Frame Data Field shall be an integral number of octets and be constrained by the length...integral octets, constrained by the frame lengthphysical.go ChannelConfig.DataFieldCapacity derives it; frame.go padDataField fills to capacityconforms
5.4.3.3aA packet handled by the packet processing and extraction functions shall have a defined Packet Version Number in...packet version number conformancepacket formats belong to pkg/spp; this package treats the data field as octetsout-of-scope
5.4.3.3bAn idle packet shall be either:idle packet definitionservice.go idleFillPacket builds a real SPP idle packet, APID 0x7FF per CCSDS 133.0-B-2, and isIdlePacket recognises one by that APID on extractionconforms
5.4.3.4aThe packet processing function shall be applied independently for each virtual channel.packet processing applied per virtual channelservice.go VirtualChannelPacketService is constructed per VCID at service.go NewVirtualChannelPacketServiceconforms
5.4.3.4bThe packet processing function shall place packets contiguously into the Transfer Frame Data Field.packets placed contiguouslyservice.go VirtualChannelPacketService.Send appends to one buffer; service.go emitFullFrames slices itconforms
5.4.3.4cIf the length of a packet exceeds the available space in the Transfer Frame Data Field, the packet processing...packets longer than the space are split across framesservice.go emitFullFrames emits full frames and carries the remainder forwardconforms
5.4.3.4dThe packet processing function shall set the First Header Pointer as specified in clause 5.2.7.6.First Header Pointer set per 5.2.7.6service.go emitFrame takes the computed pointerconforms
5.4.3.4ePackets with different Packet Version Numbers may be transmitted within a virtual channel.packets of different versions may share a virtual channelpermitted, not required. service.go VirtualChannelPacketService.Send does not inspect packet contentsconforms
5.4.3.4fA Transfer Frame Data Field containing only idle data may be created.a data field of only idle data may be createdpermitted. frame.go NewIdleFrame builds one with the FHPOnlyIdleData pointer per 5.2.7.6g, and NewIdleFrameWithCounter stamps its frame counts from the shared counterconforms
5.4.3.4gOne or more idle packets may be created to fill space in a Transfer Frame Data Field.idle packets may fill spare spaceservice.go Flush fills spare data field space with an SPP idle packet, spanning into following frames when the spare space is under the seven-octet minimum, so a conformant receiver parses the fill as a packet and discards itconforms
5.4.3.5aThe packet extraction function shall be applied independently for each virtual channel.packet extraction applied per virtual channelservice.go VirtualChannelPacketService.Receive, one service per VCIDconforms
5.4.3.5bThe packet extraction function shall extract the packets from a Transfer Frame Data Field using the value of the...extraction uses the First Header Pointerservice.go VirtualChannelPacketService.Receiveconforms
5.4.3.5cA Transfer Frame Data Field containing idle data shall be discarded.a data field of idle data is discardedframe.go IsIdleFrame matches OID frames and service.go Receive skips them; the FHPOnlyIdleData branch drops fields marked idle by the pointerconforms
5.4.3.5dAny idle packets extracted from Transfer Frame Data Fields shall be discarded.extracted idle packets are discardedservice.go isIdlePacket applied in Receive: every extracted packet with the idle APID is dropped before deliveryconforms
5.4.4.2aThe stored data shall be in the form of standard TM Transfer Frames.playback data is whole recorded frames placed in real-time data fieldsservice.go VirtualChannelAccessService carries opaque octets, so recorded frames fit; the package does not manage the recorderconfigurable
5.4.4.2bAt playback time, the recorded TM Transfer Frames shall be placed into the Transfer Frame Data Field of real-time TM...playback data is whole recorded frames placed in real-time data fieldsservice.go VirtualChannelAccessService carries opaque octets, so recorded frames fit; the package does not manage the recorderconfigurable
5.4.4.2cThe asynchronous insertion may be made in either the forward or the reverse mode.forward or reverse insertion modea caller-side choice; neither is modelledconfigurable
5.4.4.2dIf forward insertion mode is used, then any recorded attached synchronization markers shall use the alternative...alternative synchronization marker for forward modesync markers belong to pkg/tmscout-of-scope
5.4.4.2eA dedicated virtual channel shall be used for the playback data.a dedicated virtual channel for playbackthe caller assigns the VCID at service.go NewVirtualChannelAccessServiceconfigurable
5.4.4.2fAt the receiving end, the real-time virtual channel used for the playback data shall be processed and its contents...receiving end stores and later retrieves playback framesservice.go VirtualChannelAccessService.Receive returns the octets; storage and ordering are the caller'sconfigurable
5.4.4.2gIn the later off-line processing, the recorded TM Transfer Frames shall be retrieved in the correct,...receiving end stores and later retrieves playback framesservice.go VirtualChannelAccessService.Receive returns the octets; storage and ordering are the caller'sconfigurable
5.4.4.2hAny Communications Link Control Word (see clause 5.5.3) extracted from the Operational Control Field of a recorded...CLCWs from recorded frames must not drive the live linkCLCW handling belongs to pkg/copout-of-scope

5.5 Operational Control Field

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.5.1aIf present, the Operational Control Field shall occupy the four octets following, without gap, the Transfer Frame...OCF occupies the four octets after the data fieldframe.go EncodeWithoutFEC appends it there and rejects any length but fourconforms
5.5.1bThe presence or absence of the Operational Control Field shall be signalled by the Operational Control Field Flag in...presence signalled by the OCF Flagframe.go EncodeWithoutFECconforms
5.5.1cThe Operational Control Field shall be associated with a master channel or a virtual channel.OCF associated with a channel and present in every frame of itphysical.go ChannelConfig.HasOCF HasOCF fixes it per channel; the caller supplies the field per frameconfigurable
5.5.1dThe Operational Control Field shall be present in every TM Transfer Frame transmitted through the associated master...OCF associated with a channel and present in every frame of itphysical.go ChannelConfig.HasOCF HasOCF fixes it per channel; the caller supplies the field per frameconfigurable
5.5.1eBit 0 of the Operational Control Field shall contain a Type Flag which indicates the contents of the field.bit 0 of the OCF is a Type Flagframe.go TMTransferFrame.OperationalControl carries the four octets verbatim without interpreting them; the caller composes the flagconfigurable
5.5.2aThe Type Flag shall always be present in an Operational Control Field.Type Flag present in bit 0 and set by content typenot modelled. frame.go TMTransferFrame.OperationalControl is an opaque four-octet field the caller fillsconfigurable
5.5.2bThe Type Flag shall be contained in bit 0 of the Operational Control Field.Type Flag present in bit 0 and set by content typenot modelled. frame.go TMTransferFrame.OperationalControl is an opaque four-octet field the caller fillsconfigurable
5.5.2cThe Type Flag shall be set as follows:Type Flag present in bit 0 and set by content typenot modelled. frame.go TMTransferFrame.OperationalControl is an opaque four-octet field the caller fillsconfigurable
5.5.2dThe Type Flag may vary between TM Transfer Frames on the same virtual channel.the Type Flag may vary between framespermitted. frame.go TMTransferFrame.OperationalControl is set per frameconforms
5.5.3aIf the Type Flag is '0', the Operational Control Field shall contain a Type-1-Report.Type Flag '0' means a Type-1-Reportthe caller composes the fieldconfigurable
5.5.3bA Type-1-Report shall contain a Communications Link Control Word in conformance with ECSS-E-ST-50-04, clause 6.3.Type-1-Report carries a CLCW per ECSS-E-ST-50-04the CLCW belongs to pkg/cop; pkg/tmdl carries the octetsout-of-scope
5.5.4aIf the Type Flag is '1', the Operational Control Field shall contain a Type-2-Report.Type Flag '1' means a Type-2-Report and its first bit gives its usenot modelled; the caller composes the fieldconfigurable
5.5.4bThe value of the first bit of a Type-2-Report (i.e. bit 1 of the Operational Control Field) shall indicate the use...Type Flag '1' means a Type-2-Report and its first bit gives its usenot modelled; the caller composes the fieldconfigurable
5.5.4cThe value of the first bit of a Type-2-Report may vary between TM Transfer Frames on the same virtual channel.the first bit may vary between framespermitted. frame.go TMTransferFrame.OperationalControl is set per frameconforms

5.6 Frame Error Control Field

ReqRequirement (paraphrase)What it constrainspkg/tmdl evidenceVerdict
5.6.1aIf present, the Frame Error Control Field shall occupy the two octets following, without gap, one of the following:FECF occupies the two octets after the OCF or data fieldframe.go EncodeWithConfig appends it last, after whatever frame.go EncodeWithoutFEC producedconforms
5.6.1bThe Frame Error Control Field shall be present in a TM Transfer Frame if the TM Transfer Frame is not Reed-Solomon...FECF present when the frame is not Reed-Solomon encodedframe.go EncodeWithConfig always appends it, so the mandatory case is always metconforms
5.6.1cIf present, the Frame Error Control Field shall occur within every TM Transfer Frame transmitted within the same...FECF consistently present or absent across the physical channelframe.go EncodeWithConfig and DecodeTMTransferFrameWithConfig honour ChannelConfig.HasFECconforms
5.6.2aThe encoding procedure shall be as follows:encoding procedure, CRC-16 with generator X^16+X^12+X^5+1, preset to onesframe.go EncodeWithConfig calls crc.ComputeCRC16; pkg/crc ComputeCRC16 implements that polynomial with an all-ones presetconforms
5.6.3aThe decoding procedure shall use an error detection syndrome, S(X), given by S(X) = [(X16 ⋅ C*(X)) + (Xn ⋅ L(X))]...decoding uses the error detection syndromeframe.go DecodeTMTransferFrameWithConfig recomputes the CRC over the frame and comparesconforms
5.6.3bThe Frame Error Control Field shall not be used for error correction.the FECF is not used for error correctionframe.go DecodeTMTransferFrameWithConfig rejects a mismatching frame; nothing attempts correctionconforms

A3 GAPS, ALL CLOSED

The audit of 23/08/2026 found five gaps, fixed the same day in fix(tmdl): correct four ECSS-E-ST-50-03C conformance defects. A second pass on 25/08/2026 found that the first pass had itself overstated two rows, and the underlying defect was fixed then. This section records all of them, since the reasoning is worth keeping and several describe a failure mode this codebase has now hit repeatedly.

5.4.3.3b / 5.4.3.4g, raw 0xFF fill sold as "idle packets" (second pass)

The first pass marked both rows conforms with padDataField's raw 0xFF fill as evidence. That was wrong twice over: 0xFF fill is not an idle packet, and a conformant receiver parses the fill as a packet header (0x7FF in the first two octets reads as the idle APID, but the length field is garbage) and loses packet sync. The in-repo receiver survived only through a nonstandard all-0xFF heuristic no other implementation shares.

Now: Flush fills spare data field space with a real SPP idle packet (APID 0x7FF) built by idleFillPacket, spanning into following frames when the spare space is under the seven-octet minimum packet size, and Receive discards extracted idle packets by APID per 5.4.3.5d. The raw-0xFF heuristic remains only as decode-side leniency for streams from older versions of this package.

Alongside that fix, three rows the first pass graded conforms were re-graded configurable, because the property holds only under caller wiring: 5.2.5c and 5.2.6c (counts are sequential only when one shared FrameCounter feeds every service and the idle-frame path) and 5.1c (length constancy holds only under a set FrameLength, which the codec now enforces on encode and decode).

5.3.2.3c. The secondary header length field was one too small

Clause 5.3.2.3c requires the six-bit field to hold the total secondary header length in octets minus one, the total being the identification octet plus the data field. So for an N-octet data field the field reads N.

The code required it to equal len(DataField)-1 and decoded with the same offset. A four-octet header went out saying 2 where the standard wants 3.

Encoder and decoder agreed, so every round trip passed and the package's own tests were silent, one of them asserted the wrong value outright, which is how the defect looked deliberate. A conforming receiver reading 2 computes a three-octet total and starts the Transfer Frame Data Field an octet early, corrupting every frame with a secondary header.

The same wording is in CCSDS 132.0-B-3 clause 4.1.3.2.2.3, so this was never an ECSS-only defect.

Now: the field equals the data field length, SetDataField derives it, and the test asserts the octet on the wire plus the offset a receiver would compute from it.

5.3.4.2a, the extended virtual channel frame count inherited it

Clause 5.3.4.2a fixes the secondary header at 32 bits when it carries the extended count: four octets, so the field must read 3. Closed by the fix above.

5.2.7.6g, idle frames used the wrong First Header Pointer

Clause 5.2.7.6 gives two codes to two conditions: 11111111111 (0x7FF) when no packet starts in the data field, 11111111110 (0x7FE) when the field holds only idle data. NewIdleFrame filled with idle and then set 0x7FF, and IsIdleFrame matched on 0x7FF, so OID frames were mislabelled and conformant ones from other senders went unrecognised.

Fixing this exposed a second defect the audit had not found: the packet service had the two codes swapped on both paths at once. It discarded continuation frames as idle (losing payload) and appended idle fill into the reassembly buffer. Consistent on both sides, so round trips passed here too.

Now: both codes are named constants, FHPNoPacketStart and FHPOnlyIdleData, and the service uses each for its own condition.

5.3.1c. The secondary header could exceed 64 octets

Clause 5.3.1c caps the whole secondary header at 64 octets. A 63-value length field with a 64-octet data field encoded to 65, and nothing rejected it.

Now: MaxSecondaryHeaderSize is enforced by Validate.

5.6.1c. The Frame Error Control Field could not be omitted

Clause 5.6.1b makes the field mandatory when the frame is not Reed-Solomon encoded, and its NOTE makes it optional inside a code block, which already protects the frame. Clause 5.6.1c requires the choice to hold across the physical channel.

Only "always present" was supported: Encode always appended the field, DecodeTMTransferFrame always verified it, and ChannelConfig.HasFEC was never consulted by the frame codec. A Reed-Solomon mission omitting the field ( a normal configuration) could not use the package.

Now: EncodeWithConfig and DecodeTMTransferFrameWithConfig honour HasFEC. The original entry points keep the field, so existing callers are unaffected, and VirtualChannelFrameService.SetChannelConfig carries the choice through the pass-through path.

And one that was recorded as configurable

Clause 5.1b caps the frame at 2048 octets and nothing enforced it. It is still configurable rather than a gap (the caller sets FrameLength, and a CCSDS-only mission may legitimately exceed the European ceiling) but ChannelConfig.Validate now checks it for missions that care.


A4 SUMMARY

VerdictCountShare
conforms9570%
configurable3324%
out-of-scope75%
gap0-
Total135

Inventory total 135, matrix rows 135. The two numbers are stated here so the check is self-contained.

Reading the result

pkg/tmdl conforms to the European TM transfer frame profile. Every mandatory clause is either satisfied outright or satisfiable by configuration, and the rows that say configurable name the configuration.

The five gaps the first pass of this audit found were all closed on the same day, and the idle-packet fill defect the second pass found was closed on 25/08/2026. Three of them had been putting wrong bytes on the wire, and all were the kind that hide: the secondary header length was self-consistent within the library, the idle-frame pointer sat in a field most tests do not assert, and the raw fill was survivable only by this package's own receiver. Fixing the pointer exposed a further defect of the same shape in the packet service.

That is the pattern worth carrying forward. Three separate defects in this codebase (the PN randomizer, this length field, and the swapped pointer codes ) were each perfectly symmetric and perfectly wrong. A round trip cannot catch any of them. Assert the octet.

On the 24% "configurable" share

Plan 024 set a threshold: if configurable rows exceed 20% of the matrix, the audit is a hedge and should be reported rather than shipped. This matrix is at 24%, so the threshold is crossed and the reasoning belongs here rather than buried.

The threshold guards against guessing. These rows are not guesses. They fall into three groups, and every one names its exact configuration:

  • Operational constancy (14 rows). Clauses of the form "shall be static in the associated master channel throughout a mission phase", 5.2.2.3d, 5.2.4d, 5.2.7.2d, 5.2.7.2e, 5.2.7.3d, 5.3.1d, 5.3.1e, 5.3.2.3d, 5.3.4.2d, 5.3.4.2e and their neighbours. A stateless frame codec cannot be static across a mission phase; only the system operating it can. Marking these "conforms" would be the dishonest answer.
  • Opaque fields the caller fills (9 rows). The Operational Control Field is four octets pkg/tmdl carries without interpreting (frame.go TMTransferFrame.OperationalControl), so the Type Flag clauses 5.5.1e through 5.5.4b are satisfied by whatever the caller puts there. The CLCW content itself belongs to pkg/cop.
  • Caller-supplied values and policies (10 rows). The 2048-octet frame limit (5.1b), frame-length constancy (5.1c), the shared frame counters (5.2.5c, 5.2.6c), playback handling (5.4.4.2), and the extended count's placement in the secondary header data.

None of these needed mission context I did not have. Had any been genuinely ambiguous it would appear as a gap with the ambiguity stated, not as a hedge.

Coverage boundary

Seven clauses are out-of-scope, and all seven point somewhere real:

ClauseBelongs to
5.1d, 5.1e, 5.1fchannel coding and the physical layer, pkg/tmsc
5.4.3.3apacket formats, pkg/spp
5.4.4.2dsynchronization markers, pkg/tmsc
5.4.4.2h, 5.5.3bthe CLCW, pkg/cop

None was silently dropped.


A5 METHOD

  • The standard was read in full; every numbered clause under clause 5 was extracted mechanically, giving 135. Section 4 is informative and contributes none.
  • Each clause was mapped by reading frame.go, service.go, channel.go, physical.go and errors.go. Every verdict but out-of-scope carries a pkg/tmdl file and symbol; the first pass cited line numbers, which drifted within a day of the audit, so the second pass replaced them with symbols.
  • Four clauses were checked by building frames in a temporary probe rather than by reading: 5.3.2.3c, 5.3.1c, 5.2.7.6g and 5.1b. All four findings above come from what the probe actually produced. The probe was deleted; this audit changed no code.
  • Evidence is as of the working tree on 25/08/2026, which carries the uncommitted idle-packet fill, counter-threading, and frame-length enforcement changes.