Skip to content

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

make -C src/satellite/ccsds_spp
./build/ccsds_spp_demo

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