Diagnostics with ados diag
ados diag is the first thing to run on a misbehaving node. It is three thin
renderers over the agent’s own REST surface, so the CLI shows exactly what
Mission Control sees.
--json for scripted use.
ados diag does not appear in ados help. The advanced groups are hidden so the
common path stays uncluttered; ados diag is a working diagnostic group rather
than a first-run command, and it is fully supported.Run the node’s own diagnostics first
A wrong diagnostic conclusion on a flight system is worse than no conclusion, so the rule is: reach for the agent’s own verdicts before hand-probing a pipeline. The reason is measurement quality.ados diag video reads only reliable
cumulative and rate counters on canonical objects (the mediamtx main path,
the wfb-stats sidecar, the WHEP endpoint) and samples each of them twice over
a 2 second window, so a hop is judged by data actually moving between the two
reads rather than by a process being alive.
Signals that are not primary evidence
The link verdict solves the same problem differently:
channel_locked comes
from the radio’s own rx_proven, and rf_unverified is its explicit
“transmitting with zero confirmed reception” state. Both are derived from
decoded peer traffic, frames that passed error correction and decryption,
which is why they can prove delivery when a byte counter cannot. The
rf_unverified condition must hold continuously for 10 seconds before the agent
records an entry event, so a single missed beacon does not flap it.
ados diag video
profile, role, canonical_path (always
main), window_s, the hops array and video_dies_at. The hop set is
profile-aware, because a drone is the video source and a ground station is the
sink.
Drone hops
Ground-station hops
Verdicts
video_dies_at names the first real break. When every hop is flowing the
renderer prints all hops flowing; when no hops come back at all it says so
rather than inventing a verdict.
ados diag link
/api/wfb. Two things are reported side by side and deliberately not
merged.
The decode verdict (link_diag) answers “can this radio decode its peer at
all”:
The pairing state is the lifecycle record. The two legitimately disagree: a
link decoding hundreds of packets a second can sit behind a lifecycle record
nothing has refreshed. Printed bare, “HEALTHY (state=stale)” reads as a surface
contradicting itself, so the renderer labels which word is answering which
question.
Packet loss and delivered frames
link_diag is not a delivery verdict. A link that decodes fine while dropping a
fifth of the stream still answers healthy to “can you decode”, and that is
true and beside the point. So the diagnostic leads with LOSSY once measured
loss reaches 2%.
A video frame needs every one of its packets, so loss compounds across the
frame. At roughly 10 RTP packets per frame:
That is why “2% packet loss” and “a frozen picture” describe the same link. The
figure is an estimate under an independent-loss assumption and is always printed
with the assumption stated; bursty loss is kinder to it.
Who measured the loss
A transmit-only drone has no local loss figure and never will: it injects its own downlink and a single radio in monitor mode cannot capture its own frames, sopackets_received is a permanent zero. The measurement does exist,
because the receiving ground station counts exactly what arrived and sends it
back up the link, so the diagnostic prefers the radio’s resolved sample and
labels its provenance:
A number with invisible provenance is a number an operator cannot act on: “3%
loss” means something different when this node’s own receiver counted it than
when the far end did. And when nobody measured, the diagnostic says which
measurements are missing rather than printing a bare dash next to a clean-looking
link. A measured zero and an unmeasured link are different facts and are reported
differently.
Counters
The readout carriesRSSI, Channel, Decoded pkt/s, RF frames (all),
Decrypt errors, Bad packets, Packets lost, FEC recovered, FEC failed,
Loss and Bitrate. The three FEC and loss rows are what decide whether video
actually arrives.
ados diag storage
Write rate and its provenance
The write rate is reported with a window and a source, because five seconds of kernel counter and hours of stored history answer different questions:
The direct reading needs nothing but the kernel, so it is the one that still
works with the Black Box store off; the stored one reaches much further back.
Neither is “the” rate on its own. An unmeasured rate is never printed as a zero:
the reason it is missing is printed instead.
Power and thermal
On a board that reports it, the throttle bitfield’s sticky “has occurred” bits are surfaced:undervoltage, frequency capped, throttled, soft temp limit.
These are recorded at some point in the retained window, not necessarily now,
which is exactly what makes them useful for a node that misbehaves
intermittently. A board that does not report them says so.
Store footprint
The log store’s own live and WAL sizes are reported, along with any quarantined database. With the store off, the row readslog store disabled (journal is the record) rather than 0 B live, because
“a store that exists and is empty” is a different and more alarming thing than
“a store nobody asked to run”.