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.
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)
| Parameter | Type / allowed values | Required | Description |
|---|---|---|---|
name | str | yes | Identifier used as the key for this field's values after parsing. |
data_type | 'uint', 'int', 'float', 'str', 'fill' | yes | Data type of the field (see below). |
bit_length | int | yes | Number of bits the field occupies. |
bit_offset | int | no | Bit 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" | no | Byte order. Defaults to 'big'. |
description | str | no | Free-text metadata; not used during parsing. |
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_type | Meaning | Notes |
|---|---|---|
'uint' | Unsigned integer | Any 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 point | Use 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 / padding | Reserved space between meaningful fields; the value is ignored. |
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']
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.