|
| union | category_t |
| | Type-safe union for the category field of a packet header. More...
|
| |
| union | type_t |
| | Type-safe union for the type field of a packet header. More...
|
| |
|
| enum | domain_t : uint8_t { DOMAIN_TM = 0x00
, DOMAIN_TC = 0x01
, DOMAIN_TEST = 0x02
} |
| | Top-level message domain. More...
|
| |
| enum | tm_category_t : uint8_t { TM_SDATA = 0x00
, TM_BDATA = 0x01
, TM_EVENT = 0x02
} |
| | Categories within the Telemetry domain. More...
|
| |
| enum | tm_sdata_type_t : uint8_t { TM_SDATA_SENSORS = 0x00
, TM_SDATA_STATUS = 0x01
} |
| | Types within the Telemetry / Small data category. More...
|
| |
| enum | tm_bdata_type_t : uint8_t { TM_BDATA_IMAGE = 0x00
, TM_BDATA_VIDEO = 0x01
} |
| | Types within the Telemetry / Big data category. More...
|
| |
| enum | tm_event_type_t : uint8_t { TM_EVENT_LOG = 0x00
, TM_EVENT_ERROR = 0x01
, TM_EVENT_CRASH = 0x02
} |
| | Types within the Telemetry / Event category. More...
|
| |
| enum | tc_category_t : uint8_t { TC_CTRL = 0x00
, TC_REQ = 0x01
} |
| | Categories within the Telecommand domain. More...
|
| |
| enum | tc_req_type_t : uint8_t {
TC_REQ_SENSORS = 0x00
, TC_REQ_STATUS = 0x01
, TC_REQ_IMAGE = 0x02
, TC_REQ_VIDEO = 0x03
,
TC_REQ_LOG = 0x04
, TC_REQ_ERROR = 0x05
, TC_REQ_CRASH = 0x06
} |
| | Types within the Telecommand / Request category. More...
|
| |
| enum | tc_ctrl_type_t : uint8_t { TC_CTRL_REBOOT = 0x00
} |
| | Types within the Telecommand / Control category. More...
|
| |
| enum | test_category_t : uint8_t { TEST_WIMG = 0x00
, TEST_FRM = 0x01
, TEST_MSG = 0x02
} |
| | Types within the Test domain. More...
|
| |
| enum | pkt_err_t {
PKT_OK = 0
, PKT_FAIL
, PKT_ERR_INVALID_ARG
, PKT_ERR_SYNC
,
PKT_ERR_CRC
} |
| | Return codes for packet utility functions. More...
|
| |
|
| packet_t * | pkt_create (void) |
| | Allocate and initialize a packet, with each byte set to 0.
|
| |
| void | pkt_destroy (packet_t *restrict pkt) |
| | Free a packet allocated by pkt_create().
|
| |
| pkt_enc_pool_t * | enc_pool_create (void) |
| | Allocate and initialize an encoder pool.
|
| |
| void | enc_pool_destroy (pkt_enc_pool_t *restrict pool) |
| | Free an encoder pool allocated by enc_pool_create().
|
| |
| pkt_dec_pool_t * | dec_pool_create (void) |
| | Allocate and initialize a decoder pool.
|
| |
| void | dec_pool_destroy (pkt_dec_pool_t *restrict pool) |
| | Free a decoder pool allocated by dec_pool_create().
|
| |
| pkt_err_t | pkt_encode (packet_t *restrict pkt, pkt_enc_pool_t *restrict pool) |
| | Finalize and encode a packet for transmission.
|
| |
| pkt_err_t | pkt_decode (const packet_t *restrict pkt, pkt_dec_pool_t *restrict pool, int8_t *restrict relative_loc) |
| | Validate and decode a received packet.
|
| |
| pkt_err_t | dec_pool_get_pkt_from_relative_loc (const pkt_dec_pool_t *restrict pool, int8_t relative_loc, packet_t *restrict pkt) |
| | Reconstruct a recovered packet from the decoder pool.
|
| |
| pkt_err_t | pkt_get_id (const packet_t *restrict pkt, uint16_t *id) |
| | Read the packet identifier.
|
| |
| pkt_err_t | header_sensor_config (header_t *restrict header) |
| | Configure a header for a current sensor measurement packet.
|
| |
| pkt_err_t | header_old_sensor_config (header_t *restrict header, uint16_t old_id) |
| | Configure a header for a retransmitted (old) sensor measurement packet.
|
| |
| pkt_err_t | pkt_get_header (const packet_t *restrict pkt, header_t *header) |
| | Unpack the header fields from a raw packet.
|
| |
| pkt_err_t | pkt_set_header (packet_t *restrict pkt, const header_t *restrict header) |
| | Pack header fields into a raw packet.
|
| |
| pkt_err_t | pkt_get_payload (const packet_t *restrict pkt, uint8_t *payload, size_t len) |
| | Copy the payload field out of a packet.
|
| |
| pkt_err_t | pkt_set_payload (packet_t *restrict pkt, const uint8_t *restrict payload, size_t len) |
| | Copy data into the payload field of a packet.
|
| |
| bool | pkt_is_chunked (const packet_t *restrict pkt) |
| | Check whether a packet carries chunked data.
|
| |
| pkt_err_t | pkt_log (const packet_t *restrict pkt) |
| | Log a human-readable dump of a packet to the PAL logger.
|
| |
| pkt_err_t | pkt_test_random_set_sync8 (packet_t *restrict pkt, bool *failed) |
| | Randomly corrupt the SYNC8 field for testing purposes.
|
| |
| pkt_err_t | pkt_test_random_set_crc16 (packet_t *restrict pkt) |
| | Randomly corrupt the CRC16 field for testing purposes.
|
| |
- Copyright
- Copyright (c) 2026 Adrien Chevrier. Under AGPL-3.0-or-later license.
Implementation of the Plant-B application layer protocol. Provides packet creation, encoding, decoding, header and payload access, CRC16 error detection and XOR-based single-packet error recovery.
A packet is 111 bytes laid out as follows:
| Field | Size (bytes) | Description |
| SYNC8 | 1 | Synchronization word (0xAA) |
| ID | 2 | Globally incrementing packet identifier |
| DCT | 1 | Domain (bits 7-6), Category (5-4), Type (3-0) |
| INDEX | 2 | Chunk sequence number (0-based) |
| TOTAL | 2 | Total chunks minus 1 |
| PAYLOAD | 48 | Useful data |
| XOR_PART | 53 | XOR redundancy over DCT+INDEX+TOTAL+PAYLOAD |
| CRC16 | 2 | CRC16-CCITT/FALSE over ID through XOR_PART |
All multi-byte fields are stored in big-endian byte order.
- Todo
- Provide an implementation for images, video streams and events.
◆ category_t
Type-safe union for the category field of a packet header.
The active member depends on the domain field of the enclosing header_t. Use u8 for raw byte access when packing/unpacking the DCT byte.
| Data Fields |
|
tc_category_t |
tc |
Category interpreted as a TC category. |
|
test_category_t |
test |
Category interpreted as a Test type. |
|
tm_category_t |
tm |
Category interpreted as a TM category. |
|
uint8_t |
u8 |
Raw byte value for DCT packing. |
◆ type_t
Type-safe union for the type field of a packet header.
The active member depends on both the domain and category fields of the enclosing header_t. Use u8 for raw byte access when packing/unpacking the DCT byte.
◆ header_t
Decoded representation of the packet header fields.
Used as an intermediate, human-readable form between raw packet bytes and application logic. The DCT byte is unpacked into its three components; index and total are decoded from big-endian.
| Data Fields |
|
category_t |
category |
Message category (bits 5–4 of DCT). |
|
domain_t |
domain |
Message domain (bits 7–6 of DCT). |
|
uint16_t |
index |
Chunk sequence number (0-based). |
|
uint16_t |
total |
Total number of chunks minus 1. |
|
type_t |
type |
Message type (bits 3–0 of DCT). |
◆ PACKET_BYTES
| #define PACKET_BYTES (111) |
Total packet size in bytes.
◆ PAYLOAD_BYTES
| #define PAYLOAD_BYTES (48) |
Size of the payload field in bytes.
◆ PKT_NO_LOCATION
| #define PKT_NO_LOCATION (-1) |
Sentinel value returned by pkt_decode() when no packet was recovered from the decoding pool.
◆ packet_t
Opaque packet handle.
The internal layout of a packet is not exposed. Use the provided API functions to create, populate, encode, decode and destroy packets.
◆ pkt_dec_pool_t
Opaque decoder pool handle.
Holds the receiver circular buffer of exactly 32 slots storing incoming packets, the XOR_PART redundancy data, and the reception bitmap used to track received and lost packets and to drive recovery attempts.
◆ pkt_enc_pool_t
Opaque encoder pool handle.
Holds the transmitter circular buffer of exactly 32 slots used to compute the XOR_PART field of each outgoing packet.
◆ domain_t
Top-level message domain.
Encoded in bits 7–6 of the DCT byte.
| Enumerator |
|---|
| DOMAIN_TM | Telemetry domain (CubeSat to Ground).
|
| DOMAIN_TC | Telecommand domain (Ground to CubeSat).
|
| DOMAIN_TEST | Test domain (Ground to CubeSat, for tests only).
|
◆ pkt_err_t
Return codes for packet utility functions.
| Enumerator |
|---|
| PKT_OK | Operation completed successfully.
|
| PKT_FAIL | Generic failure.
|
| PKT_ERR_INVALID_ARG | One or more arguments are invalid or NULL.
|
| PKT_ERR_SYNC | SYNC8 word mismatch: packet boundary error.
|
| PKT_ERR_CRC | CRC16 mismatch: packet is corrupted.
|
◆ tc_category_t
Categories within the Telecommand domain.
Encoded in bits 5–4 of the DCT byte when domain is DOMAIN_TC.
| Enumerator |
|---|
| TC_CTRL | Control commands (e.g. reboot).
|
| TC_REQ | Data or status requests.
|
◆ tc_ctrl_type_t
Types within the Telecommand / Control category.
Encoded in bits 3–0 of the DCT byte.
- Todo
- Add other useful control sequences.
| Enumerator |
|---|
| TC_CTRL_REBOOT | Force a system reboot.
|
◆ tc_req_type_t
Types within the Telecommand / Request category.
Encoded in bits 3–0 of the DCT byte.
| Enumerator |
|---|
| TC_REQ_SENSORS | Request current sensor measurements.
|
| TC_REQ_STATUS | Request current system status.
|
| TC_REQ_IMAGE | Request image transmission.
|
| TC_REQ_VIDEO | Request video transmission.
|
| TC_REQ_LOG | Request log retrieval.
|
| TC_REQ_ERROR | Request error list.
|
| TC_REQ_CRASH | Request last crash information.
|
◆ test_category_t
Types within the Test domain.
The Test domain has no category level; this enum encodes the type directly. Encoded in bits 3–0 of the DCT byte when domain is DOMAIN_TEST.
| Enumerator |
|---|
| TEST_WIMG | Write an image to OBC memory.
|
| TEST_FRM | Remove a file from OBC file system.
|
| TEST_MSG | Send a text message.
|
◆ tm_bdata_type_t
Types within the Telemetry / Big data category.
Encoded in bits 3–0 of the DCT byte.
- Note
- Reserved for future use.
| Enumerator |
|---|
| TM_BDATA_IMAGE | Image data (JPEG).
|
| TM_BDATA_VIDEO | Video stream (H.264).
|
◆ tm_category_t
Categories within the Telemetry domain.
Encoded in bits 5–4 of the DCT byte when domain is DOMAIN_TM.
| Enumerator |
|---|
| TM_SDATA | Small data (sensor measurements, system status).
|
| TM_BDATA | Big data (images, video streams).
|
| TM_EVENT | Events (logs, errors, crashes).
|
◆ tm_event_type_t
Types within the Telemetry / Event category.
Encoded in bits 3–0 of the DCT byte.
- Note
- Reserved for future use.
| Enumerator |
|---|
| TM_EVENT_LOG | Normal event log.
|
| TM_EVENT_ERROR | Non-critical error.
|
| TM_EVENT_CRASH | Critical failure / crash report.
|
◆ tm_sdata_type_t
Types within the Telemetry / Small data category.
Encoded in bits 3–0 of the DCT byte.
| Enumerator |
|---|
| TM_SDATA_SENSORS | Sensor measurements payload.
|
| TM_SDATA_STATUS | System status payload.
|
◆ dec_pool_create()
Allocate and initialize a decoder pool.
The decoder pool holds exactly 32 slots storing incoming packets and their XOR_PART redundancy data, along with a reception bitmap. Pass it to pkt_decode() to enable automatic single-packet error recovery.
- Returns
- Pointer to the new
pkt_dec_pool_t, NULL on allocation failure.
◆ dec_pool_destroy()
Free a decoder pool allocated by dec_pool_create().
- Parameters
-
| [in,out] | pool | Pool to destroy. Logs a warning if the pool is NULL. |
◆ dec_pool_get_pkt_from_relative_loc()
Reconstruct a recovered packet from the decoder pool.
After pkt_decode() reports a recovered packet via relative_loc, call this function to copy that slot back into a packet_t for application-level processing.
- Parameters
-
| [in] | pool | Decoder pool. |
| [in] | relative_loc | Relative index returned by pkt_decode(). Must be in the range (PKT_NO_LOCATION, POOL_DEPTH). |
| [out] | pkt | Packet to fill with the recovered data. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL, relative_loc equals PKT_NO_LOCATION or relative_loc exceeds the pool depth. |
◆ enc_pool_create()
Allocate and initialize an encoder pool.
The encoder pool holds exactly 32 slots used to compute the XOR_PART field of each outgoing packet. Pass it to pkt_encode() to enable XOR-based error recovery on the receiver side.
- Returns
- Pointer to the new
pkt_enc_pool_t, NULL on allocation failure.
◆ enc_pool_destroy()
Free an encoder pool allocated by enc_pool_create().
- Parameters
-
| [in,out] | pool | Pool to destroy. Logs a warning if the pool is NULL. |
◆ header_old_sensor_config()
Configure a header for a retransmitted (old) sensor measurement packet.
Sets domain to DOMAIN_TM, category to TM_SDATA, type to TM_SDATA_SENSORS. The index field carries the original packet ID and total is set to 0xFFFF to signal a retransmission.
- Parameters
-
| [out] | header | Header to configure. |
| [in] | old_id | Original packet ID being retransmitted. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | header is NULL. |
- Warning
- This behaviour is specific to this implementation and is not part of the Plant-B application layer protocol. It highlights a limitation of the current sensor payload format, which carries no timestamp or any other field that would allow tracking when measurements were taken.
◆ header_sensor_config()
Configure a header for a current sensor measurement packet.
Sets domain to DOMAIN_TM, category to TM_SDATA, type to TM_SDATA_SENSORS, and clears index and total (non-chunked).
- Parameters
-
| [out] | header | Header to configure. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | header is NULL. |
◆ pkt_create()
Allocate and initialize a packet, with each byte set to 0.
- Returns
- Pointer to the new
packet_t, NULL on allocation failure.
◆ pkt_decode()
Validate and decode a received packet.
Checks the SYNC8 word and the CRC16. If both pass, the packet is stored in the decoder pool (if provided), the reception bitmap is updated, and a single-packet XOR recovery is attempted if any slot in the pool is unknown. The relative location of a successfully recovered packet is written to relative_loc.
- Parameters
-
| [out] | pkt | Packet to decode. |
| [in,out] | pool | Decoder pool. If NULL, no recovery is attempted and a warning is logged. |
| [out] | relative_loc | Relative index (from the current pool head) of the recovered packet or PKT_NO_LOCATION if none was recovered. May be NULL if not needed. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | pkt is NULL. |
| PKT_ERR_SYNC | SYNC8 mismatch. |
| PKT_ERR_CRC | CRC16 mismatch. |
◆ pkt_destroy()
| void pkt_destroy |
( |
packet_t *restrict | pkt | ) |
|
Free a packet allocated by pkt_create().
- Parameters
-
| [in,out] | pkt | Packet to destroy. Logs a warning if the packet is NULL. |
◆ pkt_encode()
Finalize and encode a packet for transmission.
Sets the SYNC8 word, assigns a globally unique ID (atomically incremented, thread safe), computes the XOR_PART field if a pool is provided and appends the CRC16. The header and payload must have been set beforehand via pkt_set_header() and pkt_set_payload().
- Parameters
-
| [out] | pkt | Packet to encode. |
| [in,out] | pool | Encoder pool used to compute XOR_PART. If NULL, the XOR_PART field is left as is and a warning is logged. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | pkt is NULL. |
◆ pkt_get_header()
Unpack the header fields from a raw packet.
Decodes the DCT byte into domain, category and type then converts INDEX and TOTAL from big-endian to host byte order.
- Parameters
-
| [in] | pkt | Packet to read from. |
| [out] | header | Decoded header. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL. |
◆ pkt_get_id()
Read the packet identifier.
- Parameters
-
| [in] | pkt | Packet to read from. |
| [out] | id | Decoded 16-bit packet ID. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL. |
◆ pkt_get_payload()
| pkt_err_t pkt_get_payload |
( |
const packet_t *restrict | pkt, |
|
|
uint8_t * | payload, |
|
|
size_t | len ) |
Copy the payload field out of a packet.
Always copies exactly PAYLOAD_BYTES bytes into payload.
- Parameters
-
| [in] | pkt | Packet to read from. |
| [out] | payload | Destination buffer. |
| [in] | len | Size of payload in bytes. Must be >= PAYLOAD_BYTES. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL or len < PAYLOAD_BYTES. |
◆ pkt_is_chunked()
| bool pkt_is_chunked |
( |
const packet_t *restrict | pkt | ) |
|
|
inline |
Check whether a packet carries chunked data.
A packet is considered chunked if its TOTAL field is non-zero, meaning it is one of several chunks making up a larger payload.
- Parameters
-
- Return values
-
| true | The packet is part of a chunked sequence. |
| false | The packet carries a self-contained payload. |
◆ pkt_log()
Log a human-readable dump of a packet to the PAL logger.
Prints the packet ID, domain, category, type, index, total, payload bytes and XOR_PART bytes at INFO level.
- Parameters
-
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | pkt is NULL. |
◆ pkt_set_header()
Pack header fields into a raw packet.
Encodes domain (bits 7–6), category (bits 5–4), and type (bits 3–0) into the DCT byte, and stores INDEX and TOTAL in big-endian byte order.
- Parameters
-
| [in,out] | pkt | Packet to write to. |
| [in] | header | Header to pack. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL. |
◆ pkt_set_payload()
| pkt_err_t pkt_set_payload |
( |
packet_t *restrict | pkt, |
|
|
const uint8_t *restrict | payload, |
|
|
size_t | len ) |
Copy data into the payload field of a packet.
Copies len bytes from payload into the packet. If len is smaller than PAYLOAD_BYTES, the remainder of the field is zero-padded.
- Parameters
-
| [in,out] | pkt | Packet to write to. |
| [in] | payload | Source data buffer. |
| [in] | len | Number of bytes to copy. Must be in (0, PAYLOAD_BYTES]. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | Any pointer is NULL or len is out of bounds. |
◆ pkt_test_random_set_crc16()
Randomly corrupt the CRC16 field for testing purposes.
With a probability of 1/RAND_THRESHOLD, replaces CRC16 with a random 16-bit value. Uses the ESP32 hardware RNG.
- Parameters
-
| [in,out] | pkt | Packet whose CRC16 may be corrupted. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | pkt is NULL. |
- Note
- For tests only.
◆ pkt_test_random_set_sync8()
Randomly corrupt the SYNC8 field for testing purposes.
With a probability of 1/RAND_THRESHOLD, sets SYNC8 to 0x00 and writes true to failed. Uses the ESP32 hardware RNG.
- Parameters
-
| [in,out] | pkt | Packet whose SYNC8 may be corrupted. |
| [out] | failed | Set to true if the field was corrupted, false otherwise. |
- Return values
-
| PKT_OK | On success. |
| PKT_ERR_INVALID_ARG | pkt is NULL. |
- Note
- For tests only.