Skip to main content

Equipment contracts: ACU and modem

Two device families report ground-segment RF: an antenna control unit (ACU) and a modem. Neither has a vendor-specific driver. An ACU or modem is any commercial controller that exposes the JSON documented here over HTTP or MQTT; a small adapter you write reads it and republishes it as telemetry.

This page is the field-by-field contract for those payloads — antenna.v1 and modem.v1 — and the line-protocol records a poller publishes from them.

Transport​

ACU (antenna.v1)Modem (modem.v1)
HTTPGET /stateGET /state
MQTTconstellation/antenna/<entity_id>/state (retained)constellation/modem/<entity_id>/state (retained)
EncodingUTF-8 JSON, flat objectUTF-8 JSON, flat object
Cadence1–10 Hz1–10 Hz

Publish state as a retained MQTT message so a poller that restarts recovers current pointing and RF state without waiting a full interval. Every payload carries a schema field; treat an unrecognized major version as a device you cannot parse.

The antenna control unit (antenna.v1)​

{
"schema": "antenna.v1",
"entity_id": "antenna-ka-1",
"ts": "2026-07-20T18:22:31.412Z",
"az_deg": 128.44,
"el_deg": 31.07,
"state": "tracking",
"link_id": "link-boulder-44713",
"temperature_c": 41.2,
"rssi_dbm": -71,
"fault": null,
"seq": 88213
}
FieldTypeNotes
entity_idstringStable device identity. Publish unchanged as the record's entity_id tag.
tsRFC3339Device event time, UTC. Distinct from ingestion time.
az_deg / el_degfloatMeasured pointing. Azimuth [0,360), elevation [-5,90].
stateenumidle | slewing | tracking | stowed | fault.
link_idstring | nullThe link this antenna is tracking. Correlates the ACU to its modem; it does not name the far end.
temperature_cfloatDegrees C.
rssi_dbmintWi-Fi RSSI for the ACU's own management link — not RF payload signal. See Two fields named rssi_dbm.
faultstring | nullNon-null freezes pointing at its last measured values.
seqintMonotonic; a gap indicates missing sequence values; account for resets and wraparound.

On fault, az_deg/el_deg hold their last values and state reads fault — the device does not report smooth motion it is not performing. ts is the device's own event time; do not overwrite it with poller arrival time.

The modem (modem.v1)​

{
"schema": "modem.v1",
"entity_id": "modem-ka-1",
"ts": "2026-07-20T18:22:31.412Z",
"link_id": "link-boulder-44713",
"snr_db": 15.9,
"rssi_dbm": -70.4,
"locked": true,
"modcod": "32APSK_8/9",
"goodput_mbps": 84.1,
"doppler_hz": -3421.0,
"pointing_error_deg": 0.08,
"carrier_freq_ghz": 12.0,
"fault": null,
"seq": 44120
}
FieldTypeNotes
entity_idstringStable device identity. Publish unchanged as the record's entity_id tag.
tsRFC3339Device event time, distinct from ingestion time.
link_idstringThe RF link this modem serves. Shared with the ACU's link_id so a consumer fuses geometry and RF into one link.
snr_dbfloatEstimated Es/N0. Falls with antenna pointing loss and rain.
rssi_dbmfloatReceived RF signal strength on the payload carrier. Distinct from the ACU's rssi_dbm.
lockedboolCarrier lock.
modcodstring | nullActive DVB-S2X MODCOD. Null at outage.
goodput_mbpsfloatThroughput after framing overhead; 0 at outage.
doppler_hzfloatDoppler frequency shift in Hz. See range inputs.
pointing_error_degfloat | nullAngular error against the ACU's measured pointing. Null when the ACU is unreachable.
carrier_freq_ghzfloatCarrier frequency. Published as the telemetry tag frequency_ghz.
faultstring | nullNon-null forces a fault state.
seqintMonotonic; check gaps, resets, and wraparound.

At loss of lock, modcod and goodput_mbps go null/0 rather than holding a stale value.

From device JSON to telemetry​

A poller reads each device payload and publishes it as line-protocol telemetry through the local socket. A few field names change on the way through:

Device fieldTelemetry fieldWhy
az_deg (ACU)azimuth_degTelemetry vocabulary.
el_deg (ACU)elevation_degTelemetry vocabulary.
carrier_freq_ghz (modem)frequency_ghzTelemetry vocabulary; also the link's band tag.
everything elsesame namePublished as-is.

link_id comes from the device payload; add station_id from your site configuration. Publish both as telemetry tags.

Publish antenna and modem records as equipment with their entity_type and parent station_id tags. Publish a separate link record with entity_id, link_id, source, and target tags to describe the connection.

SNR forecasting reads features from that link record. Copy the measured SNR and pointing fields onto it, and obtain slant_range_km from a pass-geometry source. See prediction inputs.

Two field-level traps​

Two fields named rssi_dbm​

Both payloads have an rssi_dbm field measuring different things. The ACU's is Wi-Fi signal strength on its own management link — a health signal, unrelated to the RF payload. The modem's is received RF signal strength on the payload carrier. Publish the modem's rssi_dbm as the link's RF signal strength; publishing the ACU's under that name mislabels a Wi-Fi reading as an RF one.

Modem doppler_hz is not slant range​

A modem publishes a Doppler frequency shift in doppler_hz. SNR forecasting needs distance in slant_range_km on the link entity. Obtain that distance from a device or station-side geometry calculation; a frequency shift alone does not supply it. Missing model inputs can prevent a forecast. Inspect entity_errors alongside prediction items.

  • Data model — the entity table and how a fused link becomes an SNR forecast.
  • Send one metric — the end-to-end path this field mapping slots into.
  • Protocol reference — the line-protocol format a poller writes into.

Next: Satellite contract.