Skip to content

Document generic (non-CCSDS) packet parsing with a custom generator example - #288

Merged
medley56 merged 2 commits into
mainfrom
190-document-generic-packet-parsing
Sep 16, 2026
Merged

medley56 merged 2 commits into
mainfrom
190-document-generic-packet-parsing

Conversation

@medley56

Copy link
Copy Markdown
Member

Summary

Closes #190.

The library has always supported non-CCSDS packet formats via user-supplied packet bytes
generators, but there was no worked example of doing so. This adds one, plus the small amount of
documentation needed to make the mechanism discoverable.

New example: examples/parsing_non_ccsds_packets.py

It demonstrates, end to end and with no external data files:

  • A made-up sensor format with no CCSDS header at all:
    | SYNC 0xDEADBEEF (4 B) | PAYLOAD_LENGTH (1 B) | COUNTER (2 B) | TEMPERATURE (2 B) |
  • A custom packet bytes generator, sync_marker_generator, that finds packet boundaries from a
    sync marker and a packet-defined length field — the "use a custom field in the packet to
    determine its length" case named in the issue.
  • That the yielded chunk must span every byte the XTCE container describes, sync marker included,
    or parse_bytes will warn about a bit-count mismatch.
  • That a definition whose root container is not named CCSDSPacket must pass
    root_container_name to parse_bytesload_xtce does not accept that argument.
  • Driving the same custom generator through create_dataset via packet_bytes_generator, and
    that packets with no PKT_APID field are grouped under key 0.

The XTCE is embedded inline as a string rather than added as a fixture under tests/test_data/.
The existing non-CCSDS fixtures don't fit (test_xtce_4byte.xml has no length field;
udp_packet.xml is already served by the built-in udp_generator), and a self-contained script has
no data-path dependency when CI runs it. This is the same inline-XTCE pattern the Quickstart in
getting_started.md already uses.

Documentation

docs/source/user_guide/generators.md already carried the conceptual prose ("XTCE is not limited
to representing CCSDS packet structures..."), so this change is scoped to the missing example plus a
tightened generator contract. The "Writing Custom Generators" section now states what a generator
must satisfy (accepts a binary source; yields exactly one packet per iteration; each chunk contains
every byte the container describes; may yield a bytes subclass carrying metadata for
packet_filter), adds the length-field generator case, and links the new script. The
"XTCE is not CCSDS-specific" paragraph was promoted to its own heading so it is linkable. The page
is otherwise unchanged. docs/source/examples.md gets a bullet for the new script.

Testing

The example is exercised by the CI run-examples job, which cd examples and runs every top-level
*.py. That is the test coverage for this change — the example is itself the test, and it fails
loudly if the documented behavior regresses. Verified locally from both examples/ and the repo
root, and under python -W error (clean — no bit-count-mismatch warning). Existing examples all
still run, pre-commit run --all-files passes, the docs build with no new warnings, and the full
suite is 481 passed.

One design note: the example deliberately uses a single concrete container, with no abstract
containers or restriction criteria, because the UnrecognizedPacketTypeError message in
definitions.py interpolates packet['PKT_APID'] and would raise KeyError on a non-CCSDS
definition. That is tracked separately as #276 and is out of scope here.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings September 13, 2026 05:42
@codecov

codecov Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.62%. Comparing base (fb8b015) to head (e19b666).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #288   +/-   ##
=======================================
  Coverage   94.62%   94.62%           
=======================================
  Files          49       49           
  Lines        4203     4203           
=======================================
  Hits         3977     3977           
  Misses        226      226           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate socket-handling issues remain, along with requested documentation and regression-test improvements.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds a runnable non-CCSDS packet-parsing example with custom generator documentation.

Changes:

  • Adds sync-marker and length-based packet parsing example.
  • Documents generator contracts and custom length handling.
  • Links the example and updates the changelog.
File summaries
File Summary Final comments
examples/parsing_non_ccsds_packets.py Custom non-CCSDS generator example Moderate (3 votes): socket handling mismatches the advertised API. Nit (1): add stronger regression assertions.
docs/source/user_guide/generators.md Generator contracts and usage guidance Moderate (2 votes): socket handling is inconsistent with the documented source types. Nit (1): the PKT_APID grouping statement is too broad.
docs/source/examples.md Links the new example None.
CHANGELOG.md Adds the unreleased feature entry None.
Review details

Suppressed comments (3)

docs/source/user_guide/generators.md:118

  • If the input ends after a complete sync marker but before its length byte, this indexing raises IndexError instead of taking the documented truncated-stream path. Add a header-length check before reading payload_length, as the runnable implementation does.
        payload_length = buffer[start + len(sync_marker)]

docs/source/user_guide/generators.md:144

  • create_dataset does not inspect the parsed PKT_APID; it uses an apid property on the yielded bytes and falls back to 0 only when that property is absent (space_packet_parser/xarr.py:212-216). A custom bytes subclass can therefore route packets with no PKT_APID field to a nonzero dataset, so this statement is too broad.
returns its Datasets keyed by APID, so packets that have no `PKT_APID` field are all grouped under
key `0`.

examples/parsing_non_ccsds_packets.py:165

  • This new behavior is only smoke-tested: the loop prints parsed packets and later indexes datasets[0], while CI runs plain python, so a shortened-but-nonempty result or wrong packet values can still pass and warnings are not errors. Add assertions for the packet count/field values and dataset length (or a focused generator test) so the documented boundary and grouping behavior is actually regression-tested.
    for packet_bytes in sync_marker_generator(stream):
        packet = packet_definition.parse_bytes(packet_bytes, root_container_name=ROOT_CONTAINER_NAME)
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/source/user_guide/generators.md
Comment thread examples/parsing_non_ccsds_packets.py
medley56 and others added 2 commits September 16, 2026 23:10
Add examples/parsing_non_ccsds_packets.py, a runnable demonstration of
parsing a packet format that has no CCSDS header. The example defines a
made-up sensor format delimited by a 0xDEADBEEF sync marker with a
packet-defined length byte, supplies a custom packet bytes generator that
finds packet boundaries from those two fields, parses the packets both
one at a time and through create_dataset, and shows that a definition
whose root container is not named "CCSDSPacket" must pass
root_container_name to parse_bytes.

The XTCE is embedded inline rather than added to tests/test_data/ because
the existing non-CCSDS fixtures do not demonstrate a packet-defined
length field (test_xtce_4byte.xml has no length field, and udp_packet.xml
is already served by the built-in udp_generator), and because the CI
run-examples job executes every script in examples/ from that directory,
so a self-contained script has no data-path dependency. This mirrors the
inline-XTCE pattern already used by the Quickstart in getting_started.md.

The "XTCE is not limited to CCSDS" prose already existed in the Packet
Bytes Generators user guide page, so the doc changes are scoped to
stating the generator contract, adding the length-field generator case,
and linking the new script from the Examples page.

Co-Authored-By: Claude Opus 5 <[email protected]>
- Drop the unimplemented socket.socket support from sync_marker_generator's
  signature/docstring in both the docs snippet and the runnable example;
  point readers to the socket-based IDEX waveform example and to
  ccsds_generator, which does support sockets
- Add the missing header-length bounds check in the docs snippet so it
  matches the runnable example's truncation handling
- Correct the create_dataset APID-grouping explanation: keyed by the apid
  property on the yielded bytes object, not the parsed PKT_APID field
- Add assertions on parsed packet values and dataset length to the
  runnable example so it regression-tests the documented behavior

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@medley56
medley56 force-pushed the 190-document-generic-packet-parsing branch from 6a28e58 to e19b666 Compare September 16, 2026 23:39

Copy link
Copy Markdown
Member Author

Replying to the three suppressed findings from the Copilot review (no separate threads were created for these):

  • docs/source/user_guide/generators.md:118 — Fixed in e19b666: added the missing start + header_length > len(buffer) bounds check before reading payload_length, matching the runnable example's existing guard.
  • docs/source/user_guide/generators.md:144 — Fixed in e19b666: reworded to describe the actual behavior — create_dataset keys by the apid property on the yielded bytes object (falling back to 0 when absent), not by the parsed PKT_APID field. Same fix applied to the equivalent comment in examples/parsing_non_ccsds_packets.py for consistency.
  • examples/parsing_non_ccsds_packets.py:165 — Fixed in e19b666: added assertions on parsed packet count/COUNTER/TEMPERATURE values and on the resulting dataset length, so the example fails loudly instead of only printing if the boundary or grouping behavior regresses.

🤖 AI-assisted comment, reviewed and approved by @medley56 before posting.

@medley56
medley56 merged commit c55e513 into main Sep 16, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document Generic Packet Parsing

2 participants