Status: design (2026-06-22). Lets a client hand off to an adjacent cell by retuning directly to a known neighbour PHY instead of blind-scanning the (channel × SF) grid.
A client today finds a cell by blind-scanning the grid. The discovery-latency result
(discovery-latency-result.md) measured that at E[L] ≈ (G−1)/2·D — 49 s at 3×3, 139 s at 4×6 — vs a
~1.1 s constant when the client knows where to look. In a multi-cell deployment (the two-tower setup,
or a campus) a moving client must hand off between cells; without a neighbour plan every handoff pays the
linear blind-scan tax. The sim's explicit conclusion: ship the guided plan before enabling multi-cell
roaming. This is that plan.
ADV_NEIGHBORS advertA new signed advert subtype the coordinator broadcasts periodically in the CW (like the identity advert),
alongside the existing ADV_IDENTITY/ADV_COORD_CLAIM/ADV_LSA/ADV_NAME:
pub const ADV_NEIGHBORS: u8 = 0x05;
body: n(1) ‖ n × { freq_offset: u16 BE, sf: u8 } // 3 bytes per neighbour cell
freq_offset is the channel-plan offset (ch_offset_to_hz, base 900 MHz / 100 kHz; offset 0 unused).
The advert is signed by the coordinator's Ed25519 key (via Advert.sig), so a client trusts the neighbour
list from the coordinator it has already verified. The client does NOT need the neighbours' identities in
advance — on retuning to a neighbour PHY it hears that neighbour's OWN signed beacon + identity advert and
joins it as a normal (but FAST) discovery.
Knows its neighbours from LW_NEIGHBORS="freqHz:sf,freqHz:sf,..." (static config for v1; auto-derivation
from the #64 wired backbone — coordinators already exchange state over the LAN — is the follow-on). Each
listed neighbour is mapped to { ch_hz_to_offset(freqHz), sf } and broadcast in the periodic
ADV_NEIGHBORS. The advert costs airtime only when neighbours are configured (none → never sent).
ADV_NEIGHBORS advert.abandon_coordinator() / beacon-loss path — try the cached neighbours
FIRST, before the blind grid scan: for each neighbour, retune (freq via the channel plan, SF), listen
~one beacon-period for a valid signed beacon; on a hit, adopt it (the foundation's cc_adopt path) — a
direct, ~1 s acquisition. If no neighbour answers, fall back to the existing grid scan (the safety net).Two cells A @915 / B @905 (the two-tower channel plan), client joined to A with B configured as A's neighbour. KILL cell A → the client should retune directly to 905 and re-join B in ~1–2 s (the guided path), vs the tens-of-seconds blind grid scan it would otherwise pay. Measure the re-acquisition time both ways. The two tower nodes (.176 @915, .108 @905, 20 ft apart) make this a real multi-cell test.
ch_offset_to_hz / ch_offset_to_mhz (#65 announced change) — neighbour freq encoding.ADV_NEIGHBORS.cc_adopt + the SF-derived geometry (the foundation) — the client adopts a neighbour exactly like any
discovered cell, just reached directly instead of by scanning.