Rocket Telemetry Project Docs
Shared

APIs and Data Contracts

This section does not lock the exact schema, but it defines the contract families the implementation must support. Detailed endpoint examples and payload structures are provided to guide implementation.

1. Antenna-Node Control Endpoints

At minimum the node should expose control families equivalent to:

  • POST /radio/config
  • POST /beam/set
  • POST /beam/schedule
  • POST /tracking/start
  • POST /tracking/stop
  • POST /frequency-hold/start
  • POST /frequency-hold/stop
  • POST /frame/config
  • POST /stream/start
  • POST /stream/stop
  • GET /metrics
  • GET /status
  • GET /capabilities
  • GET /frequency-hold/status
  • GET /scan/heatmap

Coordinate and abstract pointing controls should be explicit:

  • POST /pointing/az-el
  • POST /pointing/target-coordinate
  • POST /pointing/hold
  • POST /pointing/stop

Timestamp conventions

Timestamps are critical for cross-node correlation and post-mission analysis. Recommend the following canonical fields and formats:

  • timestamp_node_us: integer microseconds since Unix epoch (UTC) produced by the node's clock (GNSS-sourced when available).
  • timestamp_central_us: integer microseconds since Unix epoch (UTC) recorded by the central aggregator on ingest.

If only a single timestamp_us field is present (legacy examples below), document whether it refers to node time or central ingest time. Always include timezone/epoch (UTC) and prefer explicit field names in new APIs.

When emitting telemetry events, include both timestamps where possible to aid reconciliation and debugging.

Machine-readable schema: telemetry-event.schema.json

Candidate source streams should stay raw or lightly annotated until the central server selects a preferred source. A node does not know which stream the central server will prefer; it simply publishes the telemetry formats it has been configured to emit. Decoding can happen centrally for the preferred stream, or at the node when a deployment explicitly enables edge pre-decode.

Node telemetry formats at a glance

Node outputTypical formatNotes
Control and status API (8080)JSON over HTTPconfiguration, state, capabilities
Raw IQ stream (9001)Binary complex samplespre-demodulation I/Q bytes
Telemetry frames (9002)Raw binary frame bytes; optional schema-backed JSON objectsraw bytes by default, typed values if pre-decode is enabled
Metrics (9003)Prometheus textscrape endpoint
Streaming status (9004)gRPC event streamlow-rate health and state updates
Heatmap / spectrum (9005)Binary or JSONvisualization and analysis

Central source selection is a routing decision outside the node. The node emits the configured formats; the central server decides which stream is preferred downstream.

1.1 Endpoint examples

Radio configuration

Request:

POST /radio/config
{
  "frequency_hz": 5400000000,
  "sample_rate_sps": 40000000,
  "gain_db": 32,
  "band": "C",
  "modulation_hint": "QPSK"
}

Response on success:

{
  "status": "accepted",
  "command_id": "radio-cfg-001",
  "effective_frequency_hz": 5400000000,
  "effective_sample_rate_sps": 40000000,
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

Beam pointing

Request (azimuth/elevation):

POST /pointing/az-el
{
  "azimuth_deg": 45.5,
  "elevation_deg": 30.2,
  "tracking_mode": "hold"
}

Request (coordinate-based):

POST /pointing/target-coordinate
{
  "target_latitude": 28.4,
  "target_longitude": -80.6,
  "target_altitude_m": 5000,
  "reference_frame": "WGS84",
  "timestamp_utc": "2026-03-17T14:30:00Z",
  "tracking_mode": "continuous"
}

Response (both):

{
  "status": "accepted",
  "command_id": "pointing-001",
  "computed_azimuth_deg": 45.6,
  "computed_elevation_deg": 30.1,
  "range_estimate_m": 4800,
  "node_gps_position": {
    "latitude": 28.5,
    "longitude": -80.5,
    "altitude_m": 15,
    "fix_quality": "RTK_FIXED"
  },
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

Frame and payload configuration

Request:

POST /frame/config
{
  "payload_id": "fc_primary",
  "sync_words": [
    {
      "value_hex": "0x1ACFFC1D",
      "length_bits": 32,
      "confidence_threshold": 0.95
    }
  ],
  "frame_structure": {
    "header_bytes": 8,
    "max_payload_bytes": 256,
    "crc_type": "CRC16_CCITT",
    "fec_type": "VITERBI_1_2"
  },
  "predecode_enabled": true,
  "field_schema": [
    {
      "path": "latitude",
      "type": "float32",
      "offset_bytes": 0,
      "length_bits": 32,
      "endianness": "little",
      "scale": 1.0,
      "offset_value": 0.0,
      "unit": "deg"
    },
    {
      "path": "speed_mps",
      "type": "uint16",
      "offset_bytes": 12,
      "length_bits": 16,
      "endianness": "little",
      "scale": 0.1,
      "offset_value": 0.0,
      "unit": "m/s"
    }
  ],
  "output_mode": "decoded",
  "timestamp_format": "node_absolute"
}

Response:

{
  "status": "accepted",
  "command_id": "frame-cfg-001",
  "payload_id": "fc_primary",
  "effective_sync_words": 1,
  "predecode_enabled": true,
  "frame_definitions_loaded": 1,
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

Stream control

Request:

POST /stream/start
{
  "stream_type": "telemetry",
  "output_format": "json",
  "target_url": "ws://central-server:9000/node-stream",
  "payload_ids": ["fc_primary"]
}

Response:

{
  "status": "streaming",
  "stream_id": "stream-001",
  "stream_url": "ws://node:9001/stream-001",
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

Capability and status queries

Request:

GET /capabilities

Response:

{
  "node_id": "node-001",
  "antenna_type": "phased_array",
  "antenna": {
    "bands_supported": ["C"],
    "frequency_range_hz": [4900000000, 6000000000],
    "max_bandwidth_hz": 40000000,
    "beam_modes": ["az_el", "coordinate", "fixed", "scan"]
  },
  "pointing": {
    "az_range_deg": [-180, 180],
    "el_range_deg": [0, 90],
    "slew_rate_deg_per_sec": 10,
    "positioning_accuracy_deg": 0.1,
    "actuation_type": "phased"
  },
  "gps": {
    "available": true,
    "current_fix_quality": "RTK_FIXED",
    "position": {
      "latitude": 28.5,
      "longitude": -80.5,
      "altitude_m": 15
    }
  },
  "rf": {
    "channels": 1,
    "rx_capable": true,
    "tx_capable": false,
    "sample_rates_supported": [10e6, 20e6, 40e6]
  },
  "dsp": {
    "fec_types": ["VITERBI", "LDPC"],
    "modulations_supported": ["BPSK", "QPSK", "PCM_FM"],
    "doa_algorithm": "MUSIC"
  },
  "features": {
    "beam_sweep": true,
    "multi_beam": false,
    "heatmap": true,
    "multi_emitter": false,
    "auto_tracking": true
  },
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

Request:

GET /status

Response:

{
  "node_id": "node-001",
  "online": true,
  "last_heard_us": 1234567890123456,
  "uptime_seconds": 86400,
  "system": {
    "cpu_load_percent": 45,
    "memory_used_percent": 65,
    "fpga_load_percent": 72,
    "temperature_c": 52
  },
  "rf_chain": {
    "frequency_hz": 5400000000,
    "sample_rate_sps": 40000000,
    "gain_db": 32,
    "rssi_dbm": -65
  },
  "beam": {
    "current_azimuth_deg": 45.5,
    "current_elevation_deg": 30.2,
    "requested_azimuth_deg": 45.5,
    "requested_elevation_deg": 30.2,
    "tracking_mode": "hold",
    "tracking_quality": 0.92
  },
  "demodulator": {
    "lock_state": "frame_lock",
    "snr_db": 18.5,
    "frequency_offset_hz": 1200,
    "symbol_rate": 500000
  },
  "streams": {
    "active_count": 1,
    "total_packets_sent": 42000,
    "dropped_packets": 3
  },
  "gps": {
    "fix_quality": "RTK_FIXED",
    "satellites_used": 18,
    "hdop": 0.5,
    "position": {
      "latitude": 28.5,
      "longitude": -80.5,
      "altitude_m": 15
    }
  },
  "timestamp_node_us": 1234567890123456,
  "timestamp_central_us": 1234567890123456
}

1.2 Multiple payload and sync-word configuration

The node should support one or more payload streams per RF channel.

Each payload stream is defined by:

  • payload ID
  • one or more sync words (for protocol detection)
  • frame structure and size
  • error correction type
  • decoder type

Sync-word strategy:

  • If the channel carries a single protocol, one sync word is sufficient.
  • If the channel carries multiple protocols or payloads (e.g., flight computer and payload data packets), use multiple sync words with unique patterns.
  • Each sync word should have a confidence threshold to avoid false locks.

Example multi-payload configuration:

POST /frame/config
{
  "profiles": [
    {
      "payload_id": "flight_computer",
      "sync_words": [
        {
          "value_hex": "0x1ACFFC1D",
          "length_bits": 32
        }
      ],
      "frame_structure": {
        "header_bytes": 8,
        "max_payload_bytes": 256,
        "crc_type": "CRC16_CCITT"
      }
    },
    {
      "payload_id": "experiment_data",
      "sync_words": [
        {
          "value_hex": "0xF8726B19",
          "length_bits": 32
        }
      ],
      "frame_structure": {
        "header_bytes": 4,
        "max_payload_bytes": 512,
        "crc_type": "CRC32"
      }
    }
  ],
  "output_mode": "decoded",
  "routing_mode": "by_payload_id"
}

The node's frame decoder should:

  • monitor the demodulated bit stream for any configured sync word
  • when found, attempt to decode the frame using the corresponding payload definition
  • emit telemetry events tagged with the payload ID
  • deliver each payload stream separately to the central server

TM operators must be able to define payload typing, not only sync words and frame length. At minimum, each payload definition should support:

  • field schema (name, type, byte offset/length, endianness, scaling, unit)
  • optional validation limits and enum/bitfield decoding
  • stable parameter labels and identifiers that OPM/CM can bind later
  • stable parameter labels and identifiers that OPM (Operation Manager) / CM (Configuration Manager) can bind later

Detailed parameter typing model

  • A payload type contains multiple parameters (for example: latitude, longitude, altitude_m, speed_mps, vertical_speed_mps, heading_deg, battery_v).
  • Each parameter must declare: path, type, offset, length_bits, endianness, scale, offset_value, unit, and optional valid_range.
  • Derived parameters are allowed (for example, convert raw centimeters to meters) but must be explicitly declared and versioned.
  • Schema versions must be immutable once deployed to an active mission session.

Per-target dashboard binding model (OPM (Operation Manager) / CM (Configuration Manager)-managed)

  • Binding key should be (target_id, payload_type_id, parameter_path).
  • One decoded parameter may bind to multiple UI parameters (for example speed_mps to both gauge and trend chart).
  • UI must support fallback binding scopes in this order:
    1. exact target_id binding
    2. payload-type default binding
    3. global mission default binding
  • If no binding exists, parameter remains available in an unbound telemetry inspector and must not be dropped.

The normalized decoded event sent to UI services should include at least:

  • target_id
  • payload_type_id
  • decoded_values as typed fields
  • quality metadata (lock/CRC/confidence)
  • node and central timestamps

Recommended decoded payload shape:

{
  "latitude": {
    "value": 28.4,
    "unit": "deg",
    "quality": "ok"
  },
  "speed_mps": {
    "value": 1200.5,
    "unit": "m/s",
    "quality": "ok"
  }
}

1.3 Node status fields

Node status should include at least:

  • node identity
  • node time and synchronization state
  • online or degraded state
  • configured frequency and sample rate
  • active beam mode
  • current beam azimuth and elevation
  • tracking mode and track quality
  • FPGA load or thermal indicators
  • demodulator lock state

For antenna-type agnostic operation, status should also include:

  • antenna type
  • actuation type: phased, motorized, fixed
  • capability version
  • current control authority source
  • GNSS position and fix quality

1.4 Live telemetry event shape

Every telemetry event delivered to the central server should carry enough metadata to be replayed and traced. A normalized event should include:

  • event timestamp from node and central receive time
  • mission ID
  • node ID
  • emitter or source ID when known
  • payload or stream ID when known
  • frame counter or sample sequence
  • signal metrics such as RSSI, SNR, frequency offset, lock state
  • decoded payload or binary frame reference

1.5 Stream modes

The node and central server should support these logical stream modes:

ModeDescriptionMain consumer
telemetrydecoded objects for mission operationsfrontend, mission services
framesencoded binary framesrecorders, protocol tools
metricsRF and node healthmonitoring and UI
eventsstate changes and anomaliestimeline, alerting
iq_snapshotoptional short capturesengineering and debugging

1.6 Error and fallback semantics

The contracts should support explicit machine-readable states such as:

  • searching
  • carrier_lock
  • symbol_lock
  • frame_lock
  • degraded
  • lost_lock
  • over_temperature
  • tracking_unreliable
  • source_rejected

Add actuator and capability errors:

  • motion_limit_violation
  • unsupported_mode
  • capability_mismatch
  • gps_fix_unavailable
  • actuator_fault
  • frequency_hold_lost
  • doppler_out_of_range

The frontend must not infer these states from raw metrics alone.

1.7 Capability response shape

The capabilities response should include these top-level groups:

  • node metadata
  • antenna metadata
  • RF metadata
  • pointing metadata
  • telemetry stream metadata
  • health metadata

This allows central and UI to adapt to mixed node fleets without hard-coding antenna models.

1.8 Data sourcing recommendations

To keep the system lightweight and avoid unnecessary software, follow these sourcing rules:

On the antenna node:

  • Local GNSS position and altitude: use directly from GNSS receiver for coordinate steering. This allows beam tracking to persist during brief central server outages.
  • Frequency plan and mission timeline: pull from central on startup and on explicit request. Cache locally for fallback.
  • Rocket telemetry history or state: do not replicate on the node. The node is a sensor, not a mission record keeper.
  • Calibration tables and beamforming coefficients: store locally after download from central. Operator should be able to push updated calibration to the node without rebuilding.

On the central server:

  • Node capabilities: fetch once on discovery, cache, and refresh on explicit requery or timeout.
  • Node status and metrics: pull continuously via REST polling or subscribe to a push stream from nodes.
  • Telemetry events from all nodes: aggregate in a message bus or event log.
  • External data (weather, range safety, operator roles): pull from external systems on startup and maintain subscription or periodic fetch.

In the frontend:

  • Do not replicate node status or raw telemetry. Subscribe to central server WebSocket streams.
  • Do not compute beamforming weights or geodesy. Request computed results from the node or central.
  • Cache mission profile, frequency plans, and UI configuration locally for offline access, but do not treat cached data as truth during active operations.
  • Display source freshness: always indicate whether data is live, from replay, or stale.

Next: See Ground Station Frontend Architecture for frontend design and page layouts, or Ground Station Control Surface Design for control-surface patterns.

On this page