Tutorial 21: Ground Segment and CCSDS

This tutorial explains how to format spacecraft telemetry using the CCSDS (Consultative Committee for Space Data Systems) standard for ground-to-satellite communication.

1. CCSDS Space Packet

Every CCSDS Space Packet consists of a 6-byte Primary Header followed by the Packet Data Field. The header defines the Application Process ID (APID), packet type, sequence flags, and sequence count.

from opengnc.ground_segment.ccsds import SpacePacket, PacketType, SequenceFlags
import numpy as np

# 1. Create a Space Packet
# APID: 0x123 (291), Sequence Count: 42
packet = SpacePacket(
    apid=0x123, 
    packet_type=PacketType.TELEMETRY, 
    seq_count=42
)

# 2. Append some payload data (two float32 values)
# CCSDS standards use Big-Endian (Network Byte Order)
payload = np.array([10.1, 20.2], dtype='>f4').tobytes()
packet.data = payload

# 3. Pack the full packet to binary
raw_bytes = packet.pack()
print(f"Full Packet (hex): {raw_bytes.hex()}")
print(f"Header (hex): {raw_bytes[:6].hex()}")
print(f"Total Packet Length: {len(raw_bytes)} bytes")
Full Packet (hex): 0123c02a00074121999a41a1999a
Header (hex): 0123c02a0007
Total Packet Length: 14 bytes

2. Telemetry Decommutation

The decom module handles extracting fields from binary telemetry streams based on a parameter map. We use the DecomEngine to convert the raw octets back into engineering units.

from opengnc.ground_segment.decom import DecomEngine

# Define the structure of the data inside the CCSDS packet data field
# 'f' corresponds to a 32-bit float (4 bytes)
parameter_config = [
    {"name": "voltage", "data_type": "f", "offset": 0, "scale": 1.0},
    {"name": "current", "data_type": "f", "offset": 4, "scale": 1.0}
]

# Initialize the DecomEngine
engine = DecomEngine.from_dict(parameter_config)

# Extract data from the packed bytes (skipping the 6-byte CCSDS header)
data_field = raw_bytes[6:]
results = engine.decommutate(data_field)

print(f"Decommutated Telemetry (Big-Endian): {results}")
print(f"Measured Voltage: {results['voltage']:.2f} V")
print(f"Measured Current: {results['current']:.2f} A")
Decommutated Telemetry (Big-Endian): {'voltage': 10.100000381469727, 'current': 20.200000762939453}
Measured Voltage: 10.10 V
Measured Current: 20.20 A