Skip to content
Docs
foxborne.comRequest a pilot

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.

ReferenceMarkdown
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#

FieldType and unitMeaning
recv_utcString, UTC, ISO 8601 with millisecondsTime the message reached the receiver, on the receiver's NTP-disciplined clock. The console shows this time.
mono_nsInteger, nanosecondsMonotonic time on the receiver at arrival.
linkStringThe link the message arrived on: mesh0 in the example dataset.
sysidIntegerMAVLink system ID of the sender, which identifies the vehicle. In the example dataset, UAS-04 is 4 and UGV-02 is 22.
compidIntegerMAVLink component ID of the sender within its system. Every example record carries 1.
msgidIntegerMAVLink message ID. 0 is HEARTBEAT.
msgStringMessage name, such as HEARTBEAT.
seqInteger, 0 to 255MAVLink packet sequence. It counts every message the system sends on the link, not only heartbeats, and wraps at 256.
lenInteger, bytesPayload 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:

rc1-receive.jsonlExample dataset, INC-0142, line 1,118, one line in the fileJSON
{
  "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:

ColumnValueWhat it means
Clock and anchorRange control receiver clock, anchored by NTP, synchronised, offset +0.4 msThe receiver's own clock, kept on UTC by NTP.
Bound±15 ms + latencyThe clock's error bound, plus link latency on the early side only.
Parserreceive 0.4.1The 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.

Send window of the example recordExample dataset, INC-0142Text
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.927

The 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.

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.

RunMedian95th percentile
R-0931, INC-014296 ms412 ms
R-0934, INC-014388 ms380 ms

The console shows the latency wherever it shows a receipt's time:

WhereWhat it reads
Run page, Bound±15 ms + latency
TimelineA dashed extension to the left of each receipt's error bracket
Evidence inspector, WhenThe bound, then + 412 ms latency
Timeline hoverplus up to 412 ms latency
Ordering bounds between sourcesA second line, such as plus 412 ms latency
Event page, Context, ClockReceive 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.