Skip to main content

Fixed-length packets

A fixed-length packet is one whose body always occupies the same number of bits, regardless of its contents. This is the most common layout in CCSDS telemetry and the simplest to describe with CCSDSPy. You declare the packet layout once as an ordered list of fields, then apply that layout to a binary file or stream containing many packets.

CCSDSPy reads tightly packed bits in the CCSDS Space Packet Protocol format used by many NASA and ESA missions, so fields do not need to align to byte boundaries — a 3-bit field followed by a 20-bit field is perfectly valid.

note

The 48-bit CCSDS primary header is handled for you. You only describe the packet body (the application data); CCSDSPy parses the header automatically and can optionally return its fields to you (see include_primary_header).

Defining the layout with FixedLength

ccsdspy.FixedLength takes a list of field definitions. Each field is a PacketField (a single scalar value) or a PacketArray (multiple values of the same size — covered on its own page).

import ccsdspy
from ccsdspy import PacketField, PacketArray

pkt = ccsdspy.FixedLength([
PacketField(name='SHCOARSE', data_type='uint', bit_length=32),
PacketField(name='SHFINE', data_type='uint', bit_length=20),
PacketField(name='OPMODE', data_type='uint', bit_length=3),
PacketField(name='SPACER', data_type='fill', bit_length=1),
PacketField(name='VOLTAGE', data_type='int', bit_length=8),
])

Fields are read in the order you list them. CCSDSPy computes each field's bit offset from the running total, starting immediately after the primary header.

PacketField parameters

PacketField(name, data_type, bit_length, bit_offset=None, byte_order="big", description=None)

ParameterType / allowed valuesRequiredDescription
namestryesIdentifier used as the key for this field's values after parsing.
data_type'uint', 'int', 'float', 'str', 'fill'yesData type of the field (see below).
bit_lengthintyesNumber of bits the field occupies.
bit_offsetintnoBit offset into the packet, including the 48-bit primary header. If omitted, it is computed automatically from field order.
byte_order'big', 'little', or a digit string like "3412"noByte order. Defaults to 'big'.
descriptionstrnoFree-text metadata; not used during parsing.
tip

You almost never need to set bit_offset. Because fields are defined in order within a list, CCSDSPy calculates offsets for you. Set it explicitly only when you want to skip ahead or overlay fields at a known position.

Supported data_type values

The constructor accepts exactly five data types. Anything else raises a ValueError.

data_typeMeaningNotes
'uint'Unsigned integerAny bit_length; not restricted to byte boundaries.
'int'Signed integer (two's complement)Any bit_length; sign is interpreted from the field's own bit width.
'float'IEEE 754 floating pointUse bit_length=32 or bit_length=64. CCSDSPy maps floats onto NumPy's float32 / float64.
'str'String (raw bytes)Returned as fixed-width byte strings.
'fill'Placeholder / paddingReserved space between meaningful fields; the value is ignored.
info

The 'fill' type lets you reserve bits you do not care about (spare bits, reserved fields, or alignment padding) without polluting your parsed output with meaningless values. In the example above, the 1-bit SPACER keeps the following VOLTAGE field correctly positioned.

Bit-level fields

Because CCSDS packs values without padding, bit_length can be any width that the data type allows — 3, 12, 20, and so on. Integers (uint / int) are the most flexible here: a 12-bit uint and a 3-bit uint are both valid. Floats are the exception: keep them at the standard 32- or 64-bit IEEE widths.

When a field's bit length is not a whole number of bytes, CCSDSPy still parses it correctly at the bit level, then returns each value in a NumPy array whose element size is rounded up to the next whole byte (for example, a 20-bit uint is returned in a 4-byte uint32 array).

Byte order

byte_order applies to integer fields and floats. In addition to the usual 'big' and 'little', CCSDSPy accepts an ad-hoc ordering written as a digit string such as "3412", which lets you describe non-standard byte arrangements seen in some instruments.

PacketField(name='COUNTER', data_type='uint', bit_length=32, byte_order='little')

Reading a file

Call .load() with a path (or file-like object). It returns a dictionary that maps each field name to a NumPy array, with one entry per packet found in the input.

result = pkt.load('MyCCSDS.tlm')

# result is a dict: field name -> numpy array (one value per packet)
result['VOLTAGE'] # e.g. array([...], dtype=int8)
result['SHCOARSE'] # e.g. array([...], dtype=uint32)

Pass include_primary_header=True to also receive the decoded CCSDS primary header fields (such as CCSDS_VERSION_NUMBER, CCSDS_SEQUENCE_COUNT, and CCSDS_PACKET_LENGTH) as additional keys in the result:

result = pkt.load('MyCCSDS.tlm', include_primary_header=True)
result['CCSDS_SEQUENCE_COUNT']
Illustrative example

The field names (SHCOARSE, OPMODE, VOLTAGE, …) and the file name MyCCSDS.tlm above are illustrative placeholders adapted from the CCSDSPy documentation. Replace them with the field layout and file paths from your own mission's interface control document (ICD).

When to use FixedLength

Use FixedLength when every packet that matches a definition has an identical body size. If your packet contains a field whose length depends on another field's value, or an array that grows to fill the packet, you need ccsdspy.VariableLength instead.

Sources