MAVLink ground receiver
Record what range control hears from each vehicle, stamped with the receiver's own clock. The receive log is what the telemetry_gap rule reads, and it shows when the ground heard a message, not when the vehicle sent it.
On this page9
The flight log shows what PX4 believed about its link. The receive log shows what actually reached the ground, and when. The MAVLink receiver source records it: it Listens to telemetry on the ground and records every receipt with its own clock.
Before you start#
- You need the Admin role.
- The receiver must hear your vehicles' MAVLink telemetry at the address you give it.
- Keep the receiver's clock on NTP. In the example dataset it reads NTP, synchronised, good to ±15 ms.
- Know the MAVLink system IDs of the vehicles to record. Each vehicle page shows its system ID.
Add the receiver#
Choose MAVLink receiver
In Sources, select Add source. Under Vehicles, pick MAVLink receiver and select Continue.
Fill in the fields
Field Example in the console What it sets Name For example: Fleet log archive, west range A name the team will recognize, such as the range it serves. Listen on udp://0.0.0.0:14560Where the receiver listens. The example listens for UDP on port 14560, on every interface. System IDs 1 to 16 The MAVLink system IDs to record. Record Every HEARTBEAT and SYSTEM_TIME receipt Which messages to keep. Test and add
Select Test connection and wait for the fourth check. Then select Add source. The toast names the source and says The first sync is running.
If range control already writes receive logs to a server, connect that directory instead. The example dataset's Range control receive logs source reads *.jsonl over SFTP from /var/log/mav-receive/ and polls every 5 minutes. See SFTP, SMB and NFS.
What a receive record holds#
A receive log is receiver JSONL: one JSON object per receipt, one receipt per line. Foxborne recognizes the file by the key "recv_utc". This is the last heartbeat from UAS-04 before the telemetry gap in INC-0142:
{
"recv_utc": "2026-09-24T14:32:03.912Z",
"mono_ns": 12805145901122,
"link": "mesh0",
"sysid": 4,
"compid": 1,
"msgid": 0,
"msg": "HEARTBEAT",
"seq": 204,
"len": 9
}| Field | What it holds |
|---|---|
recv_utc | The arrival time in UTC, on the receiver's clock |
mono_ns | The arrival time on the receiver's monotonic clock, in nanoseconds |
link | The link the message arrived on |
sysid, compid | The sender's MAVLink system and component IDs |
msgid, msg | The message ID and name, here 0 and HEARTBEAT |
seq | The MAVLink packet sequence number, 0 to 255 |
len | The payload length in bytes, 9 for a HEARTBEAT |
Receiver JSONL records gives the full schema.
Receive time is not send time#
A receipt always comes after the transmission. The record gives the arrival, and the departure was earlier by the link latency. Latency widens the bound on one side only, toward earlier times, because it can never move a transmission after its receipt.
For an event at time t with clock error err and link latency lat, the interval is [t − err − lat, t + err]. The heartbeat above arrived at 14:32:03.912, with ±15 ms on the receiver clock and up to 412 ms of latency. It left UAS-04 between 14:32:03.485 and 14:32:03.927.
Latency per run#
Foxborne measures link latency for each run and reports its median and 95th percentile. Bounds carry the 95th percentile.
| Run | Incident | Median | 95th percentile |
|---|---|---|---|
| R-0931 | INC-0142 | 96 ms | 412 ms |
| R-0934 | INC-0143 | 88 ms | 380 ms |
The latency follows the receive log wherever it appears. Source files gives its bound as ±15 ms + latency, and Ordering bounds between sources adds a line such as plus 412 ms latency. On the event page, the Clock card's Receive latency row reads Up to 412 ms, 95th percentile.
Foxborne 1.4.1 added receive log latency estimates from SYSTEM_TIME. That MAVLink message, msgid 2, carries the sender's own clock: time_unix_usec, in microseconds since the UNIX epoch, and time_boot_ms, in milliseconds since boot.
How telemetry_gap reads the log#
The telemetry_gap rule reads HEARTBEAT receipts (msgid 0) from each vehicle's system ID. It measures the time between consecutive heartbeats and fires when two are more than 3 s apart.
The finding sits where the next heartbeat was due: one expected interval, 1 s at 1 Hz, after the last receipt. In INC-0142 the receiver heard UAS-04 at 14:32:03.912 and then not until 14:32:10.874.
The finding reads Heartbeat due and not received. Nothing for 6.96 s, with the wording Observed: telemetry receive gap, not necessarily radio failure.
On the timeline, the Ground receive lane draws each receipt as a tick and hatches the gap, labeled No telemetry received for 6.96 s.
Check the result#
- The source appears in Connections with Healthy under Status.
- Runs gain a Ground receive log under Source files. Its clock reads Range control receiver clock, its anchor NTP, synchronised and its parser
receive 0.4.1. - The audit log records Added source with a detail such as MAVLink receiver, read-only test passed.
Troubleshoot#
- Runs show Partial with No ground receive log for this run. The receiver did not record that run, or its log never reached Foxborne. Check the source's Last sync, and import the log by hand if you have it.
- A gap appears, and the radio seems fine. A receive gap is not a radio failure. Compare the vehicle's own view in
telemetry_statusbefore you conclude anything, as telemetry_gap explains.
Next#
- telemetry_gap describes the rule that reads this log.
- Receiver JSONL records lists every field.
- Clocks, anchors and error bounds explains how the three clocks combine.