Telemetry Reference
Packet Structure
All downlink telemetry packets share a common format:
[4-byte ASCII identifier][payload bytes][\n]
The \n newline is appended by radio.py during transmission and serves as a packet delimiter. It is not part of the logical payload.
Binary Packing
Telemetry payloads are packed using Python's struct.pack with little-endian byte order:
| Format Code | Type | Size |
|---|---|---|
s |
char (ASCII string) | 1 byte each |
i |
signed int32 | 4 bytes |
I |
unsigned int32 | 4 bytes |
f |
float32 (IEEE 754) | 4 bytes |
Telemetry Packet Definitions
GYRO — Gyroscope
struct.pack('4s3f', b'GYRO', x, y, z)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | X | °/s |
| 8 | 4 | float | Y | °/s |
| 12 | 4 | float | Z | °/s |
Total: 16 bytes
ACCL — Accelerometer
Same format as GYRO. Units: m/s².
MAGN — Magnetometer
Same format as GYRO. Units: µT (microtesla).
GRAV — Gravity Vector
Same format as GYRO. Units: m/s².
EULR — Euler Angles
Same format as GYRO. Units: degrees (°).
BME2 — BME280 Environmental
struct.pack('4s3f', b'BME2', temperature, pressure, altitude)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | Temperature | °C |
| 8 | 4 | float | Pressure | hPa |
| 12 | 4 | float | Altitude | m |
Total: 16 bytes
TEMP — Temperature (IMU)
struct.pack('4si', b'TEMP', temperature)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | int32 | Temperature | °C |
Total: 8 bytes
QUAT — Quaternion
struct.pack('4s4f', b'QUAT', w, x, y, z)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | W | — |
| 8 | 4 | float | X | — |
| 12 | 4 | float | Y | — |
| 16 | 4 | float | Z | — |
Total: 20 bytes
ADCS — Attitude Determination
struct.pack('4s7f', b'ADCS', heading, roll, pitch, quat_w, quat_x, quat_y, quat_z)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | Heading | ° |
| 8 | 4 | float | Roll | ° |
| 12 | 4 | float | Pitch | ° |
| 16 | 4 | float | Quat W | — |
| 20 | 4 | float | Quat X | — |
| 24 | 4 | float | Quat Y | — |
| 28 | 4 | float | Quat Z | — |
Total: 32 bytes
SOLR — Solar Panels
struct.pack('4s8f', b'SOLR', v1, i1, v2, i2, v3, i3, v4, i4)
Panel order matches INA219 addresses:
| Index | Address | Panel |
|---|---|---|
| 0 | 0x40 | X- |
| 1 | 0x41 | X+ |
| 2 | 0x44 | Y+ |
| 3 | 0x45 | Y- |
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | Panel 1 Voltage | V |
| 8 | 4 | float | Panel 1 Current | mA |
| 12 | 4 | float | Panel 2 Voltage | V |
| 16 | 4 | float | Panel 2 Current | mA |
| 20 | 4 | float | Panel 3 Voltage | V |
| 24 | 4 | float | Panel 3 Current | mA |
| 28 | 4 | float | Panel 4 Voltage | V |
| 32 | 4 | float | Panel 4 Current | mA |
Total: 36 bytes
EPSS — EPS Status
struct.pack('4s5if', b'EPSS', error, ch1, ch2, ch3, ch4, battery_voltage)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | int32 | Error Code | — |
| 8 | 4 | int32 | Channel 1 | 0=OFF, 1=ON |
| 12 | 4 | int32 | Channel 2 | 0=OFF, 1=ON |
| 16 | 4 | int32 | Channel 3 | 0=OFF, 1=ON |
| 20 | 4 | int32 | Channel 4 | 0=OFF, 1=ON |
| 24 | 4 | float | Battery Voltage | V |
Total: 28 bytes
OBCC / OBCR / OBCD — OBC Single Values
struct.pack('4sf', b'OBCC', value) # CPU, RAM, or Disk
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | float | Usage | % |
Total: 8 bytes
HOST — Hostname
struct.pack('4s11s', b'HOST', hostname_bytes)
| Offset | Size | Type | Field |
|---|---|---|---|
| 0 | 4 | char[4] | Identifier |
| 4 | 11 | char[11] | Hostname (null-padded) |
Total: 15 bytes
BECN — Health Beacon
struct.pack('4sIffff', b'BECN', uptime, cpu, ram, disk, temp)
| Offset | Size | Type | Field | Unit |
|---|---|---|---|---|
| 0 | 4 | char[4] | Identifier | — |
| 4 | 4 | uint32 | Uptime | seconds |
| 8 | 4 | float | CPU Usage | % |
| 12 | 4 | float | RAM Usage | % |
| 16 | 4 | float | Disk Usage | % |
| 20 | 4 | float | Temperature | °C |
Total: 24 bytes
XFRC — Transfer Complete
struct.pack('4sI', b'XFRC', total_packets)
| Offset | Size | Type | Field |
|---|---|---|---|
| 0 | 4 | char[4] | Identifier |
| 4 | 4 | uint32 | Total Packets |
Total: 8 bytes
OBCP / OBCL — Chunked Text Data
These are variable-length, multi-packet responses. Each chunk:
[4-byte ID][text data up to (chunk_size - 5) bytes]
The ground station accumulates chunks and flushes after 1 second of no new data.
SEND — Image Data Packet
SEND[base64_data][total_chunks:04d][current_chunk:04d]
Image packets are text-based (not binary struct). See Image Transfer.
PHOT — Photo Response
struct.pack('4sI', b'PHOT', filename_length) + filename_bytes
Variable length. Contains the filename of the captured image.