Skip to content

Add antenna-decorrelation measurement harness (spatial-diversity #129)#140

Merged
josephnef merged 4 commits into
masterfrom
spatial-diversity-foundations
Jul 2, 2026
Merged

Add antenna-decorrelation measurement harness (spatial-diversity #129)#140
josephnef merged 4 commits into
masterfrom
spatial-diversity-foundations

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

Summary

Foundation tooling for the spatial-diversity roadmap (#127) — the effort to
exploit the RF chains a single-stream video link leaves idle (1 on a 2T2R part,
up to 3 on a 4T4R 8814) as diversity/robustness, the third axis alongside
frequency hopping (docs/frequency-hopping.md) and the fused FEC
(docs/fused-fec.md).

Closes the measurement half of #129. Every downstream diversity claim (RX MRC
#130, ANTSEL probe #131, STBC #132, spatial×FEC #133) rests on the chains'
antennas being decorrelated — on a small airframe they may not be. This adds
the harness that measures the decorrelation a given antenna layout actually
delivers, so gains are reported as measured rather than theoretical.

Changes

  • demo/main.cpp — opt-in DEVOURER_RX_ALLPATHS=1 emits a
    <devourer-rxpath> line per canonical-SA frame carrying all four RX chains
    (A,B,C,D) of rssi/snr/evm. The library already populates paths C/D on the
    8814AU; the demo previously printed only A,B. On a distinct tag so the
    canonical <devourer-stream>/<devourer-body> two-path format (and its regex
    consumers) is untouched. Paths C/D read 0 on 2T2R parts.
  • tests/antenna_decorrelation.py — parses the per-chain metrics and reports
    per-chain level stats, the pairwise envelope correlation matrix (Pearson on
    linear amplitude — the diversity-relevant correlation, not on dB), and a
    combining analysis: selection- and maximal-ratio-combining gain over the
    best single chain at the 10 % and 1 % outage points vs the theoretical
    10·log₁₀(N) array gain. Verdict is keyed off the worst pairwise |ρ| (ρ<0.7 =
    "diversity still useful"). A hardware-independent --self-test validates the
    estimator and combining math against synthesised correlated Rayleigh fading.
  • tests/run_antenna_decorrelation.sh — build + capture + analyse runner
    (exact-comm cleanup trap, uv venv, sudo for the USB claim); optionally starts
    the beacon TX (TX_PID).
  • CLAUDE.md — document DEVOURER_RX_ALLPATHS.

Why measure against the canonical beacon

The library only surfaces per-chain metrics for the canonical SA
57:42:75:05:d6:00, so the harness measures a steady, known source (the standard
two-adapter bench setup) — the right controlled condition for a correlation
estimate, rather than noisy ambient traffic.

Testing

  • uv run python tests/antenna_decorrelation.py --self-test — green. Recovers a
    known envelope correlation to ±0.05 (synthesising with field-corr = √ρ_env,
    since ρ_env = |ρ_field|² for Rayleigh), confirms independent 4-chain MRC mean
    gain ≈ 10·log₁₀(4), and that correlated chains yield less low-outage gain than
    independent ones.
  • WiFiDriverDemo builds with the new emission.
  • Not yet run on hardware — the on-air 8814 capture across antenna
    spacings/polarisations is the field-validation step, tracked in Antenna-decorrelation measurement harness #129/RX-side in-chip MRC on an 8814 ground station #130.

🤖 Generated with Claude Code

Foundation tooling for the spatial-diversity roadmap (#127): measure the
inter-chain envelope correlation and realised diversity gain a given antenna
layout actually delivers, so downstream diversity work reports measured gain
rather than the theoretical ceiling.

- demo/main.cpp: opt-in DEVOURER_RX_ALLPATHS=1 emits a <devourer-rxpath> line
  per canonical-SA frame with all four RX chains (A,B,C,D) of rssi/snr/evm.
  Paths C/D are populated only on the 8814AU (4T4R); the canonical two-path
  <devourer-stream>/<devourer-body> format its regex consumers key on is left
  untouched (distinct tag).
- tests/antenna_decorrelation.py: envelope-correlation matrix (Pearson on linear
  amplitude), SC/MRC combining gain over the best single chain at the 10%/1%
  outage points vs the 10*log10(N) array gain, and a verdict keyed off the worst
  pairwise |rho|. Hardware-independent --self-test validates the estimator and
  combining math against synthesised correlated Rayleigh fading.
- tests/run_antenna_decorrelation.sh: build + capture + analyse runner with an
  exact-comm cleanup trap and a uv venv; optionally starts the beacon TX.
- CLAUDE.md: document DEVOURER_RX_ALLPATHS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef josephnef added enhancement New feature or request spatial-diversity Spatial-diversity axis: exploiting idle RF chains for the single-stream link labels Jul 2, 2026
Hardening surfaced by running the #129 harness on two identical RTL8814AU
dongles (CF-938AC vs CF-960AC), which enumerate with the same VID:PID:serial.

- demo/main.cpp: DEVOURER_USB_BUS (+ optional DEVOURER_USB_PORT dotted port path)
  selects a device by USB topology when first-match VID:PID can't disambiguate
  two identical dongles. Falls back to the normal open loop when unset.
- tests/compare_8814_decorrelation.sh: drive one controlled beacon TX and
  measure every plugged RTL8814AU as RX in turn (auto-detected by bus/port), so
  the only variable between runs is the adapter's antenna front-end.
- tests/antenna_decorrelation.py: guard the estimate — a static (non-fading)
  channel (max per-chain std < MIN_FADING_STD) is reported INCONCLUSIVE rather
  than emitting a meaningless correlation, and a near-constant chain (std <
  RAIL_STD, e.g. the 8814 path-C RSSI railing at a fixed value) is flagged as a
  railed/no-signal reading. --self-test covers both.
- tests/run_antenna_decorrelation.sh: install numpy directly instead of the
  editable project (the flat tests/ layout breaks setuptools autodiscovery).
- CLAUDE.md: document DEVOURER_USB_BUS/PORT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef

Copy link
Copy Markdown
Collaborator Author

Hardware validation on two RTL8814AU dongles

Ran the harness on both plugged 8814s (CF-938AC, 2 external dipoles; CF-960AC,
4 on-PCB internal antennas) — which enumerate with an identical
VID:PID:serial
, so this also validated the new DEVOURER_USB_BUS/PORT
topology selector. One controlled beacon TX (8812AU, ch6), each 8814 as RX,
~7.5k canonical-SA frames each.

What worked end-to-end: the selector targeted the two identical devices by
bus/port (distinct per-chain fingerprints confirm different hardware was
opened), the opt-in 4-path emission delivered all four chains, and the analyser
ran clean.

Two findings that hardened the tool:

  1. A static bench beacon can't measure decorrelation. Per-chain std came out
    at ~0.15–1.8 units — the near-field beacon is a static channel with almost no
    fading, so envelope correlation just tracks quantisation noise. The harness
    now reports INCONCLUSIVE below MIN_FADING_STD instead of a bogus
    GOOD/MODERATE. A valid decorrelation number needs a fading stimulus:
    motion, varied multipath, or a mobile link. This is the key methodology note
    for the on-air validation step.
  2. 8814 path-C RSSI rails at a constant 82.00 on both devices (std ~0.02) —
    a per-path reading (gain_trsw_cd) that carries no signal here; now flagged
    as railed. Relevant to RX-side in-chip MRC on an 8814 ground station #130 (MRC wants valid per-path level/SNR).

Per-chain level fingerprint still distinguishes the adapters (means, static
channel): CF-960AC A/B/D ≈ 74–78; CF-938AC shows a wider spread (B≈60, D≈68),
consistent with only 2 driven external antennas vs 4 internal.

Next: repeat with a fading stimulus (walk the beacon / rotate the adapter /
outdoor multipath) to get a real correlation figure per antenna layout — that's
the field step feeding #130.

Pushed as dad0469 on this branch.

Running the two-8814 comparison on hardware exposed two runner bugs:

- The beacon TX was started with a fixed short sleep, so a capture could open
  its RX window before the TX was injecting and record zero frames. Both runners
  now poll the (line-buffered, via stdbuf) TX log for a confirmed inject before
  capturing, and abort with a clear message if the beacon never comes up —
  instead of silently producing an empty capture.
- run_antenna_decorrelation.sh gains USB_BUS/USB_PORT passthrough (to pin one of
  several identical adapters via the demo's topology selector) and a ROTATE=1
  stimulus mode with a start countdown, since a static bench channel is
  unmeasurable — the operator rotates the adapter to sweep each antenna's
  pattern against the fixed TX.

Both runners clean up their beacon log on exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef

Copy link
Copy Markdown
Collaborator Author

First valid decorrelation measurement: two 8814s compared (rotation stimulus)

Since a static bench beacon can't excite fading, the RX adapter was rotated /
tilted continuously
through the capture window to sweep each antenna's pattern
against the fixed beacon TX (8812AU, ch6). This measures pattern/orientation
decorrelation
under strong LOS — a proxy for (not identical to) multipath
envelope correlation, but the relative comparison between two adapters on the
same bench is robust.

Both dongles are the same RTL8814AU silicon, differing only in antenna
front-end:

Adapter Antennas frames per-chain std worst pairwise |ρ| MRC gain @1% outage Verdict
CF-960AC (round AP) 4 on-PCB internal 14348 ~4.0–4.7 0.19 (all pairs <0.2) ~13.9 dB GOOD
CF-938AC (stick) 2 external dipoles 6546 ~3.2–4.2 0.64 (A↔C coupled) ~11.1 dB MODERATE

Interpretation. The CF-938AC drives 4 RF chains from only 2 physical
antennas
, so chains A and C are strongly coupled (ρ≈0.64) — effectively ~2
independent diversity branches out of 4. The CF-960AC's genuine 4-antenna array
decorrelates well on every pair (<0.2) — closer to 4 usable branches. So for the
RX-MRC goal (this issue), the 4-internal-antenna adapter is materially better
despite identical silicon
; the 2-external-antenna stick gives roughly half the
effective diversity order.

Incidental finding. The 8814 path-C RSSI that railed at a constant 82.00
under the static beacon varies normally once the adapter moves — it was
AGC-pinned in strong static LOS, not dead hardware.

Caveat / next. Rotation-on-a-bench is angular/pattern decorrelation under
strong LOS. The gold-standard figure is a genuine NLOS multipath capture — the
plan is to relocate the beacon TX to another room/balcony (a separate host
running the beacon) and re-measure. The relative ordering (960AC > 938AC) is not
expected to change.

Tooling: tests/compare_8814_decorrelation.sh (auto-detects every 8814 by
bus/port, one beacon TX) + the ROTATE=1 mode of
tests/run_antenna_decorrelation.sh. Runner reliability fixes pushed (33f54a3).

- docs/effective-branches-rotation.md: a lab procedure for finding an adapter's
  *effective* diversity branches (not its chain count) by rotating the receiver
  against a controlled beacon. Covers why chain count != branch count, why a
  static bench can't measure it, the method, how to read the correlation matrix /
  effective-branch count / combining gain, the pitfalls that bit this measurement
  (gentle rotation lies, AGC-railed chains, beacon-not-injecting, identical-device
  selection, rotation-vs-multipath), and the worked two-dongle example.
- tests/antenna_decorrelation.py: add the effective-branch metric N_eff (the
  participation ratio of the correlation matrix) to the report and self-test.
- README: the RTL8814AU row now names both tested SKUs (CF-938AC 2 ext antennas,
  CF-960AC 4 internal) and their measured N_eff (~2.6 vs ~3.8), mirroring how the
  other chip rows cite a tested adapter, and links the new doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef josephnef merged commit a403f81 into master Jul 2, 2026
12 checks passed
@josephnef josephnef deleted the spatial-diversity-foundations branch July 2, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request spatial-diversity Spatial-diversity axis: exploiting idle RF chains for the single-stream link

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant