Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions .github/workflows/cmake-multi-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,27 +163,29 @@ jobs:
matrix:
include:
- name: jaguar1-only
flags: "-DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: jaguar1+8814
flags: "-DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: 8822b-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: 8821c-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: jaguar2-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: 8822c-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: 8822e-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: jaguar3-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: kestrel-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF"
- name: kestrel-8852b-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852C=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852C=OFF"
- name: kestrel-8852c-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852B=OFF"
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_8733B=OFF -DDEVOURER_KESTREL_8852B=OFF"
- name: rtl8733b-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF -DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF"
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -243,6 +245,7 @@ jobs:
if cmake -B build-none -DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF \
-DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF \
-DDEVOURER_JAGUAR2_8822B=OFF -DDEVOURER_JAGUAR2_8821C=OFF \
-DDEVOURER_8733B=OFF \
-DDEVOURER_KESTREL_8852B=OFF -DDEVOURER_KESTREL_8852C=OFF \
-S "${{ github.workspace }}" 2>err.log; then
echo "ERROR: configure should have failed (no chip selected)"; cat err.log; exit 1
Expand Down
56 changes: 55 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ option(DEVOURER_JAGUAR2_8822B "RTL8822BU (Jaguar2, rtl8822b, 2T2R USB)"
option(DEVOURER_JAGUAR2_8821C "RTL8811CU / 8821CU (Jaguar2, rtl8821c, 1T1R)" ON)
option(DEVOURER_JAGUAR3_8822C "RTL8812CU / 8822CU (Jaguar3, rtl8822c)" ON)
option(DEVOURER_JAGUAR3_8822E "RTL8812EU / 8822EU (Jaguar3, rtl8822e)" ON)
option(DEVOURER_8733B "RTL8731BU / RTL8733BU USB support" ON)
# Kestrel — the Wi-Fi 6 / 802.11ax generation (Realtek "G6 phl" architecture,
# vendor trees reference/rtl8852bu + reference/rtl8852cu): monitor RX, TX
# injection and channel/BW on-air validated on both chips (see
Expand Down Expand Up @@ -82,11 +83,13 @@ else()
set(DEVOURER_KESTREL OFF)
endif()
if(NOT DEVOURER_JAGUAR1 AND NOT DEVOURER_JAGUAR2 AND NOT DEVOURER_JAGUAR3
AND NOT DEVOURER_8733B
AND NOT DEVOURER_KESTREL)
message(FATAL_ERROR
"No chip support selected. Enable at least one of DEVOURER_JAGUAR1 / "
"DEVOURER_JAGUAR2_8822B / DEVOURER_JAGUAR2_8821C / DEVOURER_JAGUAR3_8822C "
"/ DEVOURER_JAGUAR3_8822E / DEVOURER_KESTREL_8852B / DEVOURER_KESTREL_8852C.")
"/ DEVOURER_JAGUAR3_8822E / DEVOURER_8733B / DEVOURER_KESTREL_8852B "
"/ DEVOURER_KESTREL_8852C.")
endif()
if(DEVOURER_PCIE)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
Expand Down Expand Up @@ -388,6 +391,21 @@ if(DEVOURER_JAGUAR3_8822E)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_JAGUAR3_8822E=1)
endif()

# RTL8733B is an 802.11n HALMAC 87xx part, not a Jaguar3 variant. Keep the
# backend isolated: it has its own power/FW/MAC/PHY path, descriptors, and
# conservative capabilities.
if(DEVOURER_8733B)
target_sources(devourer PRIVATE
src/rtl8733b/Rtl8733bDevice.cpp src/rtl8733b/Rtl8733bDevice.h
src/rtl8733b/Rtl8733bBringup.cpp src/rtl8733b/Rtl8733bBringup.h
src/rtl8733b/Halmac8733bMac.cpp src/rtl8733b/Halmac8733bMac.h
src/rtl8733b/Phy8733b.cpp src/rtl8733b/Phy8733b.h
src/rtl8733b/Rtl8733bUsbIds.h
hal/hal8733b_fw.c hal/hal8733b_fw.h
hal/hal8733b_tables.c hal/hal8733b_tables.h)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_8733B=1)
endif()

# --- Kestrel (Wi-Fi 6 / 802.11ax: RTL8852BU / RTL8852CU) ---
# Fourth generation, ported from the vendor "G6 phl" trees (reference/
# rtl8852bu, reference/rtl8852cu): mac_ax MAC/FW plane, halbb PHY tables,
Expand All @@ -408,6 +426,16 @@ if(DEVOURER_KESTREL)
target_compile_definitions(devourer PUBLIC DEVOURER_HAVE_KESTREL=1)
endif()

# rtl8733bprobe — staged diagnostic USB probe used alongside normal factory
# dispatch. It never substitutes for the production IRtlDevice path.
if(DEVOURER_8733B)
add_executable(rtl8733bprobe
examples/rtl8733bprobe/main.cpp
)
target_link_libraries(rtl8733bprobe PUBLIC devourer PRIVATE PkgConfig::libusb)
target_include_directories(rtl8733bprobe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/examples/common)
endif()

if(DEVOURER_KESTREL_8852B)
target_sources(devourer PRIVATE
# Generated by tools/extract_8852b_fw.py (two NICCE images, cut-selected
Expand Down Expand Up @@ -1004,6 +1032,32 @@ target_link_libraries(AdapterCapsSelftest PRIVATE devourer)

add_test(NAME adapter_caps_derive COMMAND AdapterCapsSelftest)

if(DEVOURER_8733B)
add_executable(Rtl8733bEfuseSelftest
tests/rtl8733b_efuse_selftest.cpp
)
target_link_libraries(Rtl8733bEfuseSelftest PRIVATE devourer)
add_test(NAME rtl8733b_efuse_parse COMMAND Rtl8733bEfuseSelftest)

add_executable(Rtl8733bPhyTableSelftest
tests/rtl8733b_phy_table_selftest.cpp
)
target_link_libraries(Rtl8733bPhyTableSelftest PRIVATE devourer)
add_test(NAME rtl8733b_phy_tables COMMAND Rtl8733bPhyTableSelftest)

add_executable(Rtl8733bRxParseSelftest
tests/rtl8733b_rx_parse_selftest.cpp
)
target_link_libraries(Rtl8733bRxParseSelftest PRIVATE devourer)
add_test(NAME rtl8733b_rx_parse COMMAND Rtl8733bRxParseSelftest)

add_executable(Rtl8733bTxDescSelftest
tests/rtl8733b_tx_desc_selftest.cpp
)
target_link_libraries(Rtl8733bTxDescSelftest PRIVATE devourer)
add_test(NAME rtl8733b_tx_desc COMMAND Rtl8733bTxDescSelftest)
endif()

# Headless guard for the per-UE RX attribution seed (src/cell/UeRxAttribution.h)
# — TA extraction over the addr2-carrying frame types, the per-TA windowed
# aggregate folding, drain semantics, and the bounded-table eviction accounting
Expand Down
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

**The Realtek Wi-Fi driver that simply devours its competitors.**

Devourer is a userspace Wi-Fi driver for Realtek's 802.11ac and 802.11ax
Devourer is a userspace Wi-Fi driver for Realtek's 802.11n, 802.11ac and 802.11ax
USB adapters —
the cheap, everywhere-available dongles that power most long-range FPV video
links. It talks to the chip directly over libusb: no kernel module, no DKMS
tree to patch every time your kernel updates, no root filesystem to taint.
Build one static library, link it, and you have raw monitor-mode RX and
packet injection on four generations of Realtek silicon, from a single API.
packet injection across five Realtek hardware backends, from a single API.

It is the [OpenIPC](https://openipc.org) project's driver of choice for
long-range digital video links.
Expand Down Expand Up @@ -53,10 +53,11 @@ long-range digital video links.
worth it, and the two ends migrate under an authenticated
ground-proposes/drone-commits protocol that cannot split-brain
([how](docs/adaptive-channel-migration.md)).
- **Narrowband modes the kernel can't do.** 5 and 10 MHz channels on every
supported generation — including the decade-old RTL8812AU and RTL8814AU the
vendor never gave narrowband — half/quarter the bandwidth, more range from
the same power ([how](docs/narrowband.md)).
- **Narrowband modes the kernel can't do.** 5 and 10 MHz channels on the
backends that advertise them — including the decade-old RTL8812AU and
RTL8814AU the vendor never gave narrowband — half/quarter the bandwidth,
more range from the same power. RTL8733B has an experimental path that stays
unadvertised pending RF validation ([how](docs/narrowband.md)).
- **Hardware time, for coordinating radios.** Every received frame is stamped
with the chip's microsecond MAC clock (TSF) on every generation, and the
64-bit timer reads back directly — the primitive multi-radio setups need.
Expand Down Expand Up @@ -101,6 +102,7 @@ Bandwidth cells are devourer's measured on-air TX throughput (Mbps, HT MCS7,
| **RTL8822CU** | 2T2R + BT | — | — | — | — | not benchmarked (`0bda:c82c`). 5/10 MHz capable |
| **RTL8812EU** | 2T2R | ‡ | 51 | 47 | — | LB-LINK BL-M8812EU2 (`0bda:a81a`); bare 5 GHz FPV module. 5/10 MHz capable. ‡ 2.4 GHz TX airs energy but no receiver decodes it — the vendor kernel driver behaves identically on this module ([quirks](docs/8822e-quirks.md)) |
| **RTL8822EU** | 2T2R + BT | — | — | — | — | not benchmarked. 5/10 MHz capable |
| **RTL8731BU / RTL8733BU** | 1T1R | — | — | — | — | `0bda:f72b` cut D: monitor RX and legacy/HT raw TX validated on 2.4/5 GHz at 20/40 MHz; not benchmarked. `0bda:b733` is recognized from the vendor ID table but lacks a physical test. [Status and test limits](docs/rtl8733b.md) |
| **RTL8821CE** (PCIe) | 1T1R + BT | — | — | — | — | Radxa X4 onboard Wi-Fi (`10ec:c821`); not benchmarked |
| **RTL8852BU** (11ax) | 2T2R + BT | 43 | 36 | 33 | — | TP-Link Archer TX20U Nano (`35bc:0108`); Wi-Fi 6, dual-band. 5/10 MHz capable; HE ER SU + DCM extended range |
| **RTL8832BU** (11ax) | 2T2R | — | — | — | — | Wi-Fi-only SKU of the 8852B die; rides the 8852BU code path. Not benchmarked. 5/10 MHz capable; HE ER SU + DCM extended range |
Expand Down Expand Up @@ -128,6 +130,11 @@ gated off with on-air evidence — they degrade TX under the fixed-power model.
The 8852A-family (e.g. RTL8832AU) stays out of scope — its only vendor driver
is a frozen 2021 drop.

The RTL8733B backend covers the 1T1R 802.11n RTL8731BU/RTL8733BU family with
20/40 MHz monitor RX and raw injection on 2.4 and 5 GHz. It intentionally does
not advertise VHT, LDPC, SGI, STBC, or experimental 5/10 MHz operation; see
[the validation record](docs/rtl8733b.md) for the tested and deferred matrix.

> Heads up — some Realtek sticks ship in "ZeroCD" mode and first enumerate as
> a USB flash drive holding a Windows installer (`0bda:1a2b` is the canonical
> offender). If the device won't open, check `lsusb`; `usb_modeswitch` flips
Expand Down Expand Up @@ -176,6 +183,7 @@ the `env:` tags in [`src/DeviceConfig.h`](src/DeviceConfig.h).
| `chanmig` / `chanscout` | evidence-driven channel migration: the protocol, and the survey adapter that feeds it |
| `dwelltx` | dwell-1 hopping data plane on the standard Linux driver |
| `kestrelprobe` | Wi-Fi 6 (RTL8852B/C) bring-up probe, layer by layer |
| `rtl8733bprobe` | RTL8731BU/RTL8733BU identity, power, firmware and PHY diagnostics |
| `timesync` | over-the-air clock distribution (master / slave / UE roles) |
| `sense` | Wi-Fi motion sensing from beamforming reports |
| `doctor` | adapter-health triage → HEALTHY / SUSPECT / FAILING |
Expand All @@ -184,8 +192,9 @@ the `env:` tags in [`src/DeviceConfig.h`](src/DeviceConfig.h).

All chips compile in by default; per-chip CMake options (`DEVOURER_JAGUAR1`,
`DEVOURER_8814`, `DEVOURER_JAGUAR2_8822B`, `DEVOURER_JAGUAR2_8821C`,
`DEVOURER_JAGUAR3_8822C`, `DEVOURER_JAGUAR3_8822E`, `DEVOURER_KESTREL_8852B`,
`DEVOURER_KESTREL_8852C`) drop unneeded firmware and tables — an 8812AU-only
`DEVOURER_JAGUAR3_8822C`, `DEVOURER_JAGUAR3_8822E`, `DEVOURER_8733B`,
`DEVOURER_KESTREL_8852B`, `DEVOURER_KESTREL_8852C`) drop unneeded firmware and
tables — an 8812AU-only
`rxdemo` is ~1.6 MB against ~6.3 MB with everything on, and dropping just the
two Wi-Fi 6 dies takes it to ~4.2 MB (their verbatim-vendored halbb/halrf plane
is the single largest contributor). `DEVOURER_PCIE` (default OFF, Linux-only)
Expand Down Expand Up @@ -224,7 +233,7 @@ auto dev = driver.CreateRtlDevice(handle, ctx, lock, cfg);
Anything that changes mid-session is a runtime setter on the device:
`SetTxMode`, `SetTxPowerOffsetQdb`, `SetRxPathMask`, `FastRetune`, ...
The device class is chosen automatically from the chip behind the handle;
one `IRtlDevice` interface covers all four generations.
one `IRtlDevice` interface covers all five hardware backends.

## Going deeper

Expand Down Expand Up @@ -302,9 +311,10 @@ per-chip quirks notes at the bottom.
ground-proposes/drone-commits exchange, and
[its validation](docs/channel-migration-validation.md) is the failure matrix
every row of which converges without split-brain.
- [Narrowband](docs/narrowband.md) — 5/10 MHz channels across all three
generations: the baseband re-clock, the per-chip register machinery, and the
walls (RF re-latch edges, per-die clock coupling, the 5 MHz/5 GHz CFO limit).
- [Narrowband](docs/narrowband.md) — 5/10 MHz channels across the Jaguar
generations, plus the unadvertised RTL8733B experiment: the baseband
re-clock, the per-chip register machinery, and the walls (RF re-latch edges,
per-die clock coupling, the 5 MHz/5 GHz CFO limit).
- [Spectrum sensing](docs/rx-spectrum-sensing.md) — RX energy sweeps down to
5 MHz bins: a coarse per-bin H(f) from the dongle itself.
- [Pseudo preamble puncturing](docs/pseudo-preamble-puncturing.md) — how close
Expand Down
11 changes: 8 additions & 3 deletions docs/narrowband.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,21 @@ band's bursts).

## Using it

`DEVOURER_NB_BW=5` or `=10` on the demos selects narrowband; the library exposes
it as `CHANNEL_WIDTH_5` / `CHANNEL_WIDTH_10` on `SelectedChannel`, and

`DEVOURER_NB_BW=5` or `=10` on the demos selects narrowband; the library exposes
it as `CHANNEL_WIDTH_5` / `CHANNEL_WIDTH_10` on `SelectedChannel`, and
`IRtlDevice::GetAdapterCaps().narrowband_ok` reports whether the running chip
supports it. Support today: **Jaguar2 (8822B/8821C) and Jaguar3 (8822C/8822E)**
fully, and **Jaguar1 on the 8812AU/8811AU and the 8814AU** — every generation.
The 8821A is the one exclusion (its DAC-clock divide starves TX; see the walls).

RTL8731BU/RTL8733BU has an **experimental, unadvertised** 5/10 MHz path. It
ports the later `rtl8733bu-20230626` monitor-mode patch exactly: configure the
RF as 20 MHz, then apply `0x9b0`/`0x9b4`/`0x9f0`/`0x81c` after the RF writes.
The vendor HAL capability table itself declares only 20/40 MHz, and the patch
calls this a dirty workaround for a no-RF-output failure. Register readback has
passed on one `0bda:f72b`; `narrowband_ok` deliberately remains false until SDR
occupied-bandwidth and independent narrowband decode tests pass.

Test scripts: `tests/jaguar2_narrowband_sdr.sh` and
`tests/jaguar1_nb_divide_sweep.sh` (SDR occupied-bandwidth),
`tests/narrowband_cross_rx.sh` (cross-generation decode),
Expand Down
Loading
Loading