Skip to main content

fast-ccsds-parser

CI Python License: MIT PyPI

Project page · 日本語のドキュメントはこちら

Fast, dependency-free parsing of CCSDS Space Packets (CCSDS 133.0-B-2) for the modern Python data stack. Turn raw telemetry dumps into dicts and structured objects in one pass: primary headers, packet boundaries, and payloads, with a pure standard-library core that installs anywhere (pip install and go).

Built for small-satellite teams, university labs, and data engineers who just want their packets parsed, without pulling a decoding framework.

Install

pip install fast-ccsds-parser

The core has no dependencies. For columnar export, add an extra (arrow, polars, pandas, or all):

pip install "fast-ccsds-parser[pandas]"

or from source:

pip install git+https://github.com/daikiito-dk/fast-ccsds-parser.git

Quickstart

from fast_ccsds_parser import parse_primary_header, iter_file

# One header, as a plain dict:
header = parse_primary_header(raw_bytes)
# {'version': 0, 'type': 0, 'secondary_header_flag': 1, 'apid': 42,
#  'sequence_flags': 3, 'sequence_count': 1337, 'packet_length': 255}

# A whole ground-station dump, streamed packet by packet (constant memory):
for pkt in iter_file("dump_20261010.pkts"):
    print(pkt.header.apid, pkt.header.sequence_count, len(pkt.payload))

# In-memory buffers work the same way:
packets = [p.to_dict() for p in iter_packets(buffer)]

Need a whole dump as a DataFrame? Scan it column by column instead of building one dict per packet (needs fast-ccsds-parser[pandas]):

from fast_ccsds_parser import scan_file, write_parquet

df = scan_file("dump_20261010.pkts").to_pandas()
write_parquet("dump_20261010.pkts", "dump_20261010.parquet")

What it parses

The 6-byte CCSDS primary header (all fields big-endian):

bit:   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0-1    | version  | T | S |                APID                       |
       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2-3    | seq flags |        sequence count / packet name               |
       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4-5    |                     packet length                            |
       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Field Bits Meaning
version 3 0 for space packets
type 1 0 = telemetry, 1 = telecommand
secondary_header_flag 1 1 = a secondary header follows the primary header
apid 11 application process identifier (all ones = idle)
sequence_flags 2 segmentation status (unsegmented = 0b11)
sequence_count 14 packet sequence count / packet name
packet_length 16 data field length minus one (see below)

API

Entry point Purpose
parse_primary_header(data, *, offset=0, strict=True) Header fields as a dict
PrimaryHeader.from_bytes(data, *, offset=0, strict=True) Header as a NamedTuple (.apid, .total_length, .is_idle, .to_dict(), ...)
parse_packet(data, *, strict=True) Exactly one packet, as a SpacePacket
iter_packets(data, *, strict=True, skip_idle=False) Generator over a buffer of concatenated packets
parse_packets(data, ...) Same, as a list
iter_file(path, ...) / parse_file(path, ...) Stream packets from a dump file with O(1) memory
scan_bytes(data, *, payload=True, ...) Parse a buffer into a columnar PacketTable
scan_file(path, *, payload=True, ...) Parse a whole file into one PacketTable (memory-mapped)
iter_tables(path, *, chunk_packets=1_000_000, ...) Yield one PacketTable per chunk, for files larger than memory
write_parquet(source, destination, ...) Convert a packet file to Parquet chunk by chunk
parse_timecode(data, *, offset=0, epoch=None) Decode the CUC or CDS time code at offset (format auto-detected)
parse_cuc(...) / parse_cds(...) Same, pinned to one format

SpacePacket carries header (a PrimaryHeader), payload (the data field bytes), raw (the complete packet bytes), and a to_dict() that flattens everything into a single dict.

Columnar output

PacketTable stores header fields in typed array.array columns and all payloads in one contiguous buffer, so memory stays close to the input size. Columns: offset, version, type, secondary_header_flag, apid, sequence_flags, sequence_count, packet_length, and payload.

table = scan_file("dump_20261010.pkts")   # or scan_bytes(buffer)
table.apid                                # array.array, one entry per packet
table.payload(0)                          # payload bytes of packet 0

arrow = table.to_arrow()                  # pyarrow.Table (header columns are zero-copy)
df = table.to_polars()                    # polars.DataFrame
df = table.to_pandas()                    # pandas.DataFrame (payload stays Arrow-backed)

Export needs the matching extra: to_arrow() and write_parquet() need pyarrow (fast-ccsds-parser[arrow]), to_polars() and to_pandas() add polars / pandas (the polars and pandas extras). Scanning itself works with the standard library alone; pass payload=False to keep only the header columns and use much less memory.

Time codes (CUC / CDS)

Telemetry packets conventionally start their secondary header with a CCSDS time code (CCSDS 301.0-B-4). parse_timecode() reads the P-field and decodes CUC (binary seconds + fraction since an epoch) or CDS (days + milliseconds of day + optional sub-milliseconds) into a timezone-aware datetime:

from fast_ccsds_parser import parse_timecode

tc = parse_timecode(pkt.payload)   # time code at the start of the data field
tc.timestamp                       # datetime, e.g. 2026-10-10 12:34:56.789012+00:00
pkt.payload[tc.n_bytes:]           # whatever follows the time code

Conveniences: pkt.timecode(offset=0) for a single packet and table.timecodes(offset=0) for a PacketTable (a list of timestamps in file order, handy as a DataFrame column).

  • Epochs. Level 1 codes use the CCSDS epoch (1958-01-01, exported as CCSDS_EPOCH). Level 2 (agency-defined epoch) codes need epoch=...; naive datetimes are assumed UTC.
  • Time scale. Level 1 CUC counts TAI seconds (the count does not step at leap seconds), so for a spec-compliant mission the result reads ahead of UTC by the accumulated leap seconds (37 s since 2017). Many missions write naive UTC counts instead; apply your mission's convention, this library deliberately does not guess. CDS maps arithmetically onto UTC days.
  • Precision. Sub-microsecond fractions (CUC with three fine octets, the 32-bit CDS sub-millisecond segment) are truncated to datetime's microsecond resolution.
  • CCS, agency-defined level 3/4 codes, and reserved identifications raise UnsupportedTimeCodeError.

Semantics to know

  • packet_length is zero-based. The data field is packet_length + 1 bytes, so a total packet is 6 + packet_length + 1 bytes. This is the number-one source of off-by-one bugs when handling CCSDS data, and the parser handles it for you.
  • payload = the full data field. Per CCSDS 133.0-B-2 the data field is an optional secondary header followed by user data; the payload bytes are returned as-is. Check header.has_secondary_header to know whether a mission-specific secondary header is in front. Decoding those layouts is out of scope for the structural parser (see the roadmap).
  • Idle packets (APID = 0x7FF) are yielded like any other packet by default; pass skip_idle=True to filter them out.
  • Strict mode (default) raises InvalidPacketError when the packet version is not 0, which usually means your byte stream is misaligned. Pass strict=False to parse anyway.
  • All errors derive from FastCCSDSError (a ValueError).

Several mature Python libraries already handle CCSDS space packets, and all of them can split packets and read primary headers without a mission packet definition. Pick the one that fits your job:

(as of 2026-10) fast-ccsds-parser 0.2 ccsdspy 2.0 space-packet-parser 6.2 spacepackets 0.32
Main focus Structural parsing (headers, packet boundaries, payloads) Field decoding from packet definitions Field decoding from XTCE definitions Building and parsing packets, incl. ECSS PUS and CFDP
Runtime dependencies None numpy, bitstruct, pyyaml, appdirs click, lxml, rich fastcrc, deprecation
Header-only parsing without definitions Yes Yes (ccsdspy.utils.read_primary_headers, split_by_apid) Yes (ccsds_generator) Yes (parse_space_packets)
Tabular output Arrow / Polars / pandas, Parquet export (optional extras) NumPy arrays (pandas via SPaC-Kit) xarray (optional extra) -
Live input Planned: asyncio TCP / WebSocket - Sockets and UDP (synchronous) Incremental parsing from a deque
Segmented packet reassembly - - Yes -

If you need to decode packet contents against a mission packet database, use ccsdspy or space-packet-parser; both are mature and used on flight missions. If you build or parse ECSS PUS / CFDP traffic, use spacepackets. fast-ccsds-parser focuses on getting large packet dumps and live streams into data-analysis tools quickly and with little memory.

Performance

benchmarks/bench.py measures packet throughput and peak memory, running each case in a fresh process. Results for 1,000,000 synthetic 32-byte packets (32 MB), Python 3.12, Apple M4, pandas 3.0.6, polars 2.0.0, pyarrow 26.0.0:

Case Time Peak RSS
fast-ccsds-parser: scan_file (headers only) 0.15 s 80 MB
fast-ccsds-parser: scan_file (with payloads) 0.29 s 122 MB
fast-ccsds-parser: scan_file().to_pandas() 0.45 s 188 MB
fast-ccsds-parser: scan_file().to_polars() 0.62 s 232 MB
fast-ccsds-parser: write_parquet() 0.61 s 232 MB
fast-ccsds-parser: header walk 0.47 s 53 MB
fast-ccsds-parser: iter_file (streaming) 0.88 s 23 MB
fast-ccsds-parser: pandas via per-packet dicts 2.25 s 677 MB
ccsdspy: read_primary_headers 21.28 s 123 MB
ccsdspy: iter_packet_bytes (raw bytes only) 0.41 s 63 MB
space_packet_parser: ccsds_generator (raw bytes only) 0.36 s 72 MB

(ccsdspy 2.0.1 and space_packet_parser 6.2.0 rows were measured in the same setup.)

What this shows:

  • The columnar scan is the fastest way to a DataFrame: to_pandas() is about 5x faster than building from per-packet dicts and uses about a quarter of the memory (188 MB vs 677 MB for 32 MB of input).
  • Streaming with iter_file uses the least memory of all cases.
  • Iterating raw packet bytes is about 2x faster in ccsdspy and space_packet_parser, because iter_file also decodes every header and copies each payload. This is a known optimization target.

Run it yourself:

pip install -e ".[all]" ccsdspy space_packet_parser
python benchmarks/bench.py                  # synthetic data
python benchmarks/bench.py path/to/dump.bin # your own dump

Roadmap

  • Phase 2a (done in 0.2.0): columnar output (scan_file(), to_arrow(), to_polars(), to_pandas(), write_parquet()) that keeps memory close to the input size, with Arrow / Polars / pandas as optional extras.
  • Phase 2b (done in 0.3.0): decoding CCSDS time codes (CUC / CDS) from secondary headers into timestamps (parse_timecode(), pkt.timecode(), table.timecodes()).
  • Phase 2c: asyncio parsing of live TCP socket / WebSocket streams (WebSocket as an optional extra).
  • Phase 3: move the hot loops that benchmarks show are slow to Rust (PyO3), keeping the Python API unchanged.

Development

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest            # offline test suite
ruff check .      # lint

Tests marked network additionally parse public, real-world packet dumps downloaded on the fly; they skip automatically when offline (CI runs them).

Releasing (PyPI)

Publishing uses trusted publishing (no API tokens). One-time setup is described in .github/workflows/publish-testpypi.yml (the file name is historical; it publishes to PyPI). Bump __version__ in src/fast_ccsds_parser/__init__.py, add a CHANGELOG entry, then:

git tag vX.Y.Z
git push origin vX.Y.Z

The workflow builds sdist + wheel, runs twine check, and uploads to PyPI.

License

MIT

Metadata

Release files for fast-ccsds-parser 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fast-ccsds-parser 0.3.0
File Size Uploaded
fast_ccsds_parser-0.3.0.tar.gz 48.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fast-ccsds-parser 0.3.0
File Interpreter ABI Platform
fast_ccsds_parser-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 72.9 kB

Release files / fast_ccsds_parser-0.3.0.tar.gz

Download URL fast_ccsds_parser-0.3.0.tar.gz
Size 48.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1d3dbeb0a7b902bff40cb8569fd87a82247dae8370895fa6bbc411e90f8e9ef8
BLAKE2b-256 checksum
How to use checksums
1f483d45383942871316c7202e81f1042c37e2f368d9eb64bd2f20c8387e86ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / fast_ccsds_parser-0.3.0-py3-none-any.whl

Download URL fast_ccsds_parser-0.3.0-py3-none-any.whl
Size 24.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
de9f9379262d7006860a904a958d0cd3e11c6f616c713985b1b540bf576998cc
BLAKE2b-256 checksum
How to use checksums
6f38334c936efa2a4beb18a019030695f9c4b2d7d0fd83eef2a0f9a5e472b25b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page