Skip to content

Receiver ​

The receiver connects to one or more readsb TCP streams (raw ADS-B format), parses each frame to extract the ICAO hex identifier, wraps the message in a typed InboundMessage envelope, and routes it to the appropriate RabbitMQ queue based on a modulo-bucketing scheme. When RabbitMQ is unavailable the receiver writes to a local SQLite fallback queue and drains it automatically on reconnect. One receiver container handles all configured sources concurrently (one thread per source).

Receiver architecture

Configuration (settings.json) ​

Top-level fields ​

FieldTypeDefaultDescription
sourcesarray—List of readsb source objects (see below). At least one is required.
processor_countinteger1Total number of processor containers. Must match the number of active processor services. Used to compute queue_name = adsb-{int(icao_hex, 16) % processor_count}. Increment this when adding a processor.
rabbitmqobject—RabbitMQ connection settings (see below).
mqttobject—MQTT broker settings (see below). Omit the key entirely to disable MQTT.
telemetry_interval_secondsinteger30How often (seconds) the receiver publishes MQTT statistic messages.
data_dirstring"/app/data"Host-mounted directory where queue.db (the RabbitMQ offline fallback) is written.
log_levelstring"info"Log verbosity. Set to "debug" for verbose output.

sources[] object ​

FieldTypeDescription
hoststringHostname or IP of the readsb instance.
portintegerTCP port of the readsb raw output (e.g. 30002 for 1090 MHz, 30978 for 978 MHz UAT).
sourcestringTag applied to every message from this stream. One of "1090", "978", or "MLAT".

Example with three sources:

json
{
  "sources": [
    { "host": "192.168.1.10", "port": 30002, "source": "1090" },
    { "host": "192.168.1.10", "port": 30978, "source": "978" },
    { "host": "mlat-server.example.com", "port": 30105, "source": "MLAT" }
  ]
}

An MLAT source does not need to be co-located with the receiver's SDR hardware — it's a plain TCP connection like any other source, so host can point at a remote MLAT-results feed (e.g. a readsb instance receiving results from an mlat-client). MLAT frames use the same raw Mode S format as 1090, so no separate parsing is required.

rabbitmq object ​

FieldTypeDefaultDescription
hoststring—RabbitMQ hostname or IP.
portinteger5672RabbitMQ AMQP port.
usernamestring—RabbitMQ username.
passwordstring—RabbitMQ password.

mqtt object ​

FieldTypeDefaultDescription
hoststring—MQTT broker hostname or IP.
portinteger1883MQTT broker port.
usernamestring—MQTT username. Optional — omit both username and password to connect anonymously.
passwordstring—MQTT password.

RECEIVER_ID Environment Variable ​

RECEIVER_ID is an optional integer environment variable (default 0). It distinguishes multiple receiver containers publishing to the same MQTT broker and is included in every MQTT topic published by the receiver.

yaml
environment:
  RECEIVER_ID: "0"

Routing ​

Each incoming message is routed to a durable RabbitMQ queue named adsb-{n} where:

n = int(icao_hex, 16) % processor_count

This ensures all messages for a given aircraft always go to the same processor, preserving per-aircraft flight state without coordination between processors. On RabbitMQ connect the receiver pre-declares all queues (adsb-0 through adsb-{processor_count - 1}).

Fault Tolerance ​

When RabbitMQ is unavailable (at startup or after a disconnect), messages are written to queue.db (SQLite WAL mode) in data_dir. Each row stores the target queue_name, the JSON payload, and the received_at timestamp. When the RabbitMQ connection is re-established, the fallback queue is drained oldest-first before new messages are forwarded. If RabbitMQ drops mid-drain, draining stops cleanly and resumes on the next reconnect.

MQTT Topics Published ​

All topics use the root SkyFollower.

TopicPayloadRetained
SkyFollower/receiver/{receiver_id}/statusONLINE or OFFLINEYes
SkyFollower/receiver/{receiver_id}/statisticsJSON (see fields below)Yes

Statistics payload fields:

FieldTypeDescription
messages_1090_per_secondfloatAverage 1090 MHz message rate since last report
messages_978_per_secondfloatAverage 978 MHz UAT message rate since last report; only present if a 978 source is configured
messages_MLAT_per_secondfloatAverage MLAT message rate since last report; only present if an MLAT source is configured
local_queue_depthintegerMessages queued in the local SQLite fallback (queue.db)
rabbitmq_connectedbooleantrue when an active RabbitMQ connection is held
started_atstringUTC ISO-8601 timestamp of process start

A messages_{source}_per_second field is generated for every source tag present in sources[] — the table above lists the currently supported tags, not a fixed schema.

All statistics are published as a single retained JSON payload. Telemetry is published every telemetry_interval_seconds.

Home Assistant autodiscovery payloads are published to homeassistant/sensor/SkyFollower_receiver_{receiver_id}_{field}/config on MQTT connect. Each sensor uses state_topic: SkyFollower/receiver/{receiver_id}/statistics with a value_template to extract its field.

Adding or Changing readsb Sources ​

  1. Update the sources array in settings.json — add, remove, or edit entries.
  2. Restart the receiver container.

Each source runs in its own thread; adding a source does not affect others. The source tag you choose is stamped on every message and carried through to the archived flight record.