4.4 KiB
AP interference / airtime-reduction model — design + data feasibility
Goal (per Mike): a fleet/site-level model that decides which AP radios to disable, and where to reduce power, to cut total airtime contention while preserving client coverage. Per AP per radio, all bands (not 2.4-only, not per-client). Inputs: each AP's view of neighboring APs (RF)
- historical client connections.
Data feasibility (probed on Cascades 2026-06-15)
| Signal the model wants | In Mongo ace? |
Source to use |
|---|---|---|
| Our AP ↔ our AP RF visibility (A hears B at RSSI r) | NO — rogue is FOREIGN APs only; our managed APs are filtered out (0 rows match our SSIDs) |
Live Network API stat/device neighbor table / triggered RF scan (Plane 2) |
| Historical client→AP connections / roam overlap | NO — user keeps only last_uplink_mac (last AP); no sessions, alarm empty, stat collections empty |
Accumulated stat/sta polling over time (Plane 2 + a collector) |
| Physical AP coordinates | NO — 0 APs placed on the 1 floorplan | derive coarse topology from AP names (room#/floor encoded) |
| Radio config (channel/band/width/power/min_rssi) | YES | Mongo device.radio_table (Plane 1) |
| Foreign interference per channel | YES | Mongo rogue aggregate (Plane 1) |
Conclusion: the interference graph the model needs (our-AP mutual RSSI + client overlap) cannot be built from Mongo. It requires Plane 2 (the live Network API) plus a collector that accumulates snapshots over time. Mongo gives config + foreign-interference + (via names) a coarse topology prior to seed the model before enough live data is collected.
Model design
Per band b (ng/na/6e), build a weighted graph over AP radios:
- Nodes: each AP's radio on band
b. - RF edges
w_rf(A,B): from the live neighbor table — how strongly A hears B (and vice-versa), scaled up when same/overlapping channel. Strong mutual RSSI on the same channel = high co-channel interference. - Overlap edges
w_ov(A,B): fraction of clients that have associated with BOTH A and B over the collection window (built by snapshottingstat/staevery N minutes). High overlap = they cover the same space → one is redundant. - Per-radio metrics:
load(num_sta, livecu_total),unique_coverage(clients only this radio serves at good RSSI),interference_contribution(Σ strong RF edges on same channel).
Recommendation logic (greedy, coverage-safe):
- Disable a radio when: high
interference_contributionAND highcoverage_redundancy(its clients keep good signal from neighbors) ANDunique_coverage ≈ 0. Disable the worst offender, recompute the graph, repeat until a redundancy floor is hit (don't open holes). - Reduce power when interference is high but
unique_coverage > 0(can't disable without a hole) — shrink the cell to cut contention while keeping coverage. - Leave radios that carry unique coverage and contribute little interference. Band weighting: 2.4 prunes most aggressively (most redundant + least capacity value); 5/6 lighter; 6GHz usually keep (clean band, steer up). Output = ranked per-AP-per-radio actions with the metric that justified each, applied per zone with live before/after validation.
Prerequisites to build it (the real next step)
- Wire Plane 2 — provision a dedicated read-only UniFi admin or Network integration API key
on
.29(doable with our root SSH), vault asinfrastructure/uos-server-network-api. Givesstat/device(live neighbor RSSI,cu_total,num_sta, satisfaction) +stat/sta(client→AP). - Stand up a collector — a periodic job (cron on
.30/a fleet host) snapshottingstat/device+stat/stainto a small store (sqlite/postgres). The overlap + RF matrix accrue over the collection window (a week+ gives a usable model; longer = better). This is the "historical look at devices connected" Mike asked for — the controller doesn't retain it, so we accumulate it ourselves. - Build the model on the accumulated data; seed early recommendations from the Mongo config + AP-name topology prior until enough live data exists.
Status
Phase 1 (config + foreign-interference audit) is built (scripts/audit-site.sh). The interference
model is blocked on Plane 2 + the collector — needs a go to provision the UniFi API account and
stand up the collector.