|
1 | 1 | # EmbeddedSpacePacket |
2 | | -Minimum CCSDS SpacePacket implementation in C for embedded applications. |
3 | 2 |
|
4 | | -Contents |
5 | | -- tiny header and implementation in `include/` and `src/` |
6 | | -- example in `examples/` |
7 | | -- simple host test in `tests/` |
8 | | -- `Makefile` to build library, example and test |
| 3 | +Minimal, embedded-optimized implementation of the **CCSDS Space Packet Protocol** in C, |
| 4 | +following the international standard. |
9 | 5 |
|
10 | | -Build & run (host): |
| 6 | +## Standards Compliance |
11 | 7 |
|
12 | | -1. Build everything: |
| 8 | +- **CCSDS 133.0-B-2**: Space Packet Protocol |
13 | 9 |
|
14 | | - make |
| 10 | +## Features |
15 | 11 |
|
16 | | -2. Run example: |
| 12 | +### Core Protocol Implementation |
17 | 13 |
|
18 | | - ./examples/spacepacket_example |
| 14 | +- **Primary Header**: 6-byte CCSDS-compliant header with version, type, APID, sequence |
| 15 | + control and packet length fields |
| 16 | +- **Secondary Header**: Optional application-defined secondary header with configurable |
| 17 | + length |
| 18 | +- **Sequence Control**: Segmentation flags (unsegmented, first, continuation, last) and |
| 19 | + 14-bit sequence counter |
| 20 | +- **CRC-16-CCITT**: Optional CRC over secondary header and payload, signalled by a flag |
| 21 | + in the secondary header |
| 22 | +- **Serializer / Parser**: Symmetric encode and decode paths with big-endian network byte |
| 23 | + order |
19 | 24 |
|
20 | | -3. Run tests: |
| 25 | +### Design Principles |
21 | 26 |
|
22 | | - ./tests/ctest |
| 27 | +- **Minimal footprint**: Single header + single source file |
| 28 | +- **Zero allocation**: Stack-based, no dynamic memory in the library |
| 29 | +- **Embedded-optimized**: No external dependencies, no OS primitives |
| 30 | +- **Portable**: Pure C11, big-endian network byte order for all header fields |
| 31 | +- **Safe**: Input validation on every public API entry point |
23 | 32 |
|
24 | | -Notes |
25 | | -- This is a minimal, small-footprint implementation designed for embedded use. |
26 | | -- The primary header implemented is CCSDS-like (6 bytes). The packet length field |
27 | | - in the header stores `payload_len - 1` when serializing and the parser reconstructs |
28 | | - `payload_len = header_length + 1`. |
| 33 | +## Project Structure |
29 | 34 |
|
30 | | -CCSDS Space Packet (brief) |
31 | | -- **Primary header (6 bytes):** bytes 0-1 hold version (3 bits), packet type (1 bit), |
32 | | - secondary header flag (1 bit) and APID (11 bits). Bytes 2-3 are the sequence control |
33 | | - field: segmentation/sequence flags (2 bits) and packet sequence count (14 bits). Bytes |
34 | | - 4-5 are the Packet Length field (16 bits) which encodes the length of the Packet Data |
35 | | - Field minus 1 (i.e., PacketLength = N-1 where N is data-field octets). |
36 | | -- **Packet Data Field:** follows the primary header and contains an optional secondary |
37 | | - header (if the secondary header flag is set) followed by user/application data. |
38 | | -- **Secondary header:** optional, format and semantics are application-dependent (time |
39 | | - tags, service headers, etc.). The presence is indicated by the secondary header flag. |
40 | | -- **Segmentation/Sequence Flags:** indicate if the packet is standalone (unsegmented), |
41 | | - first, continuation or last segment when a logical packet is split over multiple |
42 | | - sequence counts. |
43 | | -- **APID (Application Process ID):** identifies the source/consumer application or |
44 | | - logical stream of packets. |
45 | | -- **Error control:** CCSDS Space Packet by itself does not mandate a particular CRC in |
46 | | - the primary header; error control is usually provided in lower or higher layers or |
47 | | - via application-specific fields. This repository adds an optional CRC covering the |
48 | | - secondary header and payload as an example. |
| 35 | +``` |
| 36 | +EmbeddedSpacePacket/ |
| 37 | +├── include/ |
| 38 | +│ └── space_packet.h # Public API and types |
| 39 | +├── src/ |
| 40 | +│ └── space_packet.c # Serializer, parser, CRC and helpers |
| 41 | +├── examples/ |
| 42 | +│ └── main.c # Example: build, serialize and parse a packet |
| 43 | +├── tests/ |
| 44 | +│ ├── cunit.h # Minimal test framework |
| 45 | +│ └── unit_tests.c # Unit tests |
| 46 | +├── Makefile |
| 47 | +└── README.md |
| 48 | +``` |
49 | 49 |
|
| 50 | +## Building |
50 | 51 |
|
51 | | -Links: |
52 | | -- https://ccsds.org/Pubs/133x0b2e2.pdf (CCSDS Space Packet Standard) |
| 52 | +### Build Everything |
| 53 | + |
| 54 | +```bash |
| 55 | +make |
| 56 | +``` |
| 57 | + |
| 58 | +Builds the static library, the example binary and the test binary. |
| 59 | + |
| 60 | +### Build Library Only |
| 61 | + |
| 62 | +```bash |
| 63 | +make lib |
| 64 | +# Produces: libspacepacket.a |
| 65 | +``` |
| 66 | + |
| 67 | +### Build Example |
| 68 | + |
| 69 | +```bash |
| 70 | +make example |
| 71 | +./examples/spacepacket_example |
| 72 | +``` |
| 73 | + |
| 74 | +### Run Tests |
| 75 | + |
| 76 | +```bash |
| 77 | +make ctest |
| 78 | +./tests/ctest |
| 79 | +``` |
| 80 | + |
| 81 | +### Clean |
| 82 | + |
| 83 | +```bash |
| 84 | +make clean |
| 85 | +``` |
| 86 | + |
| 87 | +## Quick Start |
| 88 | + |
| 89 | +### Create and Serialize a Space Packet |
| 90 | + |
| 91 | +```c |
| 92 | +#include "space_packet.h" |
| 93 | +#include <stdio.h> |
| 94 | +#include <string.h> |
| 95 | + |
| 96 | +int main(void) { |
| 97 | + const uint8_t payload[] = {'H', 'e', 'l', 'l', 'o', ' ', 'S', 'P'}; |
| 98 | + |
| 99 | + /* Secondary header: flags byte (LSB=1 requests CRC), length byte (0 extra bytes) */ |
| 100 | + const uint8_t sec_hdr[] = {0x01, 0x00}; |
| 101 | + |
| 102 | + sp_packet_t pkt; |
| 103 | + sp_packet_init(&pkt); |
| 104 | + sp_set_primary_header(&pkt, |
| 105 | + 0, /* version */ |
| 106 | + 0, /* type: telemetry */ |
| 107 | + 1, /* secondary header present */ |
| 108 | + 0x100, /* APID */ |
| 109 | + SP_SEQ_FLAG_UNSEGMENTED, /* sequence flags */ |
| 110 | + 1 /* sequence count */ |
| 111 | + ); |
| 112 | + sp_set_secondary_header(&pkt, sec_hdr, sizeof(sec_hdr)); |
| 113 | + sp_set_payload(&pkt, payload, sizeof(payload)); |
| 114 | + |
| 115 | + uint8_t buf[256]; |
| 116 | + size_t n = sp_packet_build_frame(&pkt, buf, sizeof(buf)); |
| 117 | + if (n == 0) { |
| 118 | + printf("Serialization failed\n"); |
| 119 | + return 1; |
| 120 | + } |
| 121 | + printf("Serialized %zu bytes\n", n); |
| 122 | + return 0; |
| 123 | +} |
| 124 | +``` |
| 125 | +
|
| 126 | +### Parse an Incoming Packet |
| 127 | +
|
| 128 | +```c |
| 129 | +sp_packet_t parsed; |
| 130 | +if (sp_packet_parse(&parsed, buf, n)) { |
| 131 | + printf("APID: 0x%03X, seq count: %u, payload: %u bytes\n", |
| 132 | + parsed.ph.apid, |
| 133 | + parsed.ph.seq_count, |
| 134 | + parsed.payload_len); |
| 135 | +} else { |
| 136 | + printf("Parse failed (bad length, corrupt CRC, etc.)\n"); |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +### Compute a CRC Independently |
| 141 | + |
| 142 | +```c |
| 143 | +uint16_t crc = sp_crc16_ccitt(data, data_len); |
| 144 | +printf("CRC-16-CCITT: 0x%04X\n", crc); |
| 145 | +``` |
| 146 | +
|
| 147 | +## API Reference |
| 148 | +
|
| 149 | +### Lifecycle |
| 150 | +
|
| 151 | +```c |
| 152 | +/* Zero-initialize a packet structure. */ |
| 153 | +void sp_packet_init(sp_packet_t *pkt); |
| 154 | +``` |
| 155 | + |
| 156 | +### Building a Packet |
| 157 | + |
| 158 | +```c |
| 159 | +/* Set primary header fields (values are masked to valid bit widths). */ |
| 160 | +void sp_set_primary_header(sp_packet_t *pkt, |
| 161 | + uint8_t version, |
| 162 | + uint8_t type, |
| 163 | + uint8_t sec_hdr_flag, |
| 164 | + uint16_t apid, |
| 165 | + sp_seq_flag_t seq_flags, |
| 166 | + uint16_t seq_count); |
| 167 | + |
| 168 | +/* Set secondary header (pointer into application-owned memory). */ |
| 169 | +void sp_set_secondary_header(sp_packet_t *pkt, |
| 170 | + const uint8_t *sec_hdr, |
| 171 | + uint16_t sec_hdr_len); |
| 172 | + |
| 173 | +/* Set payload (pointer into application-owned memory). */ |
| 174 | +void sp_set_payload(sp_packet_t *pkt, |
| 175 | + const uint8_t *payload, |
| 176 | + uint16_t payload_len); |
| 177 | + |
| 178 | +/* Serialize to buffer. Returns bytes written, or 0 on error. */ |
| 179 | +size_t sp_packet_build_frame(const sp_packet_t *pkt, |
| 180 | + uint8_t *buf, |
| 181 | + size_t buf_len); |
| 182 | +``` |
| 183 | +
|
| 184 | +### Low-Level Serialization |
| 185 | +
|
| 186 | +```c |
| 187 | +/* Required output buffer size for the given packet. */ |
| 188 | +size_t sp_packet_serialize_size(const sp_packet_t *pkt); |
| 189 | +
|
| 190 | +/* Serialize into buf. Returns bytes written, or 0 on error. */ |
| 191 | +size_t sp_packet_serialize(const sp_packet_t *pkt, |
| 192 | + uint8_t *buf, |
| 193 | + size_t buf_len); |
| 194 | +
|
| 195 | +/* Parse buf into out. out->payload points into buf (no copy). */ |
| 196 | +/* Returns 1 on success, 0 on failure (short buffer, bad CRC, etc.). */ |
| 197 | +int sp_packet_parse(sp_packet_t *out, uint8_t *buf, size_t buf_len); |
| 198 | +``` |
| 199 | + |
| 200 | +### Utilities |
| 201 | + |
| 202 | +```c |
| 203 | +/* CRC-16-CCITT (polynomial 0x1021, init 0xFFFF). */ |
| 204 | +uint16_t sp_crc16_ccitt(const uint8_t *data, size_t len); |
| 205 | +``` |
| 206 | +
|
| 207 | +### Types |
| 208 | +
|
| 209 | +```c |
| 210 | +typedef enum { |
| 211 | + SP_SEQ_FLAG_UNSEGMENTED = 0, |
| 212 | + SP_SEQ_FLAG_FIRST_SEGMENT = 1, |
| 213 | + SP_SEQ_FLAG_CONTINUING_SEGMENT = 2, |
| 214 | + SP_SEQ_FLAG_LAST_SEGMENT = 3 |
| 215 | +} sp_seq_flag_t; |
| 216 | +``` |
| 217 | + |
| 218 | +## Memory Usage (Estimated) |
| 219 | + |
| 220 | +- **Library (stripped)**: < 2 KB |
| 221 | +- **`sp_packet_t` struct**: ~40 bytes (stack) |
| 222 | +- **Serialization buffer**: 6 bytes header + secondary header + payload + 2 bytes CRC |
| 223 | +- **No heap usage**: all allocations are caller-supplied |
| 224 | + |
| 225 | +## CCSDS Space Packet — Protocol Notes |
| 226 | + |
| 227 | +- **Primary header (6 bytes):** bytes 0–1 hold version (3 bits), packet type (1 bit), |
| 228 | + secondary header flag (1 bit) and APID (11 bits). Bytes 2–3 are the sequence control |
| 229 | + field: segmentation flags (2 bits) and packet sequence count (14 bits). Bytes 4–5 are |
| 230 | + the Packet Length field (16 bits) which encodes the number of Packet Data Field octets |
| 231 | + minus 1. |
| 232 | +- **Packet Data Field:** follows the primary header; contains an optional secondary |
| 233 | + header and user/application data. |
| 234 | +- **Secondary header:** optional, application-defined. Presence is indicated by the |
| 235 | + secondary header flag. Format: byte 0 = flags (LSB = 1 requests CRC), byte 1 = |
| 236 | + number of additional secondary header bytes, followed by that many bytes. |
| 237 | +- **Segmentation flags:** indicate whether a packet is standalone (`UNSEGMENTED`), |
| 238 | + or the first, continuation, or last segment of a segmented logical packet. |
| 239 | +- **APID (Application Process ID):** 11-bit identifier for the source or consumer |
| 240 | + application / logical data stream. |
| 241 | +- **CRC:** CCSDS Space Packet does not mandate CRC in the primary header. This library |
| 242 | + provides an opt-in CRC-16-CCITT covering the secondary header and payload, enabled by |
| 243 | + setting the LSB of the secondary header flags byte. |
| 244 | + |
| 245 | +## Limitations |
| 246 | + |
| 247 | +Current implementation focuses on core protocol features: |
| 248 | + |
| 249 | +- No fragmentation / reassembly logic (sequence flag tracking is left to the application) |
| 250 | +- CRC is only supported when a secondary header is present |
| 251 | +- No thread safety (external locking required in multi-threaded contexts) |
| 252 | +- Parser does not copy payload; the caller must keep the source buffer alive |
| 253 | + |
| 254 | +## References |
| 255 | + |
| 256 | +- [CCSDS 133.0-B-2: Space Packet Protocol](https://ccsds.org/Pubs/133x0b2e2.pdf) |
| 257 | + |
| 258 | +## License |
| 259 | + |
| 260 | +See LICENSE file. |
0 commit comments