Executive summary
Each sensing node already produces three independent views of how many radios are active nearby, and none of them required new hardware:
- Delivery deficit — the injected 25 Hz reference loses frames to CSMA deferral and collisions on 2.4 GHz, and its 5 GHz pair does not. Measured at 95.8% vs 100.1% in an empty office.
- Transmitter census —
csidrecords the source MAC of every frame it extracts CSI from, so the session already contains a distinct-device count. - BLE advertisement stream —
blescandon the same node, sameunix_ts_nsclock, with upper/lower device-count bounds already computed fromaddr_kindinmonad_knowledge/csi/ble.py.
Fused with CSI these answer a question CSI alone cannot: is the room empty, or is it full of people sitting still? That ambiguity is the standing weakness of CSI crowd counting and the reason stationary counting is its own literature.
The arithmetic that reopened this
The retrospective extrapolated from one wearer. A lecture room is a different regime.
| Single wearer | ~60 students | |
|---|---|---|
| Active connected BLE links | 1 | ~20 (unvalidated assumption: 1 in 3 with live earbuds or watch) |
| Duty cycle per link (30 ms connInterval, ~300 µs/event) | ~1% | — |
| × fraction hopping into a 20 MHz passband (9 of 37 data channels) | ~0.24% | ~5% |
| Expected hits per 1000 injected frames | ~6.7 | ~50 |
| Measured empty-room false-positive floor | 7.9 ± 9.8 | 7.9 ± 9.8 |
One wearer sits inside the floor. Twenty sit several standard deviations above it. The negative result was about the wrong regime.
The honest correction that follows: in that room the band is expected to be dominated by Wi-Fi, not BLE — more devices, longer frames, higher duty cycle, and a higher transmit power at the specification level (BLE operates around 0 dBm where Wi-Fi clients sit well above it; the exact ratio in this deployment is not measured and should not be quoted until it is — S1 measures it). Isolating BLE is a cost paid only to make a BLE-specific claim. For an occupancy channel, all of it is signal.
The spectral constraint (derived 2026-08-17) — why the inclusion arm moved to ch3
Worked from the two channel plans alone in notebooks/csi-ble-spectral-overlap/. A Wi-Fi channel c
is centred at 2407 + 5c MHz and its 52 reported subcarriers occupy 16.25 MHz; a BLE RF channel k
sits at 2402 + 2k MHz. Three consequences bear directly on this card's design.
1 · ch11 excludes BLE advertising completely. Advertising channels 37 / 38 / 39 sit at 2402 / 2426 / 2480 MHz. ch11 occupies 2453.9–2470.1 MHz. None of the three is inside it, under the OFDM occupied band or the 22 MHz DSSS mask. The exclusion arm is therefore stronger than it was written to be — it is not a low-advertising condition, it is a zero-advertising condition — but it also means the archive's 2.4 GHz sessions could never have contained an advertisement, only connected-link traffic. The row above ("9 of 37 data channels") is correct precisely because advertising was never in scope.
2 · The ch13 inclusion arm was confounded by construction. ch13 is centred 2472 MHz, so advertising channel 39 at 2480 MHz lands on subcarrier +25.6 — array index 50.6 of 51, 0.4 tones from the band edge. That is inside the roll-off region where the ABBA probe measured −3 to −4 dB and found 55 % of its spurious events. Treatment and dominant artefact would have occupied the same subcarriers, and S6 could not have produced a falsifiable result.
3 · ch3 is the replacement, and the ranking is boundary-independent. Ranking every legal European channel by distance to the nearer artefact region:
| Wi-Fi | Adv | Array index | From DC | From edge | Min hazard |
|---|---|---|---|---|---|
| ch3 | 38 | 37.8 | 12.3 | 13.2 | 12.3 |
| ch5 | 38 | 6.8 | 18.7 | 6.8 | 6.8 |
| ch4 | 38 | 22.8 | 2.7 | 22.8 | 2.7 |
| ch13 | 39 | 50.6 | 25.1 | 0.4 | 0.4 |
ch3 is the only channel far from both hazards, and the ordering does not depend on where the skirts are cut off. Advertising channel 37 is unreachable on every legal channel — it appears only under ch1's 22 MHz mask skirt, where there are no subcarriers to record it.
Two budgets that bound S6 even at ch3
- Amplitude. An LE 1M burst is GFSK at 1 Msym/s with modulation index ≈ 0.5, so it occupies ~1.06 MHz ≈ 3.4 subcarriers — not the 6.4 implied by the 2 MHz channel allocation. Concentrating its power into 3.4 of 52 tones buys 11.9 dB against a wideband comparison, but a 3 dB CSI excursion still requires the burst to arrive within 19.6 dB of the Wi-Fi frame. The window is two-sided: ZigFi (INFOCOM 2018) measured the effect vanishing again at high interferer power because the Wi-Fi sender's CCA defers the frames that would have carried the evidence.
- Rate. One advertiser at a 100 ms interval with a 376 µs ADV_IND hits 3.84 records per 1000, and that figure is independent of injection rate — confirming the rate-is-not-a-lever correction S7 was written to test, before S7 runs.
The estimator revision (2026-08-17) — the probe measured the right residual with the wrong statistic
The spectral derivation above forced a second look at how the refutation was computed, and that turned out to matter more than the channel.
notebooks/csi-ble-interference-probe/probe.py builds a sound residual: amplitude in dB, de-levelled
per record by its own across-tone median (which cancels AGC, transmit power, path loss and thermal
gain drift within each record), then baselined per tone. Two things about the statistic were
wrong, and both are fixable without new data.
1 · It thresholded and counted events instead of estimating a mean. A 3 dB per-record excursion
requires per-tone SIR ≤ 7.7 dB — a demanding arrival. But interference enters the channel estimate
with random phase, so E[|Ĥ|²] = |H|²·(1 + r) and over N records with duty cycle d the mean
inflation on affected tones is (1 + d·r). That product d·r is airtime × power — which is exactly
what "traffic size" means. An estimator of it has no threshold, uses every overlapping record rather
than only those in the tail, and averages as 1/√N.
2 · It took the maximum over all 52 tones. That is maximally exposed to whichever tone carries the worst artefact — and it is precisely what the position diagnostic found. The derivation above fixes the tone indices a BLE channel can occupy, so the statistic becomes a matched filter on known positions rather than a maximum over unknown ones.
The revised statistic
Per record i, on the de-levelled dB amplitudes:
D_i = mean over T_BLE − mean over T_REF
where T_BLE are the tones in-band BLE channels land on for this Wi-Fi channel and T_REF is the
clean complement. The absolute level of D is the static channel-shape difference between the two tone
sets and carries no information; only its movement can. Implemented in
notebooks/csi-ble-spectral-overlap/estimator.py.
Measured on both corpora
| ABBA ch11 (2.4 GHz) | ABBA ch36 (5 GHz control) | today ch6 (2.4 GHz) | |
|---|---|---|---|
| arms × records | 5 × ~40k | 5 × ~41.5k | 4 × ~10.4k |
| arm length | 30 min | 30 min | 5 min |
| room | certified empty | certified empty | occupied (operator present) |
| static offset D | +0.868 dB | +1.540 dB | −1.794 dB |
| within-arm SEM | 0.26–0.68 m-dB | 1.24–1.54 m-dB | 2.3–3.1 m-dB |
| arm-to-arm sd (systematic floor) | 15.74 m-dB | 16.93 m-dB | 7.00 m-dB |
| within-arm temporal sd (10 s bins) | 14.0 m-dB | 36.9 m-dB | 13.7 m-dB |
Against the projections in the arithmetic table: on the ch11 floor, one wearer at 2.64 m-dB is 0.17× and twenty wearers at 34.3 m-dB is 2.18×.
What the 5 GHz control settles
BLE cannot exist at 5 GHz, and the 5 GHz arms have the larger floor on both measures — 16.93 against 15.74 m-dB arm-to-arm, and 36.9 against 14.0 m-dB temporally. The estimator's noise is therefore instrument, end to end, and the negative control is noisier than the treatment band. That is as clean a null as this design can give.
It also disposes of the risk I expected to be fatal. I had predicted the temporal movement of D would be motion — frequency-selective fading from people in the room, confounded with the occupancy signal itself. It is not: the empty office shows 14.0 m-dB of temporal drift against today's occupied room at 13.7 m-dB, and the band where no BLE exists is worst of all at 36.9. Whatever moves D on a 10-second scale is neither BLE nor people.
A lever that looked obvious and is not there
The ch6 floor is 7.00 m-dB against ch11's 15.74, and the obvious difference is arm length — 5 minutes against 30. Less wall-clock per arm should mean less accumulated drift, which would make a short-arm design a free halving of the floor.
Tested and refuted the same afternoon (estimator.py --arms resegment): re-cutting the five
30-minute ch11 arms into pseudo-arms of 2.5 / 5 / 10 / 15 / 30 minutes — same data, same node, same
channel, same night, length the only variable — leaves the floor flat.
| pseudo-arm length | n | floor (m-dB) |
|---|---|---|
| 2.5 min | 55 | 16.29 |
| 5 min | 30 | 15.44 |
| 10 min | 15 | 15.42 |
| 15 min | 10 | 15.51 |
| 30 min | 5 | 15.75 |
Arm length is not a lever. The ch6/ch11 difference is therefore unexplained — remaining candidates are node (monad06 vs monad02), channel and its tone-set geometry (ch6 gives T_BLE = 23 / T_REF = 15, ch11 gives 18 / 20), ambient environment, and date. Worth one controlled comparison before anyone plans around a 7 m-dB floor, because the honest number to design against today is 15.7 m-dB, which is where the 20-wearer projection sits at 2.18×.
The flatness is itself informative: a systematic that does not integrate down between 2.5 and 30 minutes is not slow thermal drift. It is something with structure on a scale shorter than 2.5 minutes, or a fixed per-arm offset. That is a narrower thing to go looking for than "drift".
Two honest corrections that came out of running it
- The revision is not a sensitivity win, and a first draft of this section claimed it was. Like-for-like the two estimators are comparable — one wearer: old 0.39× floor, new 0.38×; twenty wearers: old 5.1×, new 4.90×. The 12.5× improvement first written down divided the new 20-wearer signal by the old 1-wearer ratio. The real advantage is the absence of a threshold: the old probe counted only records crossing 3 dB, so its effective signal was 3.84/1k × P(SIR ≤ 7.7 dB) for an unknown fraction. The gain is real and currently unquantified.
- The confound is now measured, and it is at the same scale as the signal. The within-arm temporal sd is 13.7 m-dB — larger than the arm-to-arm floor and within a factor of 2.5 of the 20-wearer prediction. A person moving in the room changes |H(f)| frequency-selectively, which moves D with no BLE present at all. A BLE-airtime reading and an occupancy reading are confounded at comparable magnitude, which is the one risk that decides whether this channel is usable. The empty-office ABBA arms are the test that separates them: if their temporal sd is much smaller than 13.7 m-dB, today's figure is motion.
Consequences for the schedule
- S6 becomes ch3 / ch11 / ch36. ch36 stays as the true negative control.
- S7 is reinstated as a real test, not a confirmation. "Raising the injection rate is not a lever" is estimator-specific and was over-generalised. For a thresholded event detector bounded by a systematic floor it holds: events-per-1000 is rate-invariant and more samples do not shrink a systematic. For a mean-shift estimator whose systematics are cancelled within-record, precision is 1/√N and N is rate × duration — rate is a lever again. S7 keeps its injection-rate crossing and gains a prediction: the estimator's SEM should fall as 1/√N while the arm-to-arm floor does not.
- S8 (hopping structure) gains importance. With advertising structurally absent from ch6 and ch11, a connected link walking the in-band comb is the only BLE signature those channels can carry — and hopping is the discriminator the retrospective identified as missing, since amplitude locality is what the artefacts also produce.
- New S9 — the motion confound. Same channel, same emitter, occupancy varied with BLE held fixed (phones in a Faraday bag or powered off). If D moves as much as it does with BLE varied, the channel is not separable and the aggregate framing is the only honest one.
- Capture at HE20 where the schedule allows. 242 tones at 78.125 kHz resolve an LE 1M burst across ~13.6 tones instead of ~3.4 — four times the shape evidence for the same physics, on hardware already benchmarked at 608 Hz.
Instrument gap — no controlled BLE emitter exists
Verified 2026-08-17 by reading infra/ansible/roles/ble_scanner/files/blescand.py: the daemon is
passive-scan only (scan_type=0x00, "never transmit a SCAN_REQ — we observe, never probe"). The
implementation chain's claim of "an on-node reference scanner and a commandable advertiser"
overstates what is deployed. Every dose-response above therefore has no calibrated source.
What is verified today, and retires a caveat that has blocked every BLE arm: blescand receives.
blescan.service on monad06 logged 488 reports -> /var/lib/blescan/sessions/20260817T100030Z/adv.jsonl
from an ~11-minute scan, and two sessions (exp-band-5, exp-c1-occupancy-bench-ch36) now carry
ble_rssi.parquet + ble_scan.jsonl sidecars. The "green unit is not capturing" trap is cleared.
Spec: advertise mode for blescand
Small and local — blescand already opens a raw HCI socket and has a _send_cmd(sock, opcode, params)
helper, so this is three commands and a flag, not a new daemon or any hardware.
| Element | Detail |
|---|---|
--advertise |
Mutually exclusive with scanning on the same hciN (one controller, one role per session). |
LE_Set_Advertising_Parameters (0x2006) |
min_interval / max_interval from --adv-interval-ms; adv_type = 0x03 (ADV_NONCONN_IND — no scan/connect requests, so the emitter cannot be perturbed by observers); channel_map from --adv-channels so a single advertising channel can be isolated. |
LE_Set_Advertising_Data (0x2008) |
Fixed-length payload from --adv-bytes, defaulting to 31 so burst duration is the known 376 µs. |
LE_Set_Advertising_Enable (0x200a) |
Gated by the same _bring_up() / HCIDEVUP path the scanner already uses. |
| TX power | Read via LE_Read_Advertising_Channel_TX_Power (0x2007) and recorded in the session sidecar — the budget needs it, and the controller will not necessarily honour a requested value. |
| Session shape | Identical to a scan session (session.json + S3 sync), with role: advertiser and the four parameters above, so experiment audit can claim it like any other. |
That turns interval, payload length, advertising channel and TX power into declared axes. Without it the only emitter is a phone, which EXP-P3 already records as "a built-but-unmeasured instrument" whose rate is assumed to wander — usable for presence, useless for a calibrated dose-response.
> > Two design decisions worth carrying into the arm: > - **It refuses to transmit while a `csid` capture is live on the same host** (`--force` > overrides, for when the interference *is* the experiment). An unrecorded emitter appearing > mid-session is unrecoverable after the fact. If `/proc` exists but is unreadable that counts > as "cannot prove idle" and also blocks. > - **`Restart=no` and never enabled at boot.** The refusal exits 2, and a restart policy would > convert a correct refusal into a retry loop that eventually wins the race. > > The socket path cannot be exercised off-fleet — macOS has no `AF_BLUETOOTH` — so the first run > on a node is still the real test. Deploy with `--tags ble_scanner`; nothing starts on deploy.Measured: the certified-empty null (2026-08-16)
S1 ran early, as a re-analysis of the existing archive rather than a new capture.
notebooks/csi-device-census/hourly.py bins every record of every monad02 session by its own
unix_ts_ns in Europe/Bratislava and splits source MACs into three populations — fleet
(2c:cf:67, our own nodes' management radios, one of which appears at −11.9 dBm), infrastructure
(universal OUI, including an AP's sequential per-SSID BSSIDs), and client (locally-administered
bit set: the randomised addresses phones and laptops broadcast). Only the third is people.
Channel 11, all monad02 sessions:
| Window | Observed | Distinct client MACs | Client records |
|---|---|---|---|
| Certified empty, 23:00–05:00 | 1041 min | 0 | 0 |
| Unlabelled, 05:00–23:00 | 1644 min | 8 | 9663 (5.88/min) |
Peak activity sits at 16:00–20:00 local (up to 4 distinct clients, 46.8 records/min at 16:00), and 07:00–15:00 is essentially zero — consistent with a holiday-period building and with the operator being physically present during lab hours.
The control that makes the zero meaningful. A zero could equally mean the ambient receive path was dead at night. It was not: 4–5 infrastructure MACs were captured during every certified-empty hour, so the receiver was demonstrably extracting CSI from ambient frames all night and simply saw no client devices. The null is a true negative, and the detection threshold is therefore trivially "≥1 randomised client MAC".
What it does not establish. The evening peak is almost certainly one person — the operator, whose laptop holds SSH sessions to the fleet and transmits far more than a pocketed phone; 3–4 distinct client MACs is consistent with a single device rotating its randomised address plus a laptop. Nothing here speaks to whether the count scales with N people. Daytime is unlabelled by the operator's own account, so this is a one-class result: a rigorous null and no sensitivity estimate.
Sessions deliberately not claimed. This finding re-analyses captures belonging to other cards
(the paired-band arms, the overnight drift runs). Adding them to capture_sessions: here would annex
runs EXP-010 never made, which is exactly the attribution defect the register exists to prevent. The
result stands as a re-analysis; EXP-010's own claims begin with its own captures.
Two methodology defects this exposed, both fixed in the reduction and both worth carrying into any future baseline: sessions were first bucketed by start hour, which smears the 9–12 h drift captures across day and night; and all non-sentinel MACs were pooled, which counted our own fleet as ambient devices.
Why the delivery deficit is the right primary observable
Against the subcarrier-residual statistic the retrospective tested, on every axis:
- Already recorded. It is the rate and inter-arrival statistics
csi_summaryalready prints. No DSP, no artefact masking, no new capture format. - Built-in control. The 5 GHz paired arm sits at 100.1% delivery with inter-arrival CV 0.009 — a near-perfect null for everything that is not 2.4 GHz contention: thermal, clock, host load, injector health.
- Immune to what killed the first framing. Band-edge roll-off and DC-adjacent leakage accounted for 94.5% of the retrospective's false events. Neither has any bearing on whether a frame arrived.
- Dynamic range. 4% deficit empty, plausibly tens of percent full, against a CSI statistic whose candidate signal sat inside its own floor.
- Published anchor. Learning From Errors: Detecting Cross-Technology Interference in WiFi Networks ↗ establishes receiver-error statistics as a legitimate cross-technology observable on commodity Wi-Fi. The paired-band control and the occupancy target are the new parts.
What fusion actually buys
Stated against thesis goals rather than as a generic "more modalities is better".
Complementary failure modes. CSI is a motion sensor: weak on a still crowd, strong on a walking individual. This is not an assumption — it is the standing finding of the stationary-counting literature. Guiding Wi-Fi Sensor Placement for Enhanced CSI-Based Sensing in Stationary Crowd Counting ↗ states that counting stationary individuals "presents challenges due to reduced signal variations", with published work capped at 15 individuals; Fast and Robust Stationary Crowd Counting With Commodity WiFi ↗ calls seated counting "considerably more challenging due to the lack of major body motion" and reaches MAE 1.04 for up to 13 people. A Survey on Wireless Device-free Human Sensing: Application Scenarios, Current Solutions, and Open Issues ↗ also grounds the saturation risk directly: for one published counting metric, "with the number of people increasing, PEM almost stopped growing, which caused performance degradation".
Device activity is a presence sensor: indifferent to motion, blind to anyone whose phone is off. A silent lecture hall is CSI's worst case and airtime's best; one person pacing an empty corridor is the reverse. Fusing two channels with near-orthogonal error is the textbook case where fusion beats either input, and it is rare to get it for free. Closest prior work in the vault: Towards Energy Efficient Wireless Sensing by Leveraging Ambient Wi-Fi Traffic ↗ uses ambient traffic as a sensing resource, and A Framework to Estimate Classroom Occupancy using WiFi Channel State Information ↗ is the same venue type as ours — neither fuses an activity channel with CSI.
Resolving presence-versus-motion. A still room and an empty room look alike in CSI. Device activity separates them outright. This is a qualitative capability, not an accuracy delta, and it is the single most useful thing this channel adds. It also converts the output from a scalar count into a room state — empty / occupied-static / occupied-active / in-transition — each with a distinct signature in the (CSI, activity) plane. A state is more useful to a building application than a count, and easier to validate.
Calibration, precisely. "Calibration" covers three separable things and device activity helps with each differently:
- Scale — mapping a CSI feature to a count needs labels. Device activity supplies noisy labels continuously rather than a strong label occasionally, which is the better statistical regime when the thing being corrected drifts continuously.
- Drift — the CSI-to-count map moves with furniture, temperature and AP changes. Device activity is a different physical channel, so its own drift is uncorrelated; their disagreement is a drift estimator that needs no ground truth at all. This is the low bar that only requires the two signals to disagree, and it is the cheapest useful thing here.
- Transfer — a new room normally means recalibration from scratch. A device count is a device count regardless of geometry, so activity is a candidate bridge for the cross-geometry problem where CSI features were found not to transfer off-floor.
Semester-scale ground truth, for free. The library area is empty now and fills during term. That is a natural experiment with a long null baseline available immediately, a dose-response ramp across weeks, and — the part worth the most — the faculty timetable as a zero-cost label source. A scheduled 90-person lecture in a known room at a known hour is an occupancy prior for every capture in that window, across a whole semester, with no participants and no recruitment. Exam weeks and holidays supply the extremes.
The app closes the loop. Scripted walks and marker scans supply rare, expensive, strong labels embedded in a continuous stream of cheap weak ones. That is the right supervision architecture for this problem, and the pieces already exist.
Prior art
The mechanism is not novel and the note previously understated this. Three tiers:
Cross-technology communication — same mechanism, different goal.
XFi: Cross-technology IoT Data Collection via Commodity WiFi ↗ is the closest: an IoT frame colliding
with an ongoing Wi-Fi transmission has its data retained after Wi-Fi demodulation and recoverable
from the decoded payload. That is the collision-residual mechanism, published, on commodity hardware,
used for decoding.
BlueFi: Physical-layer Cross-Technology Communication from Bluetooth to WiFi ↗ is the first PHY-CTC
from BLE to Wi-Fi, using commodity chipsets' spectral-analysis mode to recognise BLE waveforms at
symbol-level granularity. This refutes any claim that a Wi-Fi radio cannot demodulate BLE — the
limitation is chipset-specific, and ours (AX210 via iwlwifi, no spectral-scan interface) has it.
Do not conflate with BlueFi ↗ (Cho and Shin, SIGCOMM 2021), a different paper of the same name.
Cross-technology interference detection — same purpose, network-management goal. Airshark ↗ detects non-Wi-Fi emitters including Bluetooth at 91–96% from Atheros spectral samples. Learning From Errors: Detecting Cross-Technology Interference in WiFi Networks ↗ recognises interference from receiver error statistics alone, >95% for ZigBee, microwave and LTE. Detecting radio frequency interference for CSI measurements on COTS WiFi devices ↗ uses 52-subcarrier CSI and reports a low true-positive rate for Bluetooth specifically, because it hops and often does not overlap the Wi-Fi channel — the published negative this project's retrospective independently reproduced.
Occupancy sensing — Accurate occupancy estimation with WiFi and bluetooth/BLE packet capture ↗ and Passive Indoor People Counting by Bluetooth Signal Deformation Analysis with Deep Learning ↗, both using proper radios.
US 9998903B2 (assignee Avaya) claims detecting BLE beacons via Wi-Fi channel overlap. A patent bears on freedom-to-operate, not on academic novelty, and it is listed here for completeness rather than as a threat to the contribution.
The gap: nobody in the interference-detection literature counts people, and nobody in the occupancy literature uses interference. That crossing is unoccupied.
Contribution
| Claim | Why it holds |
|---|---|
| Device activity as an occupancy channel fused with CSI | The purpose crossing above; complementary failure modes with a qualitative presence/motion resolution |
| Paired-band delivery deficit with a 5 GHz negative control | The interference-detection literature typically has no true negative arm |
| Channel-placement falsification (ch3 vs ch11) | Uses BLE's advertising-channel placement as an experimental instrument; no prior use found as a control |
| Per-event coincidence with a circular-shift surrogate null | Prior work evaluates aggregate accuracy against labelled interferers; per-event coincidence against a same-clock reference is a stronger contract |
| A quantified false-positive floor on 802.11ax/AX210 | Prior work reports accuracies, not floors |
| Instrument artefacts that mimic interference | Band-edge roll-off and DC leakage produce exactly the frequency-localised signature these detectors hunt; 94.5% of ours were that |
| The normalisation trap | Per-arm z-normalised thresholds gave p=0.032; the physically comparable statistic gave p=0.45 |
| Transmit rate is not a sensitivity lever | Corrects an intuitive design error for active-probe interference detection |
Endpoints
Primary. Occupancy explained by device activity: regress the paired-band delivery deficit and the distinct-transmitter census against timetable-derived and app-derived occupancy, over a semester. Report effect size with CI, and the incremental value over CSI alone in a fused estimator.
Secondary (novelty). BLE attribution by channel placement: coincidence between a blescand-logged
advertisement and a frequency-localised CSI residual within ±2 ms, against a circular-shift surrogate
null (displace the BLE series 1–60 s, 1000 shifts, empirical p). Prediction: detectable on ch3,
absent on ch11.
Not measured: SIR. CSI amplitude on the AX210 path is AGC-relative, so no per-packet
signal-to-interference ratio exists. The physical axis is ΔRSSI — blescand's per-advertisement RSSI
in dBm minus csid's per-chain injected-frame RSSI — a difference of two absolute measurements the
instrument does produce.
Design ladder and gates
| Stage | What | Cost | Gate |
|---|---|---|---|
| G0 | Prove the BLE reference receives. playbooks/exp-ble-verify.yml, 60 s, refuses to run while a csid session is live |
minutes | reports > 0 and sidecar present |
| S1 | Empty-baseline harvest. Ambient + injected captures in the library now, while it is empty, with blescan alongside | free, ongoing | The null distribution of all three activity channels, measured rather than assumed |
| S2 | Artefact mask. Re-run the subcarrier probe with band-edge and DC tones masked over existing captures | free | Does masking collapse the 2.4 GHz floor? |
| S3 | Semester ramp. Continuous capture through term start; join to timetable | free after setup | Monotone relationship between scheduled occupancy and each activity channel |
| S4 | Fusion evaluation. CSI alone vs CSI + activity against timetable and app labels | analysis | Incremental accuracy, and specifically on stationary occupancy |
| S5 | Specificity. Drive Wi-Fi contention and a microwave with BLE off | 2 h | False-positive rate under non-BLE interferers |
| S6 | Channel-placement falsification (ch3 / ch11 / ch36), commandable fleet advertiser | one evening | Coincidence on ch3 significantly exceeds ch11, else the BLE claim dies |
| S7 | Dose-response. Advertising interval stepped, crossed with injection rate {25, 250} Hz | half a night | Monotone slope; empirically tests the rate-is-not-a-lever correction |
| S8 | Hopping structure. ch11 with a connected BLE link | opportunistic | Do events walk the nine-channel comb? |
S1 and S3 cost nothing but disk and start paying immediately — the empty library is a wasting asset, and the baseline it offers disappears when term starts.
Operational constraints
- Force the OFDM rate (
monitor_tx_rate0x4100) on any 2.4 GHz injection arm. Group-addressed injection otherwise falls back to 1 Mbps DSSS, which carries no OFDM preamble and yields no CSI — this silently destroyed six of eleven 2.4 GHz arms in the paired-band run. - Pin management Wi-Fi to 5 GHz before 2.4 GHz illumination; the node's own management link and its illumination self-interfere on one board.
- Never
modprobe -rthe iax stack on a live node. - Segment == session, so reductions never slice a long capture by wall-clock.
- A BLE scan running beside a CSI capture is the EXP-009 treatment condition. Any scanner-induced desense is common to the ch3 and ch11 arms and cancels in the primary contrast; that is why the primary contrast is within-band.
Risks
- The channel saturates. Once congested, more devices stop producing proportional loss. Good at low-to-moderate occupancy, poor at high. Must be modelled as saturating, not linear.
- It counts devices, not people, and the device-per-person ratio varies by population — students carry more than a general population, so a model fitted here will not transfer to a corridor.
- MAC randomisation drifts with OS releases, so the transmitter census has a slow bias that must be re-estimated rather than fixed once.
- The detector may be a congestion meter, which is fine for the primary claim and fatal for the secondary. S5 must run before any BLE-specific statement.
- A published promise is at stake. The public site's data-protection note (
_site/privacy.md, served at/privacy/) currently tells visitors that no device identifiers are collected. The researcher confirms the collection is permissible; that is a separate question from the site being accurate. The note must be updated with the real retention window and salt-rotation period before the first capture that touches identifiers — not after. Publishing the change first is the whole reason the page exists, and a QR sticker on a node points at it. - Timetable labels are a biased prior, not truth. Scheduled enrolment is not attendance, and the project has no measured attendance rate for these rooms. Treat the timetable as an ordinal prior (empty / small / large) until app-derived spot counts calibrate it; do not fit a regression against enrolment numbers as if they were counts.
Next steps
- G0 after the current drift session seals (2026-08-17 09:01 UTC).
- Start S1 immediately — the empty-library baseline is time-limited.
- Draft the data-protection note that gates S3.
- Reconcile the S6 segment schedule with
playbooks/exp-ble-csi-coex.ymlbefore authoring its playbook.
Related: EXP-005, EXP-003, EXP-001, DEPLOY-001 AX210 Fleet Deployment FIIT Library.