Receiver JSONL records
Read any ground receive record field by field, from the MAVLink message that arrived to the system that sent it and the moment it reached range control. A receipt trails its send time, and this page shows by how much.
On this page7
Receiver JSONL is the ground receive log. The MAVLink receiver at range control writes one JSON object per line, one line per message it heard, stamped on arrival by its own clock. The telemetry_gap rule reads these records, and the timeline draws them as a receipt rug.
Record fields#
| Field | Type and unit | Meaning |
|---|---|---|
recv_utc | String, UTC, ISO 8601 with milliseconds | Time the message reached the receiver, on the receiver's NTP-disciplined clock. The console shows this time. |
mono_ns | Integer, nanoseconds | Monotonic time on the receiver at arrival. |
link | String | The link the message arrived on: mesh0 in the example dataset. |
sysid | Integer | MAVLink system ID of the sender, which identifies the vehicle. In the example dataset, UAS-04 is 4 and UGV-02 is 22. |
compid | Integer | MAVLink component ID of the sender within its system. Every example record carries 1. |
msgid | Integer | MAVLink message ID. 0 is HEARTBEAT. |
msg | String | Message name, such as HEARTBEAT. |
seq | Integer, 0 to 255 | MAVLink packet sequence. It counts every message the system sends on the link, not only heartbeats, and wraps at 256. |
len | Integer, bytes | Payload length. A HEARTBEAT payload is 9 bytes. |
Every record in the example dataset is a HEARTBEAT, which the vehicles send at 1 Hz. telemetry_gap reads only HEARTBEAT records and measures the time between consecutive ones from the same sysid.
Example record#
This is the last heartbeat from UAS-04 before the gap in INC-0142, line 1,118 of rc1-receive.jsonl:
{
"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
}The next record from system 4, on line 1,119, arrived at 14:32:10.874. The 6.96 s between the two is the gap telemetry_gap reports as Heartbeat due and not received. Nothing for 6.96 s.
That record's seq is 5, not 205, because the counter moves on with every message the vehicle sends on the link and wraps at 256.
The evidence inspector shows the pair, indented, under Original record, with Lines 1,118 and 1,119 under Location.
How Foxborne recognizes the format#
Import files reads the first 4,096 bytes of each file. A file with "recv_utc" in those bytes is detected as Receiver JSONL, with the detail Receive records found.
This check runs after the ULog magic bytes and __REALTIME_TIMESTAMP, and before the collector check for "boot_id" or "rss_kib". Collector JSONL records gives the full order.
Clock and bound#
Source files on the run page lists the receive log with these values, here from run R-0931 in the example dataset:
| Column | Value | What it means |
|---|---|---|
| Clock and anchor | Range control receiver clock, anchored by NTP, synchronised, offset +0.4 ms | The receiver's own clock, kept on UTC by NTP. |
| Bound | ±15 ms + latency | The clock's error bound, plus link latency on the early side only. |
| Parser | receive 0.4.1 | The parser and version that read the file. |
On the timeline, the Ground receive lane, subtitled sysid 4, 1 Hz, draws a receipt rug with one tick per record. A stretch with no receipts is hatched and labeled, such as No telemetry received for 6.96 s.
Receive time is not send time#
A receipt always comes after the transmission, later by the link latency. Foxborne gives each receipt an interval that reaches back by the clock error plus the latency, and forward by the clock error alone.
earliest send = recv_utc - error - latency = 14:32:03.912 - 15 ms - 412 ms = 14:32:03.485
latest send = recv_utc + error = 14:32:03.912 + 15 ms = 14:32:03.927The last heartbeat before the gap left the vehicle between 14:32:03.485 and 14:32:03.927. Reports quote that window, not the arrival time alone.
Link latency#
Foxborne measures link latency per run and carries the 95th percentile as the one-sided term. Since Foxborne 1.4.1, latency estimates for receive logs come from SYSTEM_TIME.
| Run | Median | 95th percentile |
|---|---|---|
| R-0931, INC-0142 | 96 ms | 412 ms |
| R-0934, INC-0143 | 88 ms | 380 ms |
The console shows the latency wherever it shows a receipt's time:
| Where | What it reads |
|---|---|
| Run page, Bound | ±15 ms + latency |
| Timeline | A dashed extension to the left of each receipt's error bracket |
| Evidence inspector, When | The bound, then + 412 ms latency |
| Timeline hover | plus up to 412 ms latency |
| Ordering bounds between sources | A second line, such as plus 412 ms latency |
| Event page, Context, Clock | Receive latency: Up to 412 ms, 95th percentile |
Latency also decides how a receipt orders against other sources. In INC-0142, mavlink-router.service stopped at 14:32:04.152 on the journal, with a bound of ±6 ms. The next heartbeat was due at range control at 14:32:04.912, with ±15 ms and up to 412 ms of latency.
The two are 760 ms apart against a bound of 6 + 15 + 412 = 433 ms, so their order holds. The report files the pair as correlated, with the note Due 0.76 s after mavlink-router.service stopped. Bound ±0.43 s including link latency.
Related#
- telemetry_gap reads these records and explains what a receive gap does not mean.
- MAVLink ground receiver covers the receiver itself.
- Clocks, anchors and error bounds and Alignment and event order explain how a receipt's bound combines with other sources.
- Collector JSONL records documents process samples from the companion computer.