98 lines
7.0 KiB
Markdown
98 lines
7.0 KiB
Markdown
# AP interference / airtime-reduction model — design + data
|
||
|
||
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 neighbor/overlap relationships
|
||
+ historical client connections + airtime.
|
||
|
||
## Data — it's ALL in the controller (corrected 2026-06-15)
|
||
First pass only checked the `ace` DB (config/current state) and wrongly concluded the history
|
||
"isn't there." It is — in **`ace_stat`** (1.5 GB of accumulated time-series) and the
|
||
`wifi_connectivity_event` collection. **No external collector is needed**; the controller already
|
||
retains it. Three databases on the UOS Mongo (port 27117):
|
||
|
||
| DB | Holds | Use |
|
||
|---|---|---|
|
||
| `ace` | config + current state | radio_table (channel/band/width/power/min_rssi), `rogue` (FOREIGN interference), `channelplan`, device/floorplan |
|
||
| **`ace_stat`** | **time-series history** | the model's airtime + client history (below) |
|
||
| `ace_audit` | audit log | — |
|
||
|
||
### `ace_stat.stat_hourly` / `stat_daily` (o:'ap') — per-AP per-band airtime history
|
||
Flat per-band fields (`ng`=2.4, `na`=5, `6e`=6): **`<band>-cu_total`** (channel utilization %),
|
||
**`<band>-cu_interf`** (airtime lost to interference %), **`<band>-cu_self_rx/tx`**, **`<band>-num_sta`**,
|
||
`<band>-satisfaction`, `<band>-tx_retries`. Plus `ap` (AP mac), `site_id`, `time` (ms epoch). This
|
||
is the historical airtime/interference/load profile per AP per band — the core "who's contending"
|
||
signal. (Also o:'user' per-client retries/anomalies; o:'site' rollups.)
|
||
|
||
### `ace_stat.wifi_connectivity_event` — the roam graph (empirical AP adjacency)
|
||
Each doc: `from_endpoint{mac(AP), channel, channel_width, band, rssi}`, `to_endpoint{mac(AP), ...}`,
|
||
`client_mac`, `successful`, `time`, `_class:WIFI_ROAMING`. Every roam is an **edge between two APs a
|
||
real client handed off between** → empirical coverage overlap/adjacency, weighted by volume, with
|
||
the handoff RSSI (coverage quality). This is the "historical connections → which APs cover the same
|
||
space" signal, and it's better than a raw RF scan because it reflects where clients actually move.
|
||
|
||
### Still useful from `ace` / setup
|
||
- `rogue` aggregate = FOREIGN co-channel interference per channel (Plane-1 audit).
|
||
- **Floorplan coords**: not set today (0/77 APs placed). Mike is willing to use the floorplan
|
||
feature → once APs are placed, `device` x/y gives true physical distance for adjacency edges
|
||
(complements the roam graph, esp. for APs that rarely roam-share).
|
||
- Live Network API (`stat/device`/`stat/sta`) still adds *current* utilization + the AP's live
|
||
RF-neighbor table; nice-to-have for before/after validation, not required to build the model.
|
||
|
||
## Model
|
||
Per band `b`, over a history window (default 7d):
|
||
- **Per-radio airtime**: avg `cu_total`, avg `cu_interf`, avg `num_sta` from `stat_hourly`.
|
||
- **Overlap/redundancy**: roam volume per AP and per AP-pair from `wifi_connectivity_event` (high
|
||
roam-share to neighbors ⇒ a client leaving this radio has somewhere to land ⇒ redundant).
|
||
- **Score (v1, `model-rank.sh`)**: `(cu_total + cu_interf) * log(1+roams) / (clients+1)` — high =
|
||
this radio burns airtime in a contended band AND its clients are mobile/redundant ⇒ shrink or
|
||
disable. Hint: **DISABLE** when high roam + near-zero clients; **POWER-DOWN** when busy but holds
|
||
some unique load.
|
||
- **v2 (greedy, coverage-safe)**: iteratively disable the top candidate, recompute overlap so we
|
||
never open a hole (stop when a radio's clients would lose their only good-RSSI neighbor), and add
|
||
floorplan-distance edges once APs are placed. Output = ranked per-AP-per-radio actions with the
|
||
metric that justified each, applied per zone with before/after validation.
|
||
|
||
## v1 result (Cascades 2.4GHz, 7d) — the smoking gun
|
||
`model-rank.sh cascades 7 ng`: 2.4 radios run **cu_total 74–94%, cu_interf 61–81%, ~0.3–2.6 clients
|
||
each** across 75 APs. Translation: 2.4 is saturated and mostly interference, serving almost no one —
|
||
a textbook case to disable 2.4 on most APs and power down the rest. Run `na`/`6e` for the 5/6GHz
|
||
picture (expected: keep, with 6GHz the clean capacity band).
|
||
|
||
## Materials matter — and why the roam graph already handles them
|
||
Cascades has **reinforced-concrete + steel-sheet walls across the hallways** that kill signal on all
|
||
bands (rooms either side of a hall, and floor-to-floor, behave normally; **across a hall is nearly
|
||
opaque**). This is why **geometric distance must never be the redundancy signal on its own**: two APs
|
||
on opposite sides of a hall are *close* but RF-*isolated*. The **roam graph is materials-aware by
|
||
construction** — clients can't roam across the steel wall, so those APs simply never appear as
|
||
neighbors, and the model won't call them redundant. Corollaries baked into the design:
|
||
- Distance/floorplan (once APs are placed) is only a **prior**; an AP pair counts as redundant **only
|
||
with RF/roam evidence** (bidirectional roams at adequate p25 RSSI). Never disable on proximity alone.
|
||
- AP **room parity (odd/even)** ≈ hallway side at Cascades — a useful prior to avoid pairing across-hall APs.
|
||
- The **live AP-to-AP RF neighbor table (API wireup)** *directly measures* this attenuation — the gold
|
||
standard for the coverage graph, and the reason it's worth wiring.
|
||
|
||
## Hardening from the multi-model review (Grok + Gemini)
|
||
Applied: bidirectional roam requirement (not one-way "escape"); band-specific p25 RSSI bars
|
||
(2.4 −68 / 5 −72 / 6 −75); **load-shift simulation** (only disable if a strong neighbor stays < 85%
|
||
after absorbing the cu_self it inherits — avoids "capacity cascades"); benefit = `cu_interf` (+ a
|
||
normalized `tx_retries` thrash term), with `cu_self` treated as transfer cost; 40%/zone disable cap;
|
||
**stepwise** output (power-down → observe → disable). `tx_retries` is a raw count → normalized by
|
||
`wifi_tx_attempts`.
|
||
|
||
## Key empirical finding (Cascades): power-down now, disable later
|
||
The **airtime data is rich and unambiguous** → `optimize-radios.sh cascades ng` recommends
|
||
**power-down on 74/75 2.4 radios** (all 74–94% utilized, 61–81% interference, ~1 client — the safe big
|
||
win; power-down keeps the BSSID). But the **roam data is too sparse** to prove coverage redundancy for
|
||
**disables** (almost no AP has a strong bidirectional roam-neighbor), so the optimizer correctly
|
||
recommends **0 disables until coverage evidence exists**. That evidence is exactly the **live AP-to-AP
|
||
RF neighbor table** → **the API wireup is the enabler for confident radio *disables*** (and for
|
||
before/after validation). So the rollout is: Phase A power-down (now) → Phase B re-measure (live API)
|
||
→ Phase C disable the radios the RF table proves redundant.
|
||
|
||
## Status
|
||
- `scripts/audit-site.sh` — config + foreign-interference audit (Plane 1).
|
||
- `scripts/model-rank.sh` — **v1 airtime-reduction ranker from real history** (this doc). Works now.
|
||
- Next: v2 greedy coverage-safe optimizer + floorplan-distance edges (after APs are placed on the
|
||
floorplan) + optional live-API before/after validation.
|