c-csi-impairment-fit — session 01M14KGBE9NBQB89AMRZ4N272W
Executive summary
exp-csi-calibration was named in IP-094 Phase 4 and in IP-101, and never built, because it needed a real capture. This is that session. It fits the simulator's ImpairmentProfile from ten AX210 nodes of first-party empty-room CSI and then asks the only question that matters downstream: does the occupancy signature survive at the measured values?
It does not. The residual occupancy SNR falls from +20.83 dB at the shipped prior to −0.60 dB at the fitted profile — the signal the in-silico occupancy programme rests on drops to the noise floor when the simulator is given this NIC's actual impairments.
Abstract
One 2,000-record segment per host was read from the csiq containers for all ten nodes of EXP-F2's lib-empty-night arm (legacy OFDM, 52 tones, dot11 grid, 5240 MHz). Six free impairment terms were estimated per host; three are identifiable on a single link and three are confounded with the channel. All three identifiable terms land outside the shipped literature prior. Sixteen exp-csi-static runs on test-lab-synth-floor-0 then compared the prior against the fitted profile over eight paired seeds.
Results, per criterion
1 — Hosts read. 10 of 10, against a floor of 6. Every host returned RecordClass(ntone=52, modulation='legacy_ofdm') on the dot11 tone grid. Read from the csiq containers via monad_knowledge.csi.export, not from the lake's packets table — whose ratio_phase_rad cancels a common CFO by construction and could not have seen the largest effect here.
2 — Container resolution. Done by listing the bucket under [csi].captures_prefix, never from sessions.csiq_form / derived_from. Result: 0 of 10 sessions have a re-derived container; each has 12 or 30 capture.csiq.zst segments. The lake's columns are NULL for these, which is why they were not consulted.
3 — Estimation. Six free terms per host with a percentile bootstrap over hosts, n = 10. n_bits is read from the container (10), not fitted. symbol_duration_s is a constant of the standard. Three terms are named unidentifiable with their confound rather than defaulted to the prior.
4 — Against the shipped prior.
| Parameter | Fitted | 95 % CI over hosts | Prior | Verdict |
|---|---|---|---|---|
cfo_hz |
62,258 | [60,970, 63,545] | 0.0 | outside |
sto_samples |
6.74 | [6.35, 7.21] | 0.5 | outside |
snr_db |
8.57 | [7.43, 9.61] | 30.0 | outside |
iq_amp_imbalance_db |
2.69 | [1.89, 3.50] | 0.3 | channel-confounded |
iq_phase_imbalance_deg |
17.64 | [12.45, 22.85] | 2.0 | channel-confounded |
phase_noise_deg_rms |
87.57 | [83.44, 91.19] | 1.0 | channel-confounded |
62.3 kHz at 5240 MHz is 11.9 ppm — squarely inside a commodity oscillator's ±10–20 ppm. The simulator's prior is 0.0 Hz: it models no carrier offset at all.
5 — The decisive comparison. Sixteen runs, eight paired seeds, test-lab-synth-floor-0 (NOT fiit-ground-0, where session 01M14FXTAQ9QA13GCDCN5DEA65 established that occupancy is inert and this metric is non-monotone).
| Clean oracle | Impaired | Retained | |
|---|---|---|---|
| Prior profile | 24.610 | 14.883 | 60.5 % |
| Fitted profile | 24.610 | 10.817 | 44.0 % |
| Metric | Paired diff (fitted − prior) | 95 % CI, 8 seeds |
|---|---|---|
| attenuation dB/person | −4.066 | [−4.860, −3.359] — excludes 0 |
| K-slope dB/person | +0.908 | [+0.232, +1.693] — excludes 0 |
residual_occupancy_snr_db: +20.83 → −0.60.
And the Rician-K slope sign-flips in both arms — +3.876 clean, −3.737 at the prior, −2.829 at the fitted profile. IP-101 predicted exactly this ("mild NIC impairments halve the attenuation signature and sign-flip the K-slope"). The prediction is confirmed, and it already holds at the shipped prior.
6 — The honesty pass. Any in-silico occupancy result quoted from the clean csi.hdf5 oracle overstates the attenuation signature by roughly 2.3× against this NIC (24.6 vs 10.8 dB/person), and does not preserve the K-slope's sign at all. Results quoted from csi_impaired.hdf5 at the shipped prior still overstate it by ~1.4× and already carry the flipped K-slope. No published figure in this project is safe to read as "what the AX210 would see" without restating it at the fitted profile.
What this means
The simulator is usable as a physics oracle and is not usable as a proxy for this receiver. The gap is not scene fidelity — it is the measurement model, exactly as the hardware-artefact literature (guarino2026_e72c, chen2023_5cbd) has been saying. synthetic-csi-sim-to-real-transfer gains no support here: if anything this sharpens why its zero-shot arm returned R² ≈ −2.3.
Caveats, in order of how much they should bother you
snr_dbis the weakest of the three I kept. 8.6 dB implies ~37 % relative amplitude variation in a room that should be static. AGC hunting between packets is a live alternative to a thermal noise floor, and this corpus cannot separate them.- The pairing is per-seed, not per-link. The brief's
statistics.estimandsays "paired per-link difference", butmean_atten_db_per_personis a per-run scalar, so n = 8 seeds is the available unit. The criterion's intent — a CI, never two point estimates side by side — is met; its literal wording is not, and the brief should be corrected rather than the result stretched. - One segment per host, ~2,000 records. A session is 25–38 segments; reading one bounds the download but samples one window of one night.
- Three terms are upper bounds, not measurements. Their confounds need a reference this corpus lacks: a loopback for the I/Q pair, a second synchronous receiver for phase noise. Independent corroboration: the three identifiable terms are tight across ten identical NICs (
cfo_hz±5 %) while the two I/Q terms span 10–12× — a hardware property clusters, a room property scatters. rt_precisionis float32 on the Metal lane for all sixteen runs.- A reduction bug was caught before sealing, not after. My first pass read the top-level
mean_atten_db_per_person, which is the clean oracle and identical across arms by construction, and reported a paired difference of exactly 0.0000 on every seed. An exact zero is a bug, not a null. The impaired metrics live underdomain_metrics.impairment.*_impaired.