Loomwave: Infrastructure-Coordinated TDMA for Reliable LoRa Mesh at Hidden-Node Scale

Status: Draft RFC (working synthesis, 2026-06-23). Internal review. Authors: Loomwave project. Audience: LoRa-mesh implementers and operators; assumes familiarity with LoRa PHY and Meshtastic/MeshCore.

Document plan. This file is the assembled paper (#11). Front matter (Abstract, §1–§2) is drafted to set the frame and the claims; §3–§11 are section contracts — each names its scope, its load-bearing claim, and the source documents that ground it — to be expanded into prose without re-deriving. Every quantitative claim must cite a *-result.md (sim) or a named hardware/field run; no unsupported "reduces collisions" assertions (the project's quantitative-discipline rule). Terminology: see glossary.md.

Wire-v2 spec correction (2026-07-02, issue #105). packet-format.md was hand-corrected to document the shipped wire v2 (VER = 0b010) — the PACT-lite membership beacon block, the TIM downlink-pending bitmap, the announced-PHY-change flag, and the on-demand grant list (it previously described the retired v1). A new reliable-messaging.md specifies the Stage-4 transport (ENV_TEXT envelope, ARQ, and the downlink RX-window timing) that until now had no home in the paper. Both were transcribed by hand from infra/crates/proto/src/lib.rs and the firmware, superseding the older auto-generated mirror (its generator pulls from a private repo that is no longer reachable).


Abstract

LoRa mesh networks built on flooding (Meshtastic) or path-vector source routing (MeshCore) degrade predictably as node count and channel contention rise, and neither addresses the hidden-node regime that dominates the deployments LoRa is best at — elevated relays that hear many terminals which cannot hear each other. We present Loomwave, a from-scratch LoRa mesh platform built on a single thesis: where an elevated infrastructure node exists, it should be the time, frequency, modulation, and topology authority, turning the hidden-node liability (one node hears everyone) into the asset that makes a collision-free schedule possible. Loomwave pairs a hybrid CSMA-discovery / TDMA-data MAC with a rotating-roster membership scheme (PACT-lite) that decouples cell membership from per-frame airtime, so a cell serves dozens of members at constant telemetry cost; an INFRA-aggregated distance-vector routing layer whose control overhead is O(N) rather than flooding's super-linear collapse; and self-certifying addressing with per-frame AEAD sized to the LoRa payload budget. The design is validated three ways: a SimPy discrete-event simulator calibrated against real propagation traces (TDMA delivers 96.5% at K=100 vs CSMA's 23%; routing control is ~23× cheaper than flooded link-state at N=100); hardware bring-up on a fleet of ESP32/nRF52 clients and Raspberry-Pi INFRA nodes (membership, on-demand messaging, adaptive spreading factor, dynamic frequency selection, and the multi-cell backbone stagger all confirmed on real radios); and a captured real-world hidden-node hub (Stone Mountain: 29 mutually-hidden terminals heard directly, ~93% mutual hiddenness) that anchors the scaling argument in measured topology rather than model assumption.


1. Introduction

1.1 The problem

LoRa's long range and sub-GHz penetration make it the natural bearer for off-grid messaging and telemetry, but its MAC is unforgiving: it is half-duplex, airtime is long (tens of milliseconds to seconds per frame), and two frames overlapping at a receiver are both lost. The dominant open implementations cope poorly as networks grow. Meshtastic floods: every node rebroadcasts, so control traffic and collisions rise super-linearly and the network suffers broadcast-storm collapse at scale (a 500-node LongFast domain admits roughly one message per node per four minutes — §2, case-study-meshtastic.md). MeshCore uses path-vector source routing: efficient once paths are known, but brittle to churn and still contention-bound on a shared channel (case-study-meshcore.md). Neither confronts the hidden-node problem as a first-class design constraint, yet it is precisely the regime that elevated LoRa relays live in.

1.2 The thesis

Loomwave's central observation: the hidden-node topology that breaks contention MACs is the same topology that makes a coordinated schedule possible. An elevated infrastructure (INFRA) node that hears N mutually-hidden terminals cannot let them contend — at 93% mutual hiddenness, carrier sense is physically dead at the hub — but it can schedule them, because it alone has the global view. Loomwave therefore makes the INFRA node the authority along four axes that contention MACs leave to chance:

Axis Authority Mechanism §/Spec
Time slot grants, beacon cadence TDMA schedule in a signed beacon §4 mac-spec.md
Frequency center freq per cell/sector DFS + announced channel plan §6 dfs-result.md
Modulation SF/BW/CR per cell, per link adaptive SF (per-cell + per-link) §6 per-link-sf-result.md
Topology route aggregation across cells INFRA-DV + link-state backbone §7 routing-control-result.md

Where no INFRA node is present, Loomwave degrades gracefully to an epoch-anchored contention fallback that never performs worse than CSMA (fallback-result.md) — pure-LoRa operation with zero infrastructure remains a first-class mode, not a bolt-on.

1.3 Contributions

  1. An INFRA-coordinated hybrid MAC (CSMA discovery, TDMA data) that eliminates hidden-node collision loss where coordination exists, with a measured fallback that floors at CSMA where it does not (§4).
  2. PACT-lite membership — a rotating telemetry roster + coordinator-polled demand that decouples cell membership from per-frame airtime, so idle members cost nothing and a cell scales from 3 to dozens of members without added airtime (§5; the #69 result).
  3. A full PHY-agility layer — per-cell and per-link adaptive spreading factor, dynamic frequency selection, and guided inter-cell roaming — all under the INFRA-as-authority model and all hardware-verified (§6).
  4. An O(N) routing/backbone design — INFRA-aggregated distance vector with a half-frame backbone stagger for multi-cell deployments, ~23× cheaper in control overhead than flooded link-state (§7).
  5. A three-way validation methodology — calibrated simulation, hardware bring-up, and a captured real-world hidden-node hub — that grounds every protocol decision in measured rather than assumed behavior (§9–§10).

1.4 Validation posture

Per the project's quantitative-discipline rule, no design claim stands on intuition. Simulation results are tagged and reproducible (sim-result-v1…); hardware claims name the specific bench/field run and were required to be observed on a separate receiver before being asserted; and the scaling argument is anchored to a captured deployment (Stone Mountain, stone-mountain-hidden-node-hub.md) rather than a synthetic topology. Idealizations and unverified edges are called out per section.


2. Background and Problem Statement

2.1 The LoRa channel is unforgiving in three specific ways

A LoRa link is governed by physics that a MAC cannot wish away. First, airtime is long and exactly predictable: the time-on-air of a frame is a closed-form function of spreading factor (SF), bandwidth (BW), coding rate (CR), and payload length, ranging from tens of milliseconds at SF7 to several seconds at SF12. A 35-byte telemetry record at SF9/BW125 occupies ~280 ms; a 162-byte message ~874 ms. Because airtime is bounded below by this law, the channel has an exact, unbeatable packet ceiling — on the order of 7.2 frames/second on a MediumFast channel — and any MAC's job is to spend that budget well, not to exceed it. Second, the radio is half-duplex: a node cannot receive while transmitting, so every transmission is a deafness window. Third, two frames that overlap in time at a receiver collide; the receiver may sometimes capture the stronger one, but only when it arrives first in the preamble and exceeds the interferer by a hardware-dependent margin (~6 dB co-SF), and the commodity SX126x/LR11xx radios offer no successive-interference cancellation — so capture is luck, not a mechanism a protocol may rely on. This last fact is load-bearing for the entire design: it forecloses the coded-slotted-ALOHA family (CRDSA/IRSA), whose efficiency depends on SIC that the hardware cannot perform, and it means the weakest-SNR terminals — exactly the edge nodes an elevated relay exists to serve — lose every capture lottery they are forced into. (hidden-node-analysis.md, mac-layer-options.md.)

2.2 The hidden-node problem, stated for elevation

Two terminals A and B are hidden from each other when both are heard by a relay R but neither can hear the other. Carrier sense then fails by construction: A and B each sense an idle channel and transmit, and their frames collide at R despite both having "listened first." This is not an edge case for elevated LoRa relays — it is the common case. A node on high ground trades a clear path to many distant terminals for the near-certainty that those terminals, scattered across valleys and ridgelines, cannot hear one another. The captured Stone Mountain hub makes the point concretely: a single elevated relay heard 29 terminals directly, of which ~93% were mutually hidden, and relayed ~200 more (stone-mountain-hidden-node-hub.md, elevated-nodes.md). At that hiddenness, carrier sense is physically dead at the hub — there is no listening discipline a contending terminal can adopt that avoids the collision, because the information it would need (is another hidden terminal mid-transmission?) is exactly what it cannot observe.

2.3 Why the incumbents do not scale here

Meshtastic floods. Every node rebroadcasts every packet it has not seen; control and redundant-relay traffic grow super-linearly in node count, and on a shared channel the result is broadcast-storm collapse — a 500-node LongFast domain admits roughly one message per node per four minutes (case-study-meshtastic.md, meshtastic-routing-analysis.md). Flooding is acceptable below the knee (sparse networks, §9), but it has no answer to the hidden-node hub: it converts one terminal's transmission into N retransmissions competing on the same dead-carrier-sense channel. MeshCore source-routes along discovered path vectors — efficient once paths are known, but the discovery is advert-flood-based, the paths are brittle to the churn of a mobile/intermittent fleet, and the data plane is still contention-bound on the shared channel, so the hidden-node collisions remain (case-study-meshcore.md, meshcore-routing-analysis.md, meshcore-network-characterization.md). Neither system treats the hidden-node regime as a design center; both inherit its collisions.

2.4 The reframing

The airtime ceiling is fixed and the hub's carrier sense is dead, so the only reclaimable capacity is the airtime currently wasted on avoidable collisions and on idle nodes holding standing resources. That reframes the problem from "make contention smarter" — impossible at 93% hiddenness — to "let the one node that hears everyone schedule them," and, separately, to "make idle membership free" (§5). The rest of this paper is the consequences of taking that reframing seriously. Where the scheduling authority is absent, Loomwave must still degrade to contention without ever doing worse than the incumbents — a floor, not a cliff (§4.4, fallback-result.md).


3. Architecture Overview

The architecture is the disciplined consequence of the §1.2 thesis: take the one node that hears every (mutually-hidden) terminal and make it the authority.

3.1 Asymmetric roles as separate firmware

Loomwave is not a runtime monolith that negotiates behaviour per node. There are three roles, each a separate optimized firmware build, and role is not runtime-negotiable in v1 (node-roles-spec.md §1; design-decision.md §1). A CLIENT is a leaf: it sleeps aggressively, relays nothing by default, and stores only its own pending traffic — so a dense crowd of phones does not multiply flood load (node-roles-spec.md §2). A REPEATER is the same hardware class minus sleep: always-on receive, forwarding only in its own scheduled slot, which is what prevents the relay storm (node-roles-spec.md §3). An INFRA node is a Linux SBC with persistent storage and optionally multiple radios; it is the only role that may coordinate (node-roles-spec.md §4).

The asymmetry is deliberately confined to behaviour and budget, never to protocol dialect: all three roles share one wire format and one security scheme, so three differently-optimized builds interoperate on one mesh (node-roles-spec.md §5). This is what lets the architecture specialize each node's power and airtime profile without fragmenting the network into incompatible populations.

3.2 INFRA as authority — why concentration makes the schedule feasible

A coordinating INFRA node is not merely a relay with more storage; it is the time, slot, modulation, and topology authority for its cell (design-decision.md §1, §3–§5). Each superframe it emits a signed beacon carrying the epoch, cell descriptor, and slot map, acting as the time reference (node-roles-spec.md §4.2). It grants and expires slots from client registrations; it picks the cell's modulation from clients' link reports; it runs the link-state backbone; and it admission-controls any backhaul-sourced traffic into the schedule rather than raw-flooding it onto RF (node-roles-spec.md §4.2; design-decision.md §5).

Authority concentration is precisely what makes a collision-free schedule feasible at a hidden-node hub, where carrier sense fails by construction (§2.4). The coordinator reclaims the wasted airtime directly: scheduled access eliminates the hidden-node collisions CSMA cannot (tdma-data-plane-result.md reports 99.8/99.6/99.2/96.5% delivery vs CSMA's 88/74/50/23% at K=10/20/50/100, with collision-free operation by construction), and a registered, coordinator-elected modulation lets each cell run the fastest SF its clients' budgets allow (adaptive-modulation-result.md reports ×16 capacity at 50 nodes, ×34 at 500). Authority is the enabling mechanism; the deltas are the evidence that the concentration pays for itself (tdma_vs_csma.png, capacity_spatial_reuse.png).

Crucially, coordinator status is earned, not configured. An INFRA node may advertise a coordinator claim carrying corroborable reach and vantage evidence; the network elects on measured score, and a mislabeled or poorly-sited INFRA node must not force itself to coordinate over a better-corroborated peer (node-roles-spec.md §4.1; design-decision.md §4). This guards the authority model against the real failure it would otherwise invite — half of the eight observed high sites are role-missed (CLIENT_MUTE or unset) yet sit 96–351 m above their neighbours, so a self-declared role field is an unreliable signal for who actually commands the vantage (high-site-backbone.md).

3.3 Three regimes: cell, backbone, and coordinator-free fallback

The architecture operates in three nested regimes. Within a cell, a single coordinator owns the superframe and schedules its registered members. Across cells, the small set of elected coordinators forms the backbone: an aggregated link-state network confined to the INFRA tier, which is what keeps control overhead O(N) rather than the O(N²) collapse of mesh-wide flooding (design-decision.md §4; routing-control-result.md reports ~23× cheaper control). This tier is physically realizable from existing high ground: the region's real high sites already cross-reference into a single connected backbone — 8 elevated nodes, 15 of 28 candidate elevated-to-elevated links, region spanned in 2–3 hops, with BSRG as a keystone cut vertex (high-site-backbone.md, backbone_topology.png).

That cut vertex is exactly why the third regime is first-class, not a courtesy. Pure-LoRa, zero-infrastructure operation is a primary mode (design-decision.md §1; CLAUDE.md goal #4). When no coordinator is reachable — backbone partition, tier failure, or simply no INFRA present — every role drops to a coordinator-free fallback: epoch-hash-slotted contention plus AODV on-demand routing (node-roles-spec.md §2.1, §5). Fallback needs no carrier sense, so it still works at a hidden-node hub, and it never degrades below the CSMA floor (fallback-result.md; fallback_schemes.png). The network never depends on any single role being present: a CLIENT-only cluster with no INFRA still communicates, degraded but functional. Clients detect a vanished coordinator by beacon silence and re-home onto the next surviving tier or into fallback (node-roles-spec.md §2.4), so authority concentration buys schedule feasibility without becoming a single point of failure.

3.4 Where the signed beacon contract sits

The signed beacon is the seam between authority and autonomy. It is the coordinator's per-superframe assertion of the entire schedule — epoch, cell descriptor (the modulation decision), and slot map — carried in one Ed25519-signed, rate-capped frame (node-roles-spec.md §4.2; packet-format.md, per design-decision.md §7). Everything downstream hangs off it: clients derive slot timing, AEAD nonces, and replay ordering from the epoch it advances, which the INFRA node persists across reboot so it never reissues a used (epoch, seq) (node-roles-spec.md §4.4). And the beacon is the liveness signal whose absence triggers the fallback transition — clients abandon a coordinator that stops beaconing and re-home or drop to epoch-hash. The beacon thus sits exactly on the boundary between the coordinated and uncoordinated regimes: present, it concentrates authority and unlocks the schedule; absent, it hands the network cleanly to its coordinator-free floor.

4. The MAC Layer — Hybrid CSMA-Discovery / TDMA-Data

The MAC is where the §1.2 thesis becomes a frame format: the one node that hears every mutually-hidden terminal schedules them rather than letting them contend. The MAC is therefore a hybrid of two cooperating planes carried in one frame structure: a contention plane for discovery and a scheduled (TDMA) plane for data (mac-spec.md §1). A node selects its plane through the 1-bit P field in the common header (P=1 scheduled, P=0 contention; packet-format.md §3) and may transmit only in the region of the superframe that matches it.

4.1 Superframe structure

Under a coordinator, time is divided into superframes of length FRAME_T, announced in the beacon. Each superframe is BEACON | guard | scheduled data slots | contention window (mac-spec.md §2). The beacon is transmitted once at superframe start; it anchors time, names the cell modulation (CELL_DESC), and carries the SLOTMAP — a list binding each slot index to its 24-bit owner address — plus the contention-window pointer (packet-format.md §8.1). Each scheduled slot is owned by exactly one address and is collision-free by construction. The contention window is an epoch-hash-slotted ALOHA region for adverts, registration, and best-effort traffic; crucially, even the coordinator's own non-beacon traffic must go through it rather than the scheduled region, a rule learned when a post-beacon coordinator advert transmitted straight through a client's slot 0 and produced an every-Nth-frame loss signature (mac-spec.md §5.0).

4.2 CSMA discovery vs TDMA data

The contention plane never relies on carrier sense for correctness — hidden senders defeat it — and instead de-conflicts by hashing each sender into a micro-slot, slot = H(SRC_ADDR ‖ EPOCH ‖ attempt) mod C (mac-spec.md §5.2). The scheduled plane eliminates collisions outright. On the identical mutually-hidden hub, TDMA holds delivery at 99.8 / 99.6 / 99.2 / 96.5% for K = 10 / 20 / 50 / 100 senders, where CSMA collapses to 88 / 74 / 50 / 23% (tdma-data-plane-result.md). The hub records fewer than 5 collision decode failures under TDMA at K=50 versus thousands under CSMA (tdma-data-plane-result.md). The gap widens with K — precisely the elevated-relay regime where contention is helpless. The cost is correspondingly low: TDMA's only control overhead is the beacon (<15% of airtime, falling with K), against flooding's measured ~90% relay tax — roughly one-eighth the channel utilization for the same delivered traffic (tdma-data-plane-result.md). The figure tdma_vs_csma.png plots the two delivery curves.

4.3 The slot-fit invariant (#60)

A slot is not a free parameter: its width must be at least the time-on-air of the largest frame that may be granted into it, plus a guard (mac-spec.md §2.1). Sizing a slot to one fixed (telemetry) payload is the non-conformant trap that produced bug #60 in the field — a message frame overran a telemetry-sized slot, so the coordinator silently dropped both the message and its delivery-ACK while smaller telemetry fit and masked the failure. The as-built US915/SF9-BW125 fleet therefore pins SLOT_W = 900 ms, holding a 162-byte message frame (~874 ms ToA at SF9) plus guard, with ENV_MAX_BODY = 112 B as the body cap that keeps a message inside that slot, and a variable FRAME_T = 10 400 ms that also fits the beacon, member slots, a downlink, the ~750 ms identity advert, and a half-frame for a staggered backbone relay (mac-spec.md §2.1). The invariant is enforced at compile time, not by convention: firmware and Rust infra both carry static_assert/const _: () = assert!(…) guards that ToA(MAX_DATA_FRAME) ≤ SLOT_W, and a shared airtime function is unit-tested to cross-validate sim, firmware, and infra (mac-spec.md §2.1, bug #61). The fix is hardware-confirmed: a 162-byte message delivers in a 900 ms slot with two-check delivery (mac-spec.md §2.1, #60). Because demand polling rather than standing airtime drives capacity, a dedicated request slot decouples poll-naming (cheap, 3 B/name in the beacon) from reserved request airtime, lifting the latency breakpoint from K≈43 to K≈145 (deviation2-request-slot.md).

4.4 Clock and drift budget

The beacon is the time reference; GPS is an enhancement, never required. A client treats the start-of-beacon instant as superframe time 0 and re-anchors on every beacon, so the relevant drift is one inter-beacon interval, not free-running drift (mac-spec.md §3). The guard must satisfy G ≥ 2·(ppm_max·FRAME_T) + T_prop_max + T_rx_settle; with ±20 ppm crystals the drift term dominates and stays near 0.42 ms even at the as-built FRAME_T, so a ~3 ms guard suffices — the value that yielded 98–99% slot efficiency in simulation (mac-spec.md §3, tdma-data-plane-result.md). First real-hardware measurement on a live T114 client over 3.8 hours found a −12.6 ppm oscillator, giving a ~4-minute free-run budget before drift consumes the 3 ms guard; the ~4 ms beacon-cadence jitter is coordinator-side and absorbed by per-beacon re-anchoring rather than accumulating into slot misalignment (timing-budget-result.md). A node that misses BEACON_MISS_MAX beacons (default 3) declares the coordinator lost and enters fallback.

4.5 Hidden-node elimination and the fallback floor

Because scheduled senders key only off the beacon and never need to hear one another, mutual hiding becomes irrelevant under TDMA — collisions are removed by construction, not mitigated probabilistically (tdma-data-plane-result.md). When no coordinator is present, the network runs entirely on the contention plane using an epoch-hash schedule keyed by a coarse shared epoch (mac-spec.md §6). The epoch must rotate: static-hash slotting collapses below even CSMA at scale (0.203 vs 0.490 at K=50) because colliding ID pairs collide identically every round, and is forbidden (fallback-result.md, mac-spec.md §6). The rotating epoch-hash fallback is instead a strict upgrade over CSMA at every K (0.977 vs 0.868, 0.903 vs 0.720, 0.607 vs 0.490 at K = 10 / 20 / 50), needing no carrier sense (fallback-result.md). One frame structure spans both regimes — a superframe with an empty SLOTMAP is a pure-contention frame — so handoff is continuous, not a re-association (mac-spec.md §7). The result is a floor, not a cliff: coordinator present gives ~99% TDMA, coordinator lost gives epoch-hash well above CSMA, and the worst case is never worse than a Meshtastic-grade flood (fallback-result.md). The figure fallback_schemes.png shows all four disciplines on one hub.

5. Membership That Scales — PACT-lite

Section 4 made the data plane collision-free: a member transmits only in its beacon-assigned slot, so the hidden-node hub that defeats carrier sense is scheduled rather than left to contend. That guarantee, however, was bought with a primitive that does not scale. Loomwave's shipped MAC tied membership to a standing per-frame telemetry slot, and the frame can only hold so many. This section replaces that primitive with PACT-lite: table-row membership where idle costs nothing, a rotating telemetry pool whose airtime is fixed independent of how many members the cell carries, and a coordinator-polled demand mechanism that lets a texting node jump the rotation without ever contending.

The standing slot is the wrong primitive

The shipped frame is beacon + guard + NSLOTS telemetry slots + advert, with NSLOTS = 3 (membership.py). Admission is first-come: the first three clients to join take the standing slots and keep them; every client beyond the third is refused membership and gets no service at all — it is not in the frame. Run against the real Stone Mountain hub (K = 29 mutually-hidden clients, median link SNR −4.5 dB at the SF9 decode margin; stone-mountain-hidden-node-hub.md), the baseline serves 3 of 29 and starves the other 26 (hidden_hub_membership experiment, baseline-standing 3/29, telemetry delivery 10.9%). The captured hub is the canonical instance of the topology §4 is built around — one elevated relay hearing ~93% mutually-hidden terminals (stone-mountain-hidden-node-hub.md) — and the standing-slot primitive cannot even admit it. The reclaimable capacity here (§2.4) is the airtime idle members waste holding standing slots.

Table-row membership and the rotating telemetry pool

PACT-lite decouples being a member from owning a slot. A member is a table row, not a standing slot — an idle member costs zero airtime. The frame still carries only P telemetry slots (P = 3), but the coordinator rotates which rows own them, round-robin over the K-member table, so every member is served once per super-frame of R = ceil(K/P) frames (membership.py, deviation2-request-slot.md). The decisive property is that telemetry airtime is constant at P·TELEM_W for any K: the idle frame carries exactly three 35-byte records (TELEM_W ≈ 280 ms, total ≈ 0.84 s/frame; hidden_hub_membership experiment) whether the table holds 3 members or 150. All 29 members are served at zero idle airtime over a 3-member cell, telemetry delivery rises to ~94% (versus 10.9% for the baseline), at the cost only of telemetry latency growing with the rotation period — you wait your turn, but you are never refused (hidden_hub_membership experiment, rotating-pool 29/29).

Polled demand and on-demand grants

Telemetry rides the rotation; messages must not. The coordinator's beacon names a small cohort of rows each frame and polls them for queued demand. Because the coordinator names who may signal, mutually-hidden nodes never contend for the request — the demand signal is collision-free by construction — and the poll cursor sweeps the whole table within ceil(K/POLL_TOTAL) frames. A node that signals demand is granted a collision-free message slot the following frame via the on-demand grant path (membership.py, deviation2-request-slot.md). A TIM-style downlink bitmap of ceil(K/8) bytes in the beacon lets sleeping members learn they have pending downlink without standing airtime (membership.py _beacon_ms).

The verdict comes from the correlated-burst experiment — at t=0, 100% of the 29 nodes go active and each must deliver one alert within a 30 s deadline, with the lowest-SNR edge quartile broken out separately:

policy (100% burst) overall delivered edge-quartile delivered
polled-demand rotating (PACT-lite) 100.0% 100.0%
DOCSIS-style request-grant 44.8% 50.0%
IRSA / coded-slotted-ALOHA 13.8% 25.0%

(hidden_hub_membership experiment.) Polled demand delivers everyone, including the whole edge quartile, with bounded first-message latency.

Why slotted-ALOHA and IRSA were rejected

The two contention baselines fail for the same physical reason. A DOCSIS-style request-grant scheme sends the request in a slotted-ALOHA window; IRSA sends coded replicas. Both depend on resolving overlaps by capture, and on commodity LoRa there is no successive interference cancellation — capture is luck: a frame decodes only if it is ≥6 dB above the summed colliders and arrives first within the preamble, otherwise all overlapping frames are lost (membership.py, capture = luck). Under a correlated burst the weakest-SNR nodes are always the quiet term in the sum, so capture systematically starves the edge — exactly the mutually-hidden, decode-margin terminals the cell exists to serve. This is why the coordinator must schedule demand, not let it contend: at 93% mutual hiddenness the contention resolution carrier sense would provide is physically absent.

Deviation-2: a dedicated request slot

The shipped firmware carries a known penalty (deviation-2): a sweep-polled non-rostered member can only emit its message in its next rostered telemetry slot, so its first-message latency is rotation-bound — linear in K — crossing the 30 s alert deadline at a breakpoint of K ≈ 43 (deviation2-request-slot.md, analytic_floor_breakpoint_k), which sits inside the realistic hub range (captured K = 29; a busy hub could plausibly reach 50–90). The fix decouples poll-naming (cheap, 3 B/name) from reserved request airtime: name many members per frame (POLL_TOTAL = 16) but reserve only a small fixed request window (REQ_WINDOW = 2 micro-slots). The naive alternative — one demand micro-slot per polled member — was rejected because it couples naming to airtime and inflates every frame enough to cancel the slope gain (at 3 micro-slots the breakpoint is worse than as-built; deviation2-request-slot.md). The dedicated request slot lifts the latency breakpoint past K ≈ 145 (the as-built membership.py estimates ~188; the exact value is model-sensitive, but every estimate is far past the realistic hub), beyond any realistic hub, while keeping the rotation as a guaranteed fallback — a member is served by whichever fires first (deviation2-request-slot.md). The honest limit: under a pathological 100% burst the request window saturates and the rotation dominates, so the fix targets the realistic light-text regime (≈1 message / 10 min / client) where a lone texter's rotation wait is the cost that actually hurts.

The hardware confirmation is the same two-check delivery §4 established: a PACT-lite cell with rotating telemetry slots and on-demand message grants delivers a message end-to-end, observed firsthand on a separate radio.

Reliable messaging on top of the schedule

The membership machinery above admits the message; a transport layer makes its delivery confirmed and end-to-end confidential (reliable-messaging.md). User text rides inside a DATA frame as an inner, endpoint-to-endpoint-sealed ENV_TEXT envelope (PORT_MSG = 0x10), so the coordinator routes an opaque blob it cannot read — a second ChaChaPoly seal keyed X25519(src_x, dst_x) under the outer client↔coordinator transport seal, with the endpoint keys already on the wire in signed identity adverts (no handshake). Reliability is an ARQ loop: a sender sets an ack-request flag and retains the envelope; the recipient auto-emits a signed-path delivery receipt carrying the acked msg_id; the sender retransmits on a frame-paced timer (45 s, up to 4 tries) and delivery is made idempotent by msg_id dedupe, so a lost receipt or a store-and-forward re-delivery never double-delivers (reliable-messaging.md §4). The one load-bearing timing rule is that a scheduled client opens only a short post-slot downlink RX window (in-cell beacon + 1428 ms), so the coordinator MUST transmit every downlink there — firing it at the general control instant instead silently dropped delivery, the "one check at home" bug — with the v2 beacon's TIM bitmap (§8.1.2 of packet-format.md) telling a member to keep that window open when a downlink is pending (reliable-messaging.md §5). The single-frame text ceiling is ENV_MAX_BODY = 112 B (lowered from 206 for slot-fit, bug #60); longer text will fragment (deferred). This is the Stage-4 messaging deliverable, HW-validated as the two-check end-to-end path above.

6. PHY Agility — Spreading Factor, Frequency, Rendezvous

PHY agility reclaims a third kind of wasted airtime (§2.4) — slots run slower than the link needs, channels louder than they have to be, and idle scanning. The coordinator is the one node with the global view to reclaim each: it alone measures every member's link budget (via LINK_REPORTS), surveys the band, and announces the result in a beacon every member already decodes. PHY agility is that authority applied to the (SF, BW, freq) triple. All of it rides one generalized airtime law — lora_toa_ms(sf, bw, pl) over SF7–SF12 / BW125–250, unit-tested identically across sim, firmware, and infra (CR4:5, 16-symbol preamble, LDRO auto), reproducing the firmware's pinned constants to ±1 ms (SF9/BW125 162 B = 873 vs 874 ms) (adaptive-modulation-design.md). Slot widths are a deterministic function of (SF, BW) through this law, so a member computes its own slot width rather than being told it.

Per-cell adaptive SF

The cell runs one SF: the slowest any reachable member needs, the worst-margin terminal (adaptive-sf-selection-result.md). This buys reach, not raw throughput — at N=50 per-cell-auto delivers 0.63 pps versus static-SF9's 0.83, but serves 100% of members where static-SF9 drops the ~27% of stragglers below SF9's floor and reaches only ~73% (adaptive-sf-selection-result.md). It is also nearly free and nearly inert: zero incremental wire cost (the CELL_DESC SF field already exists), and essentially thrash-free — cell-flip rate is ≈0 across N≥50 because the slowest member's identity is stable, converting N independent fading processes into one slow worst-member process (adaptive-sf-selection-result.md). Asymmetric hysteresis (sticky on speed-up, prompt on fall-back so a dropping link is never lost) keeps reach intact even at 0 dB band; a 2.5–3 dB band sits at the stability/capacity knee for the per-link loop below (adaptive-sf-selection-result.md).

Per-cell leaves capacity on the table: one straggler pins every fast near-member to slow airtime. Per-link gives each member its own SF — close members run short SF7 slots, the straggler alone pays SF11. The mechanism: the beacon TXes at the slowest member's SF (so all can decode the schedule); the roster is SF-sorted with each entry carrying a per-member SF; the coordinator retunes its modem per SF-block to RX each slot; and slot offsets become a prefix-sum of the preceding (per-SF) widths rather than k·SLOT_W (per-link-mixed-sf-design.md). Idealized, this roughly doubles per-cell capacity (×1.70 at N=10 → ×2.00 at N=200) (adaptive-sf-selection-result.md). Charging the real frame costs — the slow beacon (+1 B/grant), guards — the net gain survives: ×1.62 at N=5 → ×1.98 at N=200, the slow beacon eroding the prize only at small N and amortizing as 1/N (per-link-sf-result.md). But the win is entirely a spread phenomenon: with no SF spread per-link is a ×0.99 net loss (a larger beacon for no data gain), jumping to ×2.1 the instant the cell spans ≥2 SF steps (per-link-sf-result.md). Hence the gate, two cheap checks on inputs the coordinator already holds: enable per-link only at N ≥ 5 scheduled members AND SF spread ≥ 2 steps; otherwise per-cell-auto, the safe default (per-link-sf-result.md, per-link-mixed-sf-design.md). The prize costs ~100× the per-cell SF-flip churn, which the per-link beacon machinery must absorb (adaptive-sf-selection-result.md). On the bench, the mechanism is HW-verified: a coordinator at SF9 simultaneously heard a Heltec V3 member at SF7 and a Heltec V4 at SF9 — and because LoRa SFs are quasi-orthogonal (an SF9 RX cannot demodulate SF7), hearing both proves the per-SF-block RX retune end-to-end, with the SF7 slot genuinely narrower (446 ms vs ~640 ms uniform) (per-link-mixed-sf-design.md).

DFS and the announced-change FSM

A fixed coordinator that lands on a loud channel bleeds delivery; the North-Georgia firehose (753,979 rows) confirms the band is non-uniform — the loud Meshtastic slot runs ≈3.4× the airtime of the quiet one (dfs-result.md). DFS scans and picks the quietest candidate. The single-cell win is conditional: only +0.03 delivery on an already-clear band, rising to +0.23 at ~17% occupancy and +0.40–0.46 at ~33% (fixed falls 0.97→0.54 as the channel loads; DFS holds ≈0.99) (dfs-result.md). The decisive case is multi-coordinator co-channel: two coordinators sharing a channel (the live 911.5 collision) deliver only 0.36–0.46, losing >half their traffic; DFS frequency separation restores ≈0.99 — a +0.52 to +0.63 delivery gain (dfs-result.md). Against an adversary the same lever answers a jammer that reassignment cannot: a continuous jammer collapses a static cell to ~0.10 delivery, while a frequency-agile coordinator that watches per-frame decode health detects the collapse and hops to a clean channel, recovering to ~0.95 (jammer-freq-agility-result.md).

Changing PHY while members are joined must not strand them. One announced-grace-period FSM serves both SF (#66) and channel (#65): on a target change the coordinator enters ANNOUNCING, sets BCN_FLAG_PHY_CHANGE, and appends a 4-byte block {target_sf_bw, target_freq_offset, frames_until_switch} counting down over K≈4 beacons (~8 s of notice); members record the switch epoch, continue at the old PHY, and pre-tune at the boundary — an O(1) direct follow, never a re-scan, with the foundation's blind grid scan as the safety net only on a missed switch (announced-phy-change-design.md). Because per-cell SF changes are rare (≈0 thrash), the cost is depth-per-event, not frequency, which is exactly what the announcement makes cheap.

Guided roam vs blind-scan rendezvous

Discovery is what makes agility look expensive. Firmware discovers a coordinator on an unknown PHY by sweeping a G = N_channels × N_SFs grid, dwelling D = 12 s per cell; mean acquisition is (G−1)/2·D, linear in grid size — ~6 s per added cell, reaching 49 s mean (3×3) and 139 s mean / 266 s p95 (full 4×6) (discovery-latency-result.md). This matches the bench symptom (~45 s acquisition, "2nd dwell of the right channel"). The cost is the missing prior: blind scan must find the cell, the beacon plan names it. Guided roam supplies the map — a signed ADV_NEIGHBORS advert (n × {freq_offset:u16, sf:u8}) the coordinator broadcasts; on cell loss a client retunes directly to a cached neighbour PHY and acquires in ~1 beacon period before falling back to the grid (guided-roam-design.md). Acquisition becomes constant at B/2 ≈ 1.1 s regardless of grid — a 42× win at 3×3 and 118× at 4×6 — turning the ~49–139 s blind-scan handoff into ~1.1 s, and shrinking the scan-while-camped data gap by the same D/B ≈ 5.7× per probed cell (discovery-latency-result.md, guided-roam-design.md). This argues for shipping the guided plan before enabling any multi-cell agility, so agility never pays the blind-scan tax. The announced-change block, the DFS target, and the neighbour hint are deliberately one wire primitive and one pre-tune path (announced-phy-change-design.md).

Hardware validation. The SF-agility mechanisms are confirmed on real radios (stage5-hw-agility-results.md). A two-member cell booted at SF9 auto-selected SF7 from the members' measured +12 dB SNR — clearing the SF7 decode margin by >13 dB — and both members followed without re-scanning, serving 78 telemetry frames at the selected SF7 (adaptive_sf_hw.png). In per-link mode a coordinator at SF9 received a SF7 and a SF9 member concurrently (n=38/42) by retuning per SF-block — impossible at a single SF, since the two are quasi-orthogonal, so the concurrent reception is itself the proof — with the telemetry region measured at 446 ms against 640 ms for a uniform SF9 cell (−30%; perlink_mixed_sf_hw.png). Frequency agility is likewise measured: a 22 dBm CW jammer spiked the serving channel's monitored RSSI by ~40 dB and the autonomous dual-radio DFS hopped the cell 915→917 (the quietest candidate), the member following (dfs_off_jam_hw.png); and an announced FSM change moved a running cell 915→914 with the member pre-tuning on the beacon and delivering across the switch with no re-scan gap (channel_change_hw.png) (#65; stage5-hw-agility-results.md).

7. Routing and the Multi-Cell Backbone

Routing applies the §1.2 thesis to topology: an elevated INFRA node cannot let its mutually-hidden terminals route by flooding — the broadcast storm is O(N²) and saturates a LongFast channel at a handful of nodes — but it can aggregate them, because it alone holds the global view of its cell. Routing is therefore two-tier and role-asymmetric: clients carry no global topology, and link-state is confined to the few INFRA nodes (routing-spec.md §1).

7.1 Why flooded control collapses, and what replaces it

Every flooded control scheme is governed by one equation: a control packet that floods costs ~N transmissions, so with N originators on period P the load is L ≈ N²·ToA/P (routing-options.md §0). Mesh-wide link-state inherits this directly. The Stage-2 measurement is unambiguous: offered control load for flooded link-state is 259% of channel at just N=20 and 1441% at N=100 — the channel asked to carry 14× its capacity by control alone, the O(N²) signature made concrete (routing-control-result.md; figure routing_overhead.png).

The escape is structural, not parametric. Client→coordinator registration is scheduled unicast, not flooded — one transmission, not N — so edge control is N·ToA/P_reg, linear in N (routing-options.md §6; routing-spec.md §3.1). Forwarding uses a distance-vector next-hop table: a client holds only in-cell direct entries plus a default to its coordinator; the coordinator holds the next coordinator toward any remote cell (routing-spec.md §3.2). The same simulation shows INFRA-aggregated DV at 61.6% offered / 31.5% median-node at N=100 — ~23× less control airtime than flooded link-state, under the 40% red line, and the gap widens with N (routing-control-result.md). Registration cadence is the tuning knob the analysis predicted: slowing it from 60 s to 180 s drops the network onto its ~22% offered / ~12% median budget, because slowing an O(N) term scales it proportionally (routing-control-result.md). Cold-start and coordinator-absent operation fall back to a rate-capped flood discovery plane (ANNOUNCE_CAP ≤ 2%) and AODV-style reactive routing, so the network is never dependent on coordination, only optimized by it (routing-spec.md §6).

7.2 The backbone tier and the half-frame stagger

Link-state survives only among the few coordinators. Because a cell is aggregated to a single vertex carrying a member count and prefix — not N vertices — the graph scales with the number of coordinators, not nodes, and the real high-site backbone is 8 vertices in one connected component spanning ~180 km in 2–3 elevated hops (routing-spec.md §4; high-site-backbone.md). LSAs are signed and flooded only across the backbone, never into cells (routing-spec.md §4).

The mechanism that lets a single-radio coordinator carry both a client cell and a backbone on one channel is the half-frame stagger: role A anchors its beacon at t₀; role B anchors at t₀ + FRAME_T/2, phase-locked to A, so each owns one half of the superframe (backbone-b1-design.md; backbone-chain-bottleneck.md §1). Roles are assigned by radio observation alone — scan-before-claim — not configuration. The B1 hardware milestone validated this end to end: a bench role-B coordinator heard a tower role-A beacon during scan and staggered by half a frame on the same channel (backbone-b1-results.md). The path was bidirectionally adjacent with ~28 dB of RSSI asymmetry (tower hears bench −62 dBm; bench hears tower −34 dBm), the two exchanged signed LSAs, and a T114 client's position reports traversed client → cell coordinator → backbone → second coordinator with independent AEAD re-sealing on each hop — 46 cross-cell frames decoded at the tower, populating the cross-cell table across three independent crypto stacks with zero verification failures (backbone-b1-results.md). The half-frame stagger thus delivers the minimum viable backbone with no second radio.

7.3 The long-chain throughput bottleneck

The stagger's cost is its ceiling. A single-radio, half-duplex relay must pack its FRAME_T/2 half with beacon, telemetry slots, downlink, identity advert, and forward grants; the slot-fit budget admits exactly one message-sized forward grant per half-frame per direction (GRANT_MAX=1 fits 4162 ms into the 4400 ms half; GRANT_MAX=2 overruns. A relay coordinator runs a fixed 8800 ms FRAME_T — distinct from, and deliberately shorter than, the 10 400 ms standalone-cell superframe of §4.3: the half-frame stagger needs a stable period, and a tighter one cuts cross-cell latency, #62) (backbone-chain-bottleneck.md §1). That pins a per-relay cap of 1 msg / FRAME_T = 6.8 msg/min per direction, independent of chain length, and the middle relay of a chain must pass all traffic through its cut. An 8-cell all-cross-chain chain therefore saturates near 0.6 msg/min/user: delivery ratio falls off a cliff (1.00 → 0.70 → 0.39 → 0.21) as offered load crosses the knee, with p95 latency blowing into thousands of seconds, while delivered throughput plateaus at ~30–34 msg/min regardless of offered load — a capacity floor, not a tuning artifact (backbone-chain-bottleneck.md §3). The airtime ceiling is fixed, so the only reclaimable capacity is structural: moving cross-cell relay onto a dedicated SF7/BW250 second channel raises the forward cap ~26× and holds delivery at 1.00 with flat ~7 s p95 across the entire range that collapsed the in-band backbone (backbone-chain-bottleneck.md §4). A longer FRAME_T is a partial in-band fallback, clearing the knee at ~9× the latency floor (backbone-chain-bottleneck.md §5). The conclusion for deployment is concrete: do not run long single-radio chains for cross-chain traffic; prefer short chains/trees and the dedicated relay radio coordinators already carry.

8. Security

Loomwave's security layer inherits the same governing constraint as its MAC: the airtime ceiling is fixed, so every overhead byte is spent against payload that could otherwise carry user traffic. The design therefore treats the security scheme as an airtime decision first and a cryptographic one second (security-options.md §1), and it leans on the one structural advantage the coordinated topology already provides — a node that schedules a cell can also police it.

8.1 Self-certifying identities and TOFU

Every node holds a long-term Ed25519 signing key with a derived X25519 agreement key; its 24-bit on-air address is the truncated hash of that public key, ADDR24 = TRUNC_24(H(pubkey)) (security-spec.md §2). The address is thus self-certifying: a receiver MUST verify that an advertised key hashes to the claimed ADDR24 before caching it, so an attacker cannot bind an arbitrary key to a target's address. Keys are generated offline on-device with no enrollment authority (goal #4); trust is key-continuity (TOFU) — a peer's first-seen key binds to its address, with optional out-of-band fingerprint verification at the application layer (security-spec.md §2). Public keys are distributed in signed, rate-capped identity ADVERTs (packet-format.md §8.3).

8.2 The byte budget — overhead that fits the LoRa frame

The brief's worked example sizes a naive scheme at a 32-byte MAC plus a 24-byte transmitted nonce, which on a 255-byte LoRa frame consumes 56 bytes and leaves 199 bytes of payload (security-options.md §3; 255 − 56 = 199). Loomwave reclaims almost all of that. The 96-bit AEAD nonce is derived, never transmitted — computed at both ends as TRUNC_12(H(SRC ‖ SEQ ‖ EPOCH ‖ DIR)) from fields already on the wire or in shared state (packet-format.md §7), saving the full 24 bytes. The authentication tag is a standard AEAD tag truncated to 8 bytes (2⁻⁶⁴ forgery probability per attempt, beyond any LoRa-rate online attack, where the channel admits at most a handful of guesses per second) rather than 32 (security-spec.md §5; packet-format.md §11). Static-static X25519 key agreement adds zero handshake bytes on the wire (security-spec.md §3). The result is a DATA frame whose entire security cost is 8 B tag + 0 B nonce + 0 B handshake = 8 B, inside a 16-byte header-plus-tag total, leaving 239 B of frame payload (238 B after the 1-byte application PORT) — no regression against the budget asserted at Checkpoint B (security-spec.md §9; packet-format.md §10). In airtime terms a 20-byte secured telemetry message costs ~1.31× the unsecured floor, against 2.04× for the brief's 56-byte scheme and 2.55× for a Reticulum-style 83-byte token (security-options.md §4, §6) — preserving roughly 1.5–2× more network message capacity. The trade is honestly stated: static-static ECDH gives no forward secrecy, with epoch rotation limiting the exposure window and an optional ephemeral ratchet reserved for high-value channels (security-spec.md §3).

8.3 Replay protection without a clock

Replay is prevented with no time synchronization, satisfying goal #4. Ordering is carried entirely by the monotonic (EPOCH, SEQ) pair: a receiver keeps, per source, the highest accepted SEQ in the current epoch and a sliding bitmap window of width W = 64, accepting a frame only if its SEQ is ahead of the window or within it and not already seen, and dropping it otherwise (security-spec.md §7). Because SEQ is a single byte (wrapping at 256) and feeds both the nonce and the per-epoch subkey, the nonce-uniqueness invariant is maintained by a normative rule: a sender MUST advance the 32-bit EPOCH before SEQ would wrap, so the real (key, nonce) space is (EPOCH, SEQ, DIR) and monotonic EPOCH guarantees no repeat (security-spec.md §5.1). Reboots are handled by persisting the last-used epoch and advancing strictly past it before transmitting (security-spec.md §8).

8.4 Broadcast versus unicast keying

Two key regimes coexist. Unicast pairs derive a confidential, attributable key from implicit ECDH; a tag that verifies under the address-bound pair key is cryptographic origin proof. Broadcast (channel) frames use a pre-shared symmetric K_chan distributed out of band (the Meshtastic model), which proves channel membership but not individual origin — so frames that need provable origin (BEACON, ADVERT, coordinator-claim) carry a full 64-byte Ed25519 signature, not merely an AEAD tag (security-spec.md §4, §6). The signature is full-length by necessity: an erratum corrected an earlier truncated-signature design, since Ed25519 verification requires the complete (R,S) pair and cannot be verified from a truncated value, unlike a MAC (security-spec.md §6). The 64-byte cost lands only on rate-capped control frames and never on DATA; measured control overhead stayed under 15% (security-spec.md §6; sim-result-v3/v5), and a client need only verify the coordinator's ADVERT and first BEACON at acquisition, re-verifying periodically rather than per-beacon given the ~230 ms cost on an unaccelerated Cortex-M4 (security-spec.md §6).

8.5 The rogue-coordinator boundary and abuse mitigation

The most important limitation, stated plainly: Loomwave authenticates identity but does not authorize authority. There is no PKI, so possessing a valid identity does not entitle a node to any role, and by default any node can act as a coordinator (security-spec.md §1.1; F8). An adversary can generate a free keypair, advertise a coordinator-claim with a plausible score, be TOFU-trusted as the first beacon a new client hears, and thereby decrypt what that client seals to the advertised key. Election corroboration raises the cost of winning among honest nodes but is itself Sybil-gameable, since identities are free (ADDR24 is a free keygen) and Sybils from one attacker share one physical radio (security-spec.md §1.2; F13). The accepted v1 posture is an open mesh that trusts the RF environment; the partial defense is airtime-based admission — metering slot grants against airtime heard from a source's radio rather than per identity — and an optional, default-off channel-endorsement key for provisioned networks that must reject unendorsed coordinators (security-spec.md §1.1–1.2). These bounds are explicit, not assumed.

The squatter case shows why coordination is the lever rather than the liability. In simulation, a single misbehaving insider that transmits in slots it does not own degrades a 20-client collision-free cell as (K−S)/K: squatting 1 of 20 slots drops delivery to 0.947, 10 slots to 0.503, and squatting all 20 collapses the cell to 0.000 (adversarial-squatter-result.md; adversarial_squatter.png). The collision-free guarantee is binary — it holds under compliance and breaks under defection — but unlike a flat flood mesh, Loomwave has an authority that can detect and recover: because the coordinator knows the full schedule, a scheduled slot failing consecutive frames is unambiguous interference. A slot-health monitor that reassigns victims to clean spare slots after D=3 failures restores a cell squatted on 10 of 20 slots from 0.583 back to 1.000 within the first minute, with ~8 one-time moves (adversarial-squatter-result.md; adversarial_recovery.png). The same enforcement vantage grounds the abuse ladder for the "selfish-flooder" / QSL-hunter class — traffic whose airtime consumed far exceeds value delivered to a local recipient: the coordinator observes airtime share and reach-without-recipient and applies a graduated, decaying, operator-overridable response (cap wide propagation via a locality filter, rate-limit, deprioritize, deny admission, block), with shared reputation kept signed, corroborated, and optional precisely because a blocklist is itself an attack surface (abuse-mitigation-note.md §3–§5). A flat flood mesh has no authority, no schedule, and no detection point, so it can do none of this.

9. Evaluation — Simulation

Before any firmware, every architectural claim in §§3–8 was tested in a SimPy discrete-event simulation. Stated as a falsifiable hypothesis, the §1.2 thesis predicts that scheduling the mutually-hidden terminals an elevated node hears outperforms letting them contend, across every axis. This section reports the measurements that confirm that hypothesis across the MAC, routing, membership, fallback, capacity, and agility axes, then closes with the three reference scenario classes.

9.1 Methodology

The simulator is a SimPy event model with a LoRa PHY layer whose airtime is cross-checked against the Semtech/Meshtastic time-on-air formula (65 passing tests, design-decision.md). The PHY models per-link path loss, an RSSI/SNR decode threshold per spreading factor, and collisions as time-overlap at a receiver, with the LoRa capture effect (a ~6 dB-stronger, earlier frame decodes through an overlap). The three node roles (CLIENT, REPEATER, INFRA) carry their behavioural asymmetries, and MAC and routing are pluggable so each comparison runs the same topology and traffic under two schemes. Per the project's comparison-methodology rule, every reported delta is a relative comparison on identical topology and traffic, the regime the North-Georgia validation gate licensed; the propagation model's mean channel-util now matches the real network (7.4% vs 7.0%, propagation-calibration-result.md) with a residual upper tail confirmed structural, and that residual cancels under relative comparison.

9.2 Consolidated results

Axis Claim under test Measured result Source
MAC (v3) TDMA removes hidden-node loss Delivery 99.8 / 99.6 / 99.2 / 96.5% vs CSMA 88.5 / 73.8 / 49.9 / 23.4% at K = 10/20/50/100; <5 collisions vs thousands; 92.5–98.6% efficient tdma-data-plane-result.md
Capacity (v4) Adaptive modulation + spatial reuse beat the single-domain ceiling ×16 per-node aggregate at 50 nodes (2.45 → 39.30 msg/min); 500-node ceiling 0.247 → 8.29 msg/min (×34); reuse exact to 5× with 0 cross-cell failures by 12 km adaptive-modulation-result.md
Routing (v5) INFRA-aggregated DV is O(N), flooded link-state O(N²) Flooded link-state 259% → 1441% offered at N = 20 → 100; INFRA-DV 13.1% → 61.6% (~23× cheaper), tunable to 21.6% / 11.7% median at 180 s cadence routing-control-result.md
Fallback (v6) Graceful coordinator-free degradation Epoch-hash > CSMA at every K (0.977/0.903/0.607 vs 0.868/0.720/0.490 at K = 10/20/50); static-hash collapses to 0.203 at K=50; TDMA ceiling ~0.99 fallback-result.md
Membership (channel) Membership-scoped multicast beats whole-mesh flood Scoped ~10× cheaper than flood even for a universal public channel; 40–49× for a 20%-cell topic; full delivery at 10.9 s vs 109 s full flood on 32 cells channel-traffic-result.md
Scope (real data) Most flooded airtime is over-propagation 94.3% of NG receptions multi-hop, median origin distance 69 km; 99.4% machine/broadcast; capping machine traffic at ≤2 hops recovers ≈29% of relay airtime (≈21% of channel) scope-waste-result.md

The capacity result is the architecture-only headline: the per-node ×16–34 gain (adaptive-modulation-result.md) requires a coordinator acting as modulation authority, a role no incumbent flood configuration can express. The single LongFast domain sits exactly at the raw channel ceiling (2.04 ≈ 2.1 pkt/s, adaptive-modulation-result.md); splitting the nodes into spatially-separated MediumFast cells composes ~×3.4 from faster SF with ~×5 from five-cell reuse. Reuse is robust because capture, not separation, does the work — a client sits ~17 dB above any cross-cell interferer, so aggregate throughput holds at exactly 5× a single cell from 3 km to 15 km, with cross-cell decode failures reaching 0 by ~12 km (adaptive-modulation-result.md). Figures: tdma_vs_csma.png, capacity_spatial_reuse.png, routing_overhead.png, fallback_schemes.png, scope_waste.png.

The reclaimable capacity (§2.4) splits along the design's axes: the MAC axis reclaims the collision waste (TDMA is collision-free by construction); the scope and membership axes reclaim the propagation waste — the real North-Georgia data shows 71% of channel airtime is relaying, most of it machine traffic flooded a median ~65–80 km past any local relevance (scope-waste-result.md).

9.3 Scenario suite

The three reference deployments were each run under flooding+CSMA and hybrid TDMA on identical topology and traffic — every client offers one telemetry packet/min to its nearest INFRA — measuring delivery, latency CDF, and channel utilisation (scenario_latency_cdf.png).

Scenario (nodes / INFRA) MAC delivery p50 p90 channel util
sparse-rural (10 / 2) flood 1.000 0.48 s 2.44 s 58.5%
tdma 0.990 5.43 s 7.83 s 7.9%
dense-event (100 / 4) flood 0.346 4.14 s 7.62 s 99.2%
tdma 0.961 16.2 s 27.0 s 24.7%
neighborhood (50 / 6) flood 0.877 2.43 s 6.56 s 99.2%
tdma 0.980 7.32 s 11.9 s 12.7%

All figures from scenario-suite-result.md. At scale, coordination wins decisively: dense-event flooding collapses to 0.346 with the channel pinned at 99.2%, while TDMA holds 0.961 at 24.7% utilisation; neighborhood is 0.980 vs 0.877 at one-eighth the channel. Below the collapse knee, flooding is the right tool — sparse-rural flooding delivers everything at sub-second latency and TDMA merely matches delivery (0.990) while adding latency for no benefit, which is exactly the design's "never worse than flood, strictly better where a coordinator helps" stance. TDMA's latency is the single-domain frame cost (dense-event's ~14 s 100-slot MediumFast frame drives the ~16 s p50), and it is precisely what the §9.2 spatial-reuse lever relieves: splitting 100 nodes into four cells shortens the frame ~4× (scenario-suite-result.md). The quiet headline is channel efficiency — TDMA carries the same load at 8–25% where flooding saturates at 58–99%, and the airtime it does not burn is headroom for more nodes, higher rates, or lower power.

10. Evaluation — Hardware and Real-World Validation

The thesis of §1–§2 is testable in two registers: the simulation must be grounded in measured reality rather than literature assumption, and every protocol mechanism must execute on real radios under an adversarial observation rule — never claim a node transmitted or received until a separate receiver logs the actual frame. This section reports both: the measured-topology and measured-propagation evidence that anchors the sim, and the hardware bring-up matrix that executed the protocol on real silicon, ending with field drive tests.

10.1 The hub is measured, not assumed

The topology of §1.2 is not a thought experiment. Subscribing to Nano Newt 0b3b, a Portduino router gatewaying the local mesh from atop Stone Mountain — one of the highest sites in metro Atlanta — and filtering to direct receptions (originator heard firsthand, hop_start == hop_limit) yields a captured elevated-relay hidden-node hub. Over a ~16 h sample the hub heard 29 nodes directly (24 positioned), with 275 direct receptions, ~93% of node-pairs mutually hidden, at a median link SNR of −4.5 dB — right at the decode margin (stone-mountain-hidden-node-hub.md). The heard nodes spread over a median pairwise separation of 35 km (preliminary 1 h sample: median 22.9 km from the dome, max 123 km), far beyond ground-to-ground LoRa range, so they are physically invisible to one another (stone-mountain-hidden-node-hub.md, stone_mountain_hub.png). At 93% mutual hiddenness, carrier sense is physically dead at the hub: a node cannot sense a carrier it cannot hear, so an elevated INFRA node that hears these terminals cannot let them contend — but it can schedule them, because it alone holds the global view. The capture confirms the counter-intuitive corollary: the better the relay's vantage, the more mutually-hidden transmitters it concentrates, which is precisely why the elevated node must be the slot authority and not a passive repeater.

10.2 The simulation is calibrated to measured propagation

The validation gate (§8.5) refuses to trust any prediction until the Meshtastic-equivalent baseline reproduces the real network. Running flooding+CSMA over the real 47-node metro Atlanta cluster (largest 5 km-connected component of the 244-node view.mtnme.sh census) with the real per-node offered-load distribution (median 0.274 pkt/h, mean 1.167, heavy tail to 119), the baseline landed in the same regime as the self-reported real network — within ~1.5× on channel utilization — neither collapsing nor trivially idle (north-georgia-validation.md, north-georgia-data-characterization.md, ng_validation_gate.png). The propagation half of the model was then calibrated against real measurements rather than assumed: malla.mtnme.sh and malla.gamesh.net expose public per-gateway snr/rssi rows; filtering to direct hop-0 receptions only (≈20k → 1,125 valid originator→gateway links) and fitting rssi = A + B·log10(d) gave a path-loss exponent of n ≈ 2.50 combined (2.52 and 3.36 across the two independent gateway sets), bracketing the literature value of 2.7 (propagation-calibration-result.md, propagation_calibration.png). With the data-derived exponent the gate's mean channel utilization matched the real network (7.4% vs 7.0%), up from a ~1.5× under-prediction (propagation-calibration-result.md). The residual narrow distribution is, with propagation now ruled in, confirmed structural — co-channel traffic, gateway concentration, and a 2.3× diurnal swing, none of which an isolated steady-state single-cluster sim contains. This licenses the relative A/B comparisons that drive every design decision in §3–§7.

10.3 Hardware bring-up matrix — the protocol on real radios

Stage 4 executed the protocol on a bench of real radios (Pi 5 + Nebra Duo + Heltec T114, all at −9 dBm chip power), each mechanism confirmed on a separate receiver (stage4-results.md). The GPS-free, beacon-anchored timing thesis held on hardware: 30/30 beacons with arrival jitter of −1/0/+1 ms against the 3 ms guard. Membership ran end-to-end (SCANNING→REGISTERING→SCHEDULED, REGISTER → SLOTMAP grant → 17/17 scheduled DATA at ±1 ms), with a killed client's slot reclaimed on membership expiry. A T114 client built from the spec alone decoded beacons 8/8 — spec-as-contract demonstrated across an independent wire-format implementation. Security interoperated across two independent crypto stacks with 0 rejects (X25519+HKDF+ChaCha20-Poly1305 plus Ed25519 TOFU; sealed DATA 28/28). Multi-client sharing ran with replay rejection live (66 sealed DATA across two slots), and dual-cell ran one identity across two radios and two cells concurrently — SF9 reach plus SF7/BW250 capacity, exercising node-role and per-radio behavior (stage4-results.md). Implementation also surfaced the airtime-sizing erratum (a 670 ms advert overflowing a 4-slot superframe; distinct from the ~750 ms as-built advert budget of §4.3) and the rule that the coordinator must obey its own schedule — both folded back into the spec.

Beyond Stage 4, the PHY-agility and multi-cell mechanisms of §6–§7 were brought up on hardware in subsequent bench/tower sessions and are recorded in stage5-hw-agility-results.md, each observed on a separate receiver. Per-cell adaptive SF auto-selected SF9→SF7 from the members' measured SNR and both followed, serving 78 frames at SF7 (adaptive_sf_hw.png); per-link mixed-SF had a coordinator at SF9 receive a SF7 and a SF9 member concurrently (n=38/42) via per-block retune, telemetry region 446 ms vs 640 ms uniform (perlink_mixed_sf_hw.png); the backbone role-B stagger formed over real separation (bench role-B +4400 ms on a tower role-A beacon at ~80 ft, adjacency −62/−76 dBm, LSA exchange, #60); and frequency agility is measured: autonomous DFS hopped a cell off a live jammer (915→917, dfs_off_jam_hw.png) and an announced 915→914 change the member followed with no re-scan gap (channel_change_hw.png) (#65). The agility/backbone claims of §6–§7 thus rest on measured hardware as well as the reproducible simulation.

10.4 Field drive tests

The first fielded cell put the tower coordinator (CM3 + Nebra Mesh HAT, ~30 dBm EIRP, 60 ft AGL) against a T114 in a moving truck over a 34-min suburban loop. The protocol survived contact: 149 sealed position reports delivered, 5 joins including 4 live dead-zone recoveries, ~95% in-session scheduled delivery, max uplink 612 m, weakest decoded uplink −113 dBm (drive-test-1-result.md, drive_coverage.png). The two-scale result is the honest one — where link exists the TDMA cell delivers near-perfectly, but only ~9% of sampled route points were inside coverage (drive_holes.png): link existence, not the protocol, is the scarce resource here, an antenna/terrain limit (suspect truck coax, mid-canopy antenna, dense June foliage, the under-tower null) consistent with the propagation cloud sitting in a receive-system clutter offset rather than a model failure. Where link exists, these runs confirm on real hardware exactly what the simulator predicts — the coordinated schedule delivers.

Loomwave is best understood as a re-composition of ideas already present in the LoRa-mesh ecosystem, plus one structural addition — a coordinator — that the airtime math and simulation show is necessary to break the flood ceiling (meshtastic-meshcore-differentiation.md). It is not "Meshtastic but better"; it is a different bet, and the honest framing is a tradeoff, not a verdict (meshtastic-meshcore-differentiation.md).

Meshtastic is the primary baseline and the reason this field exists: managed flooding is the right call for a small, casual, zero-infrastructure network that "just works," and it has by far the largest community and real deployments — the North Georgia network we calibrate against is Meshtastic (meshtastic-meshcore-differentiation.md). Its limits are inherent to peer symmetry: every data packet floods, saturating a LongFast channel at roughly 5–11 nodes, and CSMA/CAD cannot sense a hidden transmitter, so hidden-node loss is unaddressed (meshtastic-meshcore-differentiation.md). MeshCore advances the state of the art on the two axes Meshtastic is weakest — it treats airtime as a budget and signs identity — and its flood-once-then-source-route model is genuinely more efficient at steady state (meshtastic-meshcore-differentiation.md). Its real census is structurally closer to Loomwave's INFRA model: far more repeater/infrastructure-heavy than Meshtastic's ~8% relay ratio, on narrow 62.5 kHz channels (meshcore-network-characterization.md). Loomwave keeps both projects' contention behavior as its discovery plane and borrows MeshCore's airtime budget and signed adverts, but trades MeshCore's stateless source path for a small next-hop table to get O(N) control and mobility tolerance (meshtastic-meshcore-differentiation.md). Reticulum is the most directly relevant prior art for the routing layer: Loomwave adopts its self-certifying addresses, signed self-validating announces, distance-vector next-hop table, and — most importantly — the ANNOUNCE_CAP discipline that gives control traffic a hard, prioritized fraction of channel; it leaves behind Reticulum's 500-byte MTU and 16-byte addresses, which are too heavy for a 255-byte LoRa frame (reticulum-routing-analysis.md). Against the two scheduled-infrastructure traditions named in this paper's scope — LoRaWAN (star-of-stars, gateway-centric, no terminal-to-terminal mesh) and DVB-RCS2 (scheduled DAMA satellite return links) — Loomwave occupies a deliberate middle: an optional elevated coordinator that schedules a mesh, degrading to pure peer flooding when absent, rather than a mandatory centralized base station.

11.2 Limitations

The honest other side of differentiation must be stated plainly (meshtastic-meshcore-differentiation.md). First, simplicity and statelessness are given up: managed flooding needs no coordinator, no slot math, and MeshCore's source routing keeps zero per-relay state — Loomwave's TDMA plus adaptive modulation plus distance-vector is materially more firmware to get right, and it walks away from a large, working community by declining wire compatibility (meshtastic-meshcore-differentiation.md). Second, the high-K hidden-node hub is validated in simulation and against a captured real hub, but not yet by an instrumented Loomwave deployment at that scale: the Stone Mountain anchor is a measured Meshtastic relay (~93% mutually hidden) that motivates the design, not a Loomwave node serving those terminals (stone-mountain-hidden-node-hub anchor; meshcore-network-characterization.md frames the second-network rationale). Third, the multi-radio concurrent-cell hub (#72) is design-complete but unbuilt: it is gated on a cell() radio-abstraction refactor and carries a load-bearing risk — the LR1121 re-inits after every TX or its RX goes deaf, which a per-frame TDMA cell hits every frame (dual-radio-hub-design.md). BW/CR adaptation is the next modulation-authority increment, now analysed (bw-cr-adaptation-result.md, #76): because spreading factor saturates at SF7, bandwidth is the only lever that can speed a strong link further — BW500 nets ×4 telemetry capacity over the shipped SF7/BW125 floor (bw_cr_adaptation.png), a larger and orthogonal gain to the per-link SF work; coding rate is a separate robustness knob (+35% airtime at 4/8 for coding gain against burst/fade). Extending the worst-member/per-link selector from SF to (SF, BW) is a contained extension of #66, gated on the BW500-needs-a-strong-link and the spectrum/DFS channel-width caveats.

11.3 Future work

The clearest next axis is a multi-radio capacity layer. PACT-lite already makes idle membership nearly free within one cell, but the airtime ceiling is per-channel; the only way past a single channel's ceiling is more channels (dual-radio-hub-design.md). One INFRA node running R full cells on R non-overlapping channels serves roughly R·K members at R× aggregate capacity, with clean spatial reuse protected by LoRa capture — capacity_scaling.py shows five MediumFast cells deliver ×16 per-node versus one LongFast domain and improve the 500-node per-node ceiling ×34 (dual-radio-hub-design.md; capacity_spatial_reuse.png). Beyond capacity, the operational feedback loop makes GitHub the community surface — an AI maintainer semi-automatically triages issues and field reports behind a human gate — and enforces sim/firmware lockstep: every protocol change MUST land in the SimPy model in the same change-set as spec and firmware, with simulation scenarios generated from operational telemetry rather than hand-authored, so the evaluate-before-implement gate never silently drifts (operational-vision-note.md).

11.4 Conclusion

Loomwave rests on a single observation — that the hidden-node topology which defeats a contention MAC is the same one that makes a coordinated schedule possible — and on taking it seriously: the node that hears N mutually-hidden terminals cannot let them contend at ~93% hiddenness, but it can schedule them. The thesis is carried by a three-way validation — calibrated simulation, hardware bring-up, and a captured real-world hidden-node hub — so that the hidden-node-as-asset claim rests on measured rather than assumed behavior (§9–§10). Where Meshtastic and MeshCore optimize for zero-infrastructure simplicity and accept a low scaling ceiling, Loomwave optimizes for reliable delivery at scale with explicit hidden-node mitigation, accepts an optional coordinator and more complexity, and is engineered to be never worse than a flood network when that coordinator is absent (meshtastic-meshcore-differentiation.md).