CCSDS Space Packet Protocol¶
Overview¶
The Space Packet Protocol (CCSDS 133.0-B-2) is the fundamental application-layer data unit for space missions. It provides a standardized way to package telemetry (TM) and telecommand (TC) data with routing via APIDs (Application Process Identifiers). Space Packets are the payload carried within TM and TC Transfer Frames.
Key Facts
- Standard: CCSDS 133.0-B-2 (Blue Book, Issue 2)
- Header size: 6 bytes (fixed primary header)
- Max packet size: 65,542 bytes (6 header + 65,536 data)
- Addressing: 11-bit APID (2048 unique endpoints)
- Sequencing: 14-bit counter per APID
- CRC: Optional CRC-16-CCITT in data zone
- Usage: Every modern space mission (ISS, JWST, Mars rovers)
Primary Header Structure¶
The Space Packet primary header is exactly 6 bytes (48 bits) one of the most efficient headers in networking:
Byte: 0 1 2 3 4 5
Bit: [7......0] [7......0] [7......0] [7......0] [7......0] [7......0]
├─────────────────────┼──────────────────────┼─────────────────────┤
│ Packet ID (16b) │ Packet Seq Ctrl(16b) │ Packet Data Len(16b)│
├───┬──┬──────────────┼──┬────────────────────┼─────────────────────┤
│Ver│Tp│ APID │SF│ Sequence Count │ Data Length - 1 │
│3b │1b│ 11 bits │2b│ 14 bits │ 16 bits │
└───┴──┴──────────────┴──┴────────────────────┴─────────────────────┘
Field Descriptions¶
| Field | Bits | Range | Description |
|---|---|---|---|
| Version | 3 | 000 | Packet version (always 0 for current CCSDS) |
| Type | 1 | 0/1 | 0 = Telemetry (TM), 1 = Telecommand (TC) |
| Sec Hdr Flag | 1 | 0/1 | 1 = Secondary header present |
| APID | 11 | 0–2047 | Application Process Identifier (routing) |
| Seq Flags | 2 | 0–3 | Segmentation flags (see below) |
| Seq Count | 14 | 0–16383 | Per-APID sequence counter |
| Data Length | 16 | 0–65535 | Octets in data zone minus 1 |
Data Length Field
The Data Length field contains the number of octets in the Packet Data Zone minus 1. A value of 0 means 1 byte of data. Maximum value 65535 means 65536 bytes of data. Total packet size = 6 + (Data Length + 1).
Packet Identification¶
graph TD
A[Incoming Packet] --> B[Extract APID<br>11 bits]
B --> C{APID value?}
C -->|0x000-0x7FE| D[Valid application<br>endpoint]
C -->|0x7FF| E[Idle packet<br>fill data]
D --> F{Type bit?}
F -->|0| G[Telemetry<br>Downlink data]
F -->|1| H[Telecommand<br>Uplink command] APID Concept¶
The Application Process Identifier is an 11-bit field providing up to 2048 unique logical addresses on a spacecraft:
APID Allocation (Typical Mission)¶
| APID Range | Usage | Example |
|---|---|---|
| 0x000 | Time/reference | Onboard time broadcast |
| 0x001–0x00F | System housekeeping | Power, thermal, AOCS |
| 0x010–0x0FF | Instrument science | Camera, spectrometer, radar |
| 0x100–0x1FF | Instrument housekeeping | Instrument health data |
| 0x200–0x3FF | Mission-specific | Orbit determination, relay |
| 0x400–0x7FE | Reserved/mission-defined | Extended payloads |
| 0x7FF | Idle packet | Fill pattern (no valid data) |
APID as a Routing Key
Think of APID like a lightweight port number. Ground systems use APID to route incoming telemetry to the correct processing pipeline APID 0x010 goes to the camera team, APID 0x001 goes to spacecraft operations.
Virtual Channels and APID Multiplexing¶
Multiple APIDs share a single physical downlink through Virtual Channel multiplexing at the Transfer Frame layer:
Virtual Channel 0 (Realtime TM) Virtual Channel 1 (Stored TM)
┌────────┬────────┬────────┐ ┌────────┬────────┬────────┐
│APID 001│APID 010│APID 002│ │APID 010│APID 010│APID 011│
│HK pkt │Sci pkt │HK pkt │ │Replay │Replay │Replay │
└────────┴────────┴────────┘ └────────┴────────┴────────┘
│ │
▼ ▼
TM Frame (VC 0) TM Frame (VC 1)
│ │
└────────────┬───────────────────────┘
▼
Physical downlink
(frames interleaved by priority)
Packet Types¶
Telemetry Packets (Type = 0)¶
Spacecraft → Ground. Carry science data, housekeeping, event reports:
// Example: Temperature housekeeping packet
// APID = 0x001, Type = TM, Seq Count increments each packet
Header: [Ver=0][Type=0][SecHdr=1][APID=0x001][Seq=3][Flags=11][Len=31]
Secondary Header: [Timestamp: 4 bytes coarse + 2 bytes fine]
Data: [Temp1: 2B][Temp2: 2B][Temp3: 2B]...[TempN: 2B]
CRC-16: [2 bytes]
Telecommand Packets (Type = 1)¶
Ground → Spacecraft. Carry commands, parameter loads, memory patches:
// Example: Set heater ON command
// APID = 0x001, Type = TC, Sequence = next in TC sequence
Header: [Ver=0][Type=1][SecHdr=1][APID=0x001][Seq=47][Flags=11][Len=9]
Secondary Header: [Ack flags: 1B][Service Type: 1B][Subtype: 1B]
Data: [Heater ID: 1B][State: 1B (ON=1)]
CRC-16: [2 bytes]
Sequence Flags (Segmentation)¶
Large application data units that exceed the maximum packet size are segmented across multiple packets:
| Seq Flags | Binary | Meaning |
|---|---|---|
01 | First | First segment of a multi-packet group |
00 | Continue | Middle segment (neither first nor last) |
10 | Last | Last segment of a multi-packet group |
11 | Unsegmented | Complete packet (standalone, most common) |
Large data unit (200 KB image):
┌──────────────────────────────────────────────────────────────────────┐
│ Original Image Data (200 KB) │
└──────────────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────┬──────────────┬──────────────┬──────────────┐
│ Packet #1 │ Packet #2 │ Packet #3 │ Packet #4 │
│ Seq=01 │ Seq=00 │ Seq=00 │ Seq=10 │
│ (First) │ (Continue) │ (Continue) │ (Last) │
│ 65535 bytes │ 65535 bytes │ 65535 bytes │ 3395 bytes │
└──────────────┴──────────────┴──────────────┴──────────────┘
Count=N Count=N+1 Count=N+2 Count=N+3
CRC-16-CCITT¶
Error Detection¶
The optional (but universally used) CRC-16-CCITT provides error detection for the entire packet:
Polynomial: x¹⁶ + x¹² + x⁵ + 1 (0x1021)
Initial value: 0xFFFF
Input/output reflection: No (CCSDS convention)
Final XOR: 0x0000
CRC covers: Primary Header + Data Zone (excluding CRC itself)
CRC position: Last 2 bytes of Packet Data Zone
CRC vs FEC
The packet CRC detects errors that survive the transfer frame's FEC (Forward Error Correction). It's a second line of defense the TM frame's Reed-Solomon or LDPC coding handles most channel errors, but the CRC catches any residual bit flips.
Maximum Packet Size¶
| Field | Max Value | Resulting Size |
|---|---|---|
| Data Length field | 65535 (0xFFFF) | 65536 bytes of data zone |
| Primary Header | 6 bytes | |
| Total packet | 65542 bytes |
In practice, most missions limit packet size to fit within a single TM Transfer Frame:
| Frame Size | Typical Max Packet | Reason |
|---|---|---|
| 1115 bytes (standard) | ~1100 bytes | Avoid spanning frames |
| 892 bytes (with RS coding) | ~880 bytes | Frame - header - CRC |
| Custom | Mission-defined | Depends on link design |
Comparison: CCSDS Space Packet vs IP Packet¶
| Feature | CCSDS Space Packet | IPv4 Packet |
|---|---|---|
| Header size | 6 bytes | 20–60 bytes |
| Address space | 11-bit APID (2048) | 32-bit IP (4.3 billion) |
| Max payload | 65,536 bytes | 65,515 bytes |
| Sequencing | Built-in (14-bit) | None (TCP adds it) |
| Fragmentation | Sequence flags | IP fragmentation |
| Error detection | CRC-16 (optional) | Header checksum only |
| Routing | Static (APID tables) | Dynamic (routing protocols) |
| Overhead | ~0.5% (6B / 1100B) | ~3.6% (40B / 1100B) |
| Designed for | Low bandwidth, high latency | High bandwidth, low latency |
Why Not Just Use IP in Space?
Some modern missions do use IP (CCSDS 702.1-B-1, "IP over CCSDS"). But the 6-byte SPP header saves ~85% overhead vs IPv4+UDP headers critical when your Mars link is 6 Mbps and every bit costs solar panel energy.
API Reference¶
Core Types¶
#include "ccsds_spp.h"
typedef enum {
SPP_TYPE_TM = 0, // Telemetry
SPP_TYPE_TC = 1 // Telecommand
} spp_packet_type_t;
typedef enum {
SPP_SEQ_CONTINUATION = 0b00,
SPP_SEQ_FIRST = 0b01,
SPP_SEQ_LAST = 0b10,
SPP_SEQ_UNSEGMENTED = 0b11
} spp_seq_flags_t;
typedef struct {
uint8_t version; // Always 0
spp_packet_type_t type; // TM or TC
bool sec_hdr_flag; // Secondary header present
uint16_t apid; // 0–2047
spp_seq_flags_t seq_flags; // Segmentation
uint16_t seq_count; // 0–16383
uint16_t data_length; // Data zone length - 1
} spp_header_t;
typedef struct {
spp_header_t header;
uint8_t *data; // Pointer to data zone
size_t data_size; // Actual data size (data_length + 1)
uint16_t crc; // CRC-16-CCITT (if present)
bool crc_valid; // CRC verification result
} spp_packet_t;
Encoder/Decoder Functions¶
// Encode packet structure to wire format
// Returns total packet size written, or -1 on error
ssize_t spp_encode(const spp_packet_t *packet, uint8_t *buffer,
size_t buf_size);
// Decode wire format to packet structure
// Returns bytes consumed, or -1 on error
ssize_t spp_decode(const uint8_t *buffer, size_t buf_size,
spp_packet_t *packet);
// Encode only the 6-byte primary header
void spp_encode_header(const spp_header_t *header, uint8_t out[6]);
// Decode only the 6-byte primary header
void spp_decode_header(const uint8_t buf[6], spp_header_t *header);
CRC Functions¶
// Compute CRC-16-CCITT over buffer
uint16_t spp_crc16(const uint8_t *data, size_t len);
// Verify packet CRC (assumes CRC is last 2 bytes of data zone)
bool spp_verify_crc(const spp_packet_t *packet);
// Append CRC to packet data zone
void spp_append_crc(spp_packet_t *packet);
Sequence Management¶
// Per-APID sequence counter (thread-safe)
typedef struct {
uint16_t counters[2048]; // One per APID
} spp_seq_manager_t;
// Initialize sequence manager (all counters to 0)
void spp_seq_init(spp_seq_manager_t *mgr);
// Get next sequence count for APID (auto-increments, wraps at 16383)
uint16_t spp_seq_next(spp_seq_manager_t *mgr, uint16_t apid);
// Reset sequence counter for APID
void spp_seq_reset(spp_seq_manager_t *mgr, uint16_t apid);
Packet Builder (Convenience API)¶
// Build a complete unsegmented TM packet with CRC
ssize_t spp_build_tm_packet(uint16_t apid, uint16_t seq_count,
const uint8_t *payload, size_t payload_len,
uint8_t *output, size_t output_size);
// Build a complete unsegmented TC packet with CRC
ssize_t spp_build_tc_packet(uint16_t apid, uint16_t seq_count,
const uint8_t *payload, size_t payload_len,
uint8_t *output, size_t output_size);
// Segment large data into multiple packets
// Returns number of packets generated
size_t spp_segment(uint16_t apid, spp_packet_type_t type,
const uint8_t *data, size_t data_len,
size_t max_packet_size,
spp_packet_t *packets, size_t max_packets);
Usage Example¶
Building a Telemetry Packet¶
#include "ccsds_spp.h"
#include <stdio.h>
int main(void) {
// Housekeeping telemetry data
uint8_t hk_data[] = {
0x00, 0x1A, // Battery voltage: 2.6V (scaled)
0x01, 0x2C, // Solar panel current: 300mA
0xFF, 0xE2, // Temperature: -30 (signed, 0.1°C units)
0x00, 0x03 // Mode: SCIENCE (3)
};
// Build packet
uint8_t packet_buf[256];
spp_seq_manager_t seq;
spp_seq_init(&seq);
ssize_t pkt_size = spp_build_tm_packet(
0x001, // APID: housekeeping
spp_seq_next(&seq, 0x001), // Next sequence number
hk_data, sizeof(hk_data), // Payload
packet_buf, sizeof(packet_buf) // Output buffer
);
if (pkt_size > 0) {
printf("Packet built: %zd bytes\n", pkt_size);
printf(" Header: %02X %02X %02X %02X %02X %02X\n",
packet_buf[0], packet_buf[1], packet_buf[2],
packet_buf[3], packet_buf[4], packet_buf[5]);
// Ready to insert into TM Transfer Frame
}
// Decode it back
spp_packet_t decoded;
spp_decode(packet_buf, pkt_size, &decoded);
printf(" APID: 0x%03X | Type: %s | Seq: %u | CRC: %s\n",
decoded.header.apid,
decoded.header.type == SPP_TYPE_TM ? "TM" : "TC",
decoded.header.seq_count,
decoded.crc_valid ? "VALID" : "INVALID");
return 0;
}
Build & Run¶
Output:
=== CCSDS Space Packet Protocol Demo ===
Standard: CCSDS 133.0-B-2 | Header: 6 bytes | Max: 65542 bytes
--- Packet Construction ---
Building TM packet: APID=0x001, payload=8 bytes
Header: 00 01 C0 00 00 09
Total size: 16 bytes (6 hdr + 8 data + 2 CRC)
CRC-16: 0xA3B7
--- Packet Decode ---
Version: 0 | Type: TM | Sec Hdr: No
APID: 0x001 | Seq Flags: Unsegmented | Seq Count: 0
Data Length: 9 (10 bytes in data zone including CRC)
CRC verification: VALID ✓
--- Segmentation Test ---
Input: 150000 bytes | Max packet: 65536 bytes
Segment 1: [FIRST] seq=1, 65536 bytes
Segment 2: [CONT] seq=2, 65536 bytes
Segment 3: [LAST] seq=3, 18928 bytes
Total segments: 3 | All CRCs valid ✓
--- APID Routing ---
APID 0x001 → Housekeeping pipeline
APID 0x010 → Camera science pipeline
APID 0x7FF → Idle packet (discarded)
--- Sequence Counter ---
APID 0x001: seq 0, 1, 2, ... 16383, 0 (wrap) ✓
=== RESULTS: 18 passed, 0 failed ===
=== ALL TESTS PASSED ===
References¶
- CCSDS 133.0-B-2: Space Packet Protocol (Blue Book, June 2020)
- CCSDS 135.0-B-5: Space Link Identifiers (SCID, APID allocation)
- ECSS-E-ST-70-41C: PUS (Packet Utilization Standard) ESA secondary header format