ados-swarmbus is the service. It is a Core service on both flight-capable
profiles (drone and ground_station), gated the same way ados-mavlink is,
because the two are the same seam seen from either end: a drone fills its beacon
from the router’s state socket, and a ground station listens so the operator’s
fleet view is local-first.
A ground station holds slot 0 and never emits a beacon. It is not an aircraft.
The beacon
20 bytes on the air. Position and velocity use the MAVLinkGLOBAL_POSITION_INT encodings verbatim (degrees times 1e7, decimetres, cm/s),
so filling a beacon from the flight controller’s fused state is a shift rather
than a lossy re-quantisation.
Heading is not carried because every reference implementation derives it from
velocity, and 2 bytes to restate information the velocity already holds is
2 bytes of airtime.
seq_ms deliberately carries no wall clock: two drones with
unsynchronised clocks must still agree on which of two beacons is newer, and a
65.5 second wrap is far longer than the staleness window.
Status bits
One byte packs five independent conditions plus the active mode-precedence level. Each bit is a separate condition and they are never blended: an armed drone with a bad GPS fix and an armed drone in emergency are different situations.Airtime
This is the number the design lives on, so here is the arithmetic rather than the assertion. One beacon on the air at MCS 0:The neighbour table
Three seconds rather than one because outages of about a second are normal in
real formation flight, and dropping a neighbour that is still there is worse
than carrying a slightly old one: the separation layer would stop avoiding an
aircraft that has not gone anywhere.
The 64-entry cap is not reachable by a legal fleet. It bounds the table against
a garbage or hostile slot flood, since the slot field is a byte and 255 distinct
values are expressible.
GET /api/swarm/neighbors
fleet_id, slot, a neighbors array, a slots table and
a counter block.
The two differ exactly when a drone is lost, which is the operator-facing fact
slots exists to carry. A slot present in slots but absent from neighbors is
an aircraft the fleet issued a slot to and has since stopped hearing.
Each neighbour row carries slot, device_id, seq_ms, lat, lon, alt_m,
vx_ms, vy_ms, vz_ms, heading_deg, armed, guided, emergency,
gps_ok, hero, mode_precedence, age_ms and rssi_dbm. The counter block
carries beacons_tx, beacons_rx, beacons_bad_magic, beacons_bad_tag,
beacons_stale_dropped and neighbors_now.
Two rules the shape follows throughout:
- A missing reading is
null, never a plausible number.rssi_dbmis null when the capture carried no signal field, anddevice_idis null when the slot cannot be joined to an identity. A fabricated-100 dBmwould render as a real value. - Derived fields are computed on the node, so every consumer agrees on them.
heading_degandage_msare emitted rather than left to the client.
200. When the bus has published nothing (an absent
socket, a profile that does not run it, a radio that has not come up) the body is
the degraded shape: an empty neighbour array, zeroed counters, and fleet_id and
slot as null. Null rather than the config defaults, because a reader cannot
know the fleet identity of a service that is not running, and reporting 1 and
0 would make an unprovisioned node indistinguishable from a correctly
provisioned fleet-1 node with no neighbours.
The publish socket comes up first and unconditionally, so a bus with no radio
still answers with an empty table and zeroed counters. That is distinguishable
from an absent service, which answers with a null fleet id.
Fleet addressing
The bus rides the fleet identity the radio plane already uses. Nothing new is invented for it, so a node cannot be addressed differently on the two planes./etc/drone.key. That
file is the only shared-content key on disk: the bind protocol delivers it
byte-for-byte to both the drone and the ground station. The wfb-ng tx.key and
rx.key are the two different halves of a crypto_box pair, so a symmetric key
derived from either diverges across the two rigs and every frame is silently
dropped at the far end.
One symmetric key for the whole fleet is the point: every drone must be able to
decrypt every other drone’s beacon, which is what makes the neighbour table
work with the ground station off. A fleet is one trust domain.
The key changes whenever a bind rewrites /etc/drone.key, so the supervisor restarts ados-swarmbus after every bind. See WFB-ng pairing for the bind and Key model for the radio key files.
The autonomy layer
Theswarm: config block drives the onboard autonomy that consumes the
neighbour table. It is off by default.
What the keys mean:
enabledis the operator switch. With it false the setpoint loop returns immediately.modeis the operator-commandable behaviour mode. Hard separation and operator direct command are precedence levels the runtime arbitrates into, not modes anyone commands, so neither is a value here.default_formationis a closed set matching the built-in generators. An unknown name is rejected at config load rather than silently producing no formation.default_spacingis metres between slots.flock.cohesion,flock.alignmentandflock.separation_gainare Olfati-Saber alpha-lattice weights, as integer percentages of the underlying float weight (40means 0.40), because the Mission Control config primitives carry no float field.flock.radius_mis the range beyond which neighbours contribute nothing to the flocking terms.flock.neighborscaps how many of the nearest neighbours are weighted, so a dense cluster cannot dominate the solution.separation.radius_mis where repulsion starts.separation.hard_mis where the horizontal solution is abandoned for a deterministic climb and hold. It must stay belowradius_m; the config model rejects it otherwise.tasks.enabledjoins consensus-based task allocation.assigned_task_idandbundle_positionin the same block are agent-written status mirrors and stay null until a runtime writes them.
The setpoint loop
The loop runs insideados-mavlink, at 10 Hz, against the last neighbour
payload, dead-reckoning each neighbour forward by its age plus the time since
that payload arrived. That predict-and-correct split is why a 2 Hz beacon can
drive a 10 Hz controller: the loop never sees a staircase, and the correction
arrives before the prediction has drifted.
It lives in the router because the setpoint has to leave through the process
that holds the FC link, the sequence counter and the writer. Putting it in the
bus would mean a second command path to the autopilot.
It commands the flight controller through
SET_POSITION_TARGET_GLOBAL_INT and adds no radio traffic of its own: every
input is already on the air for other reasons, so a 24-drone swarm costs exactly
the beacon bandwidth.
Precedence
Highest authority first: hard separation, operator direct command, formation, flocking, hold. What rides bits 5 to 7 of the beacon is the active level, not the commanded one. A drone whose separation layer has taken over readshard-separation on the
operator’s screen, never the mode somebody asked for. Mode-transition ambiguity,
an operator believing one mode governs a vehicle while another actually does, is
implicated in a long series of supervisory-control losses, so the active level is
broadcast by the aircraft itself and every reader derives it from the same byte.
A node with no autonomy layer running radiates 000 there and every reader
honestly decodes hold.
What the layer never does
- No leader election. The operator screen is the single authority by construction. Electing a leader among drones over a lossy broadcast invites a split brain, a leader that still hears its peers but has lost the operator, for no benefit.
- No failsafe of its own. ArduPilot’s
FS_GCS_ENABLEandFS_LONG_ACTNand the geofence are the backstop and act independently. - It stops rather than substitutes. When the swarm is disabled, the vehicle is disarmed, the flight controller is out of GUIDED, or the neighbour table has been empty for the 3 second staleness window, the loop emits nothing and the flight controller holds on its own terms. It never competes with the autopilot’s failsafes.
Next steps
WFB-ng
The radio link the beacon rides, and its fleet addressing.
WFB-ng pairing
The bind that creates the fleet key.
World Engine
The extension that consumes the shared fleet picture.
Safety
Read before flying more than one aircraft.