Skip to content

Add WiFiSenseDemo: Wi-Fi motion sensing from beamforming reports#176

Merged
josephnef merged 4 commits into
masterfrom
wifi-sense-motion-demo
Jul 5, 2026
Merged

Add WiFiSenseDemo: Wi-Fi motion sensing from beamforming reports#176
josephnef merged 4 commits into
masterfrom
wifi-sense-motion-demo

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

What

A runnable example — examples/sense/ (WiFiSenseDemo) — that turns two
supported Realtek adapters into a motion / presence sensor, with no SDR and
no cooperating AP. One binary drives a sounder and a beamformee on the
same host: the sounder injects an NDPA (the MAC hardware-generates the sounding
NDP), the beamformee returns a VHT compressed beamforming report, and the sounder
self-captures it on a concurrent RX loop (the existing beamforming self-sounding
path). The per-tone Givens angles are a beamformee-side measurement of the channel
between the two adapters, so a person moving makes them jitter frame-to-frame —
the mechanism behind published work like Wi-BFI / BeamSense.

Contents

  • src/BfReportDecode.h — header-only decoder for VHT/HT compressed
    beamforming reports (LSB-first Givens-angle unpack + dequant) and a
    MotionMeter that measures the per-tone cross-frame circular variance of the
    phase angle
    . pick_split() enforces the 802.11 codebook relationship
    b_phi = b_psi + 2 — uniquely (6,4) for a 10-bit 2×1 report. (A naive
    min-cross-frame-variance search is fooled by a trivially-constant coarse psi
    into a bit-misaligned split whose phi decodes to noise and reads as constant
    motion — a real bug found on hardware and now pinned by the self-test.)
  • examples/sense/main.cpp — the WiFiSenseDemo binary: two-adapter bring-up
    (separate libusb contexts), the sounding loop, an AdaptiveDetector
    (self-calibrating CFAR floor + rising-edge dwell + hold, so no fixed per-rig
    threshold
    ), a live σ readout, a report-stream stall watchdog, and a
    deadlock-proof shutdown.
  • examples/sense/bf_report_decode_selftest.cpp — headless ctest
    (bf_report_decode) guarding the decoder against a real captured report;
    asserts the (6,4) split.
  • examples/sense/README.md — comprehensive hands-on guide: hardware,
    placement, every knob, the decoding/detector maths, and a capture→analyse loop
    for experimenting with your own formulas.
  • src/logger.h — add a verbosity Level to Logger (default Debug,
    unchanged behaviour) so a demo can quiet the library and own the console.
  • src/jaguar1/RadioManagementModule.cpp — a channel-set trace was logged at
    error level; demoted to info.
  • docs/beamforming-victim-sensing.md — trim the "Try it" section to a
    quickstart that points at the full guide.

Validation

  • Active two-adapter mode: validated on hardware. Still → CLEAR (0 false
    alarms over a deliberate hands-away capture), hand wave → MOTION (verified by
    replaying a labelled still/wave capture through the exact detector logic). Clean
    SIGINT exit; stall watchdog exits cleanly when the beamformee stops responding.
  • Decoder: ctest self-test (green) decodes a real captured report and pins
    the Givens split.
  • Builds on macOS/Linux; the demo touches no chip-specific code.

Scope (deliberately honest)

  • A passive AP-sniffing mode (Wi-BFI style) and a single-radio --mode self
    variant are documented as unimplemented follow-ups rather than shipped untested —
    passive needs a real AP actively sounding VHT beamforming, which couldn't be
    validated against here.

🤖 Generated with Claude Code

josephnef and others added 4 commits July 5, 2026 14:15
A runnable example (examples/sense/) that turns two supported Realtek
adapters into a motion/presence sensor with no SDR and no cooperating AP.
One binary drives a sounder and a beamformee on the same host: the sounder
injects an NDPA (the MAC hardware-generates the NDP), the beamformee returns
a VHT compressed beamforming report, and the sounder self-captures it on a
concurrent RX loop (the existing self-sounding path). The per-tone Givens
angles track the channel between the two adapters, so a moving person makes
them jitter frame-to-frame.

New/changed:

- src/BfReportDecode.h — header-only decoder for VHT/HT compressed beamforming
  reports (LSB-first Givens-angle unpack + dequant) and a MotionMeter that
  measures the per-tone cross-frame circular variance of the phase angle.
  pick_split() enforces the 802.11 codebook relationship b_phi = b_psi + 2
  (uniquely (6,4) for a 10-bit 2x1 report); a naive min-variance search is
  fooled by a trivially-constant coarse psi into a bit-misaligned split whose
  phi decodes to noise.

- examples/sense/main.cpp — the WiFiSenseDemo binary: two-adapter bring-up
  (separate libusb contexts), the sounding loop, an AdaptiveDetector
  (self-calibrating CFAR floor + hysteresis, so no fixed per-rig threshold),
  a live sigma readout, a report-stream stall watchdog, and a deadlock-proof
  shutdown.

- examples/sense/bf_report_decode_selftest.cpp — headless ctest guarding the
  decoder against a real captured report; asserts the (6,4) split. Registered
  as the bf_report_decode test.

- examples/sense/README.md — comprehensive hands-on guide: hardware, placement,
  every knob, the decoding/detector maths, and a capture->analyse loop for
  experimenting with your own formulas.

- src/logger.h — add a verbosity Level to Logger (default Debug, unchanged
  behaviour) so a demo can quiet the library and own the console.

- src/jaguar1/RadioManagementModule.cpp — a channel-set trace was logged at
  error level; demote to info.

- docs/beamforming-victim-sensing.md — trim the "Try it" section to a
  quickstart that points at the full guide.

Active two-adapter mode is validated on hardware (still->CLEAR, wave->MOTION,
clean shutdown). A passive AP-sniffing mode and a single-radio self mode are
noted as unimplemented follow-ups rather than shipped untested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two portability breaks in examples/sense/main.cpp caught by the windows-cl
and build-mingw CI jobs (macOS/Linux were green):

- windows.h (pulled in via libusb.h) defines min/max macros, so std::max(...)
  failed to compile (C2589). Define NOMINMAX before the libusb include.
- MSVC/MinGW have no POSIX setenv. Add a portable set_env() wrapper
  (_putenv_s on Windows, setenv elsewhere) and route the five arming-flag
  writes through it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The build-mingw job builds an explicit target subset and (per its own
comment) every registered test's binary must be listed or ctest reports it
Not Run. The new bf_report_decode test's binary was missing, so mingw ctest
failed with "Could not find executable BfReportDecodeSelftest.exe". Add it to
the target list. (The MSVC matrix cell builds all targets, so it needs no
change.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@josephnef josephnef merged commit ef77709 into master Jul 5, 2026
15 checks passed
@josephnef josephnef deleted the wifi-sense-motion-demo branch July 5, 2026 11:41
josephnef added a commit that referenced this pull request Jul 5, 2026
…tigation, named knobs (#180)

A review of this week's merged PRs surfaced building blocks that already
exist in
the tree but weren't captured in
`docs/adaptive-link-building-blocks.md`. This
adds them, keeping the doc's conceptual + reference tone (knobs as
hooks).

- **Motion / presence sensing** (#176 WiFiSenseDemo — from the
self-sounded
beamforming report) — a new *environment* sensor class. It is the signal
that
  drives the fade-state receive-chain lever the doc already discusses.
- **Interference-mitigation actuators** — the narrowband notch
(`DEVOURER_RX_NBI`)
and the per-tone de-weighting mask (`DEVOURER_RX_CSI_MASK`) — the *act*
half
paired with the per-tone localizer's *sense* half, closing that loop
(the doc
  previously had only the sensor).
- **Named the concrete knobs** behind existing lever rows: the RX-path
mask
  (`DEVOURER_RX_PATHS`), the hopping / fast-retune channel lever
(`DEVOURER_HOP_*`), the 5/10 MHz narrowband re-clock, and the per-layer
FEC
  ladder (`DEVOURER_SVC_LADDER` / `fused-fec.md`).

Consistency pass: all links + tool paths resolve; terminology matches
the related
docs; cross-links added to `pseudo-preamble-puncturing.md` and
`beamforming-victim-sensing.md`. Also fixed the README cont-TX entry,
which
implied the carrier feeds the probe — the probe uses a decoupled beacon
feed.

Docs only; no code change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant