Devourer's RTL8733B backend supports the Wi-Fi function of the 1T1R, dual-band RTL8731BU/RTL8733BU family. It is based on the vendor's HALMAC 87xx path rather than any Jaguar or Kestrel implementation.
The supported USB identities are:
0bda:f72b, the Wi-Fi-only identity used by the tested RTL8731BU module.0bda:b733, the combo-module Wi-Fi function listed by the vendor driver.
An opened device must also report RTL8733B chip ID 0x16; a matching PID is
not accepted as sufficient identity.
The backend advertises one spatial stream, 2.4 and 5 GHz, legacy OFDM and HT MCS0–7 at 10, 20 or 40 MHz. It also accepts all four long-preamble CCK rates on 2.4 GHz at 20 MHz; CCK is rejected on 5 GHz and off 20 MHz. Raw HT TX is deliberately forced to BCC. SGI, STBC, LDPC, VHT, 80 MHz, CCK short preamble, Bluetooth/coexistence controls and 5 MHz are not advertised — 5 MHz is refused outright because its BB mode airs no packets and has shown a continuous-carrier failure mode ("Narrowband status" below); 10 MHz is qualified.
That conservative surface follows both the hardware results and the vendor
HAL. The pinned 2023 HAL declares 802.11b/g/n, one TX and RX spatial stream,
and only BW_CAP_20M | BW_CAP_40M. It sets TX-LDPC true but RX-LDPC false;
devourer still reports both false because independently witnessed raw HT TX
only became reliable after clearing the global HT/VHT LDPC enable bits. The
newer 2024 vendor tree has the same 1SS, HT and 20/40 MHz declarations, and its
Fix VHT flags commit removes the accidentally enabled VHT build flag.
The claims below rest on two physical units, and each number names its
unit. The original evidence set — everything in this document not explicitly
attributed otherwise — comes from a bare unbranded 1T1R RTL8731BU module,
0bda:f72b, RTL8733B chip ID 0x16, cut D, on USB high speed, witnessed by
an RTL8812AU in monitor mode.
A second sample, an RTL8733BU combo module (LB-LINK BL-M8733BU2-L,
0bda:b733, EFUSE chip id 0x8129, also cut D, TSSI-offset PG mode,
rfe_type 0x04, USB high speed), re-ran the core evidence set and agreed
everywhere it was pointed:
- Adapter doctor HEALTHY on first claim; 10/10 lifecycle-soak cycles over channels 6 and 36, thermal raw 31–33 throughout.
- 3/3 true VBUS cold boots (uhubctl per-port power cycle) HEALTHY, EFUSE stable, each cold rep decoding a radiation-verified canonical-SA flood.
- Witnessed per-rate delivery, ~2.3–3.6k frames per cell, every cell decoded at its requested modulation: on ch6, CCK 1/2/5.5/11 Mbps at 76–90%, 6 Mbps OFDM 62%, HT20 MCS0/4/7 flat 71–72%, HT40 MCS0/4 66–70% (ch6 is this bench's busy ambient band — CSMA deferral, not a transmitter ceiling); on ch36, 6 Mbps 79%, HT20 MCS0/4/7 and HT40 MCS0/4/7 all 94–97%.
- HT40 MCS7 on ch6 read 0% at an RTL8822BU ground station and 81% at an RTL8812CU one — the 8822BU unit is delivery-qualified at 20 MHz only and its 40 MHz cliff sits inside the HT ladder (MCS5 52%, MCS6 2%), so that zero measured the witness, not the transmitter. Qualify the ground at the width under test, not just the rate.
- On-air TX throughput (
tests/bench_onair.py, SDR duty × PHY rate, MCS7/20, 1500 B singles): 62 Mbps on ch6 (96% duty), 50 Mbps on ch36 and ch149 (77% duty). The ~20-point 5 GHz duty gap against 2.4 GHz is repeatable across both 5 GHz bands and unexplained — measure it before trusting any higher 5 GHz number. - Occupied bandwidth (
tests/sdr_obw.py, frame-gated average PSD, equal-tail 99%-power edges): MCS7 at 20 MHz reads OBW99 17.5 MHz on ch36 / 17.9 MHz on ch13, −20 dBr width 17.9 MHz (ch36); at 40 MHz, OBW99 37.7 MHz on ch36 (−20 dBr 42.6 MHz) and 40.0 MHz on ch13 (ambient-inflated, −20 dBr pinned at the 50 MHz span), each at its correct HT40 centre (5190 / 2462 MHz) — the 40 MHz centre-channel table checks out on both bands. The full spectral mask, EVM and absolute output power remain unmeasured. - 120 s sustained 5 GHz TX (ch36, MCS4/20, ~500 fps) delivered 96.5% with no
decay, with the module's Bluetooth function enumerated and idle (
btusbbound to interfaces 0/1 throughout every result above). BT idle is the measured state; BT under active traffic is not, and BT itself stays out of scope — the Wi-Fi function is interface 2, and the demos' claim helpers select it (find_wifi_interface).
The following was exercised through the normal WiFiDriver / IRtlDevice
factory path:
- USB discovery and endpoint selection; card enable and disable; firmware download, checksum and ready handshakes; HALMAC/MAC, EFUSE, BB and RF setup.
- Stable physical EFUSE reads, permanent MAC extraction, firmware-health and thermal telemetry.
- Ambient monitor RX on channels 6 and 36, including descriptor aggregation, PHY status, CRC reporting and clean shutdown.
- Independently captured raw TX on 2.4 and 5 GHz: legacy OFDM and HT MCS0–7, 20 MHz and HT 40 MHz. BCC was used throughout. On 2.4 GHz, an RTL8812AU witness also decoded long-preamble 1, 2, 5.5 and 11 Mbps CCK with valid FCS.
- Repeated warm initialization, concurrent RX/TX, bounded sustained TX, teardown during active RX/TX, malformed-input rejection and post-test adapter-doctor checks.
Per-rate delivery, witnessed by an RTL8812AU (0bda:8812) in monitor mode on
channel 6, 300 submitted frames per rate. Counted per frame from the witness's
own RX events, so these are exact rather than sampled:
| requested | delivered | decoded as | FCS errors |
|---|---|---|---|
| 1 Mbps CCK | 298 / 300 | 1 Mbps | 0 |
| 2 Mbps CCK | 300 / 300 | 2 Mbps | 0 |
| 5.5 Mbps CCK | 300 / 300 | 5.5 Mbps | 0 |
| 11 Mbps CCK | 294 / 300 | 11 Mbps | 0 |
| 6 Mbps OFDM | 300 / 300 | 6 Mbps | 0 |
| HT MCS0 | 270 / 300 | MCS0 | 0 |
| HT MCS7 | 277 / 300 | MCS7 | 0 |
Every frame decoded at the rate it was requested at, and no captured frame carried an FCS error at any rate. These are single probes, so per this repo's own guidance they are worth about +/-3 points each.
The two HT readings sitting below the legacy ones are not a transmitter
effect, and the witness was qualified rather than assumed
(tests/ground_station_qualify.sh exists for exactly this question). Sweeping
the RTL8812AU witness across the HT ladder, 300 frames each:
| rate | delivered | witness RSSI |
|---|---|---|
| MCS0 | 270 / 300 | 57.8 |
| MCS1 | 300 / 300 | 65.0 |
| MCS3 | 300 / 300 | 65.3 |
| MCS5 | 300 / 300 | 65.2 |
| MCS6 | 296 / 300 | 65.1 |
| MCS7 | 277 / 300 | 60.3 |
The ladder is flat at 100% through MCS5, so the witness is nowhere near its modulation cliff and is fit to measure this rate range — a receiver measuring itself would fall off progressively at the top, not dip at both ends. MCS0 is the most robust rate in the set, so its being the lowest reading rules out a cliff at either end. Both low readings also came with 5-7 units less RSSI than the flat middle, which places the variation in the per-run link conditions rather than in modulation. Treat the whole HT column as one flat band around 90-100% until something moves it by more than the probe noise.
A mixed run alternating all eight rates (1/2/5.5/11 Mbps CCK, 6 Mbps OFDM,
MCS0/4/7) submitted 250 frames, crossing the CCK/OFDM rate-class boundary on
every frame. The witness decoded all eight rate_hw values on air with valid
FCS, every frame at 20 MHz with STBC and LDPC clear, matching the forced-BCC
contract.
USB TX aggregation is ported and is the one host-side lever that matters on
a small SoC. send_packets packs up to three [txdesc][frame] blocks into one
bulk-OUT URB (DEVOURER_TX_USB_AGG, default 0 = off = byte-identical), the
HalMAC 3-descriptor rule that MAC init had already programmed as
BLK_DESC_NUM in DWBCN0_CTRL[7:4]. The block count rides the first
descriptor's dword7[31:24] and is written before the checksum, which folds
over byte 0x1f.
The cost it removes is host CPU, not air time. One bulk submission measured ~248 µs of CPU on the CV610 craft against ~22 µs on x86 — the same transfer, an 11× ratio — and a profile attributes ~87% of it to the kernel USB submit/completion path, with the per-byte term below noise on ARM. Packing 3:1 therefore removes two submissions in three: on the craft, 248 → 148 µs per frame and 43.0% → 26.7% of one core at ~1750 fps, frame rate unchanged (1733 → 1800). On x86 the same A/B reads 21.5 → 10.6 µs.
The failure mode this was checked against is the 8822BU's: wrong packing makes
the TXDMA re-air block 1 agg_num times, which frame counts cannot see. Each
frame was therefore stamped (DEVOURER_TX_QOS_DATA) and the stamps counted at
the witness (DEVOURER_RX_PCTR): 31552 receptions, 31551 distinct counters,
ratio 1.00 where re-airing would read 3.00. Note GetTxStats().submitted
counts URBs rather than frames, so an aggregated session reports about a third
— accounting, not throughput.
The boundary shim is verified separately, because a fixed-payload sweep
never reaches it. When a packed total lands on an exact bulk-MPS multiple the
planner inserts the 8-byte first-block PKT_OFFSET reserve (the sync bulk path
has no ZLP), and on the 8822BU a reserved first block is precisely what made
the TXDMA re-air block 1. For three blocks at USB HS that happens when the MPDU
length is ≡ 472 mod 512 — measured exactly, urb_bytes 1544 / 3080 / 4616 at
payloads 472 / 984 / 1496, all n x 512 + 8. Stamped at the witness with the
shim engaged: 15027 shim URBs, 44032 receptions, 44032 distinct, ratio 1.00
against 1.00 for the no-shim control. This part's block walker does account the
reserve.
RX aggregation is capped at 12 KiB (rtl8733b::kRxAggregateBytes8733b) because
the vendor default of 20 KiB exceeds one bulk-IN URB: a 60-second high-traffic
receive run had xHCI split such an aggregate, leaving a descriptor tail and its
body in separate completions. The RX loop floors its URB size at the same
constant, so DEVOURER_RX_URB_BYTES cannot reintroduce the straddle from the
other side. Five repeated high-traffic channel-36 runs with the cap in place
completed without malformed descriptors, aggregate mismatches or parser aborts.
A separate ten-cycle channel-6/channel-36 warm lifecycle run passed EFUSE,
firmware-ready, initialization, thermal, rollback and card-disable gates.
tests/rtl8733b_lifecycle_soak.sh makes that warm test reproducible and labels
it explicitly as distinct from a true VBUS cycle.
Thermal readings are telemetry, exactly as on every other generation:
GetThermalStatus() exposes the RF 0x42 meter against the EFUSE baseline,
InitWrite logs one bring-up snapshot, and DEVOURER_THERMAL_POLL_MS emits
thermal events at the caller's cadence. Nothing in the send path reads the
meter — a per-frame read would be several USB control transfers per frame, and
the meter is a PA-bias tracking index rather than a calibrated junction
temperature, so it is not sound input for a TX gate
(docs/warm-tx-degradation.md). During the mixed CCK/OFDM witness run the raw
code stayed at 32–33 against baseline 32 across 650 submitted frames; 636 were
captured with valid FCS across all five requested rates. A separate 1,000-frame
run alternating CCK and OFDM submitted 1,000/1,000; the witness captured 660
canonical frames across both rates, all with valid FCS, while the same 32–33
thermal range held. Both runs are rate-coverage and thermal evidence only —
neither says anything about TX-power tracking, and at these deltas (+0 to +1)
the two thermal-compensation curves are the same bits anyway.
On a unit whose EFUSE is in TSSI-offset PG mode — the tested RTL8731BU is —
closed-loop TSSI is the TX-power control, so it runs by default. It is not
optional there: the flat fallback index (kSafeTxAgcIndex8733b) is a
conservative bring-up value, and on air it runs cold enough that HT rates do
not survive the link. Measured with the witness, 300 frames submitted at MCS7
with the flat index: zero captured, twice. A unit whose EFUSE carries no TSSI
calibration has nothing to drive the loop and takes the flat path.
Because the loop is the power control, the runtime knob is the loop's target.
IRtlDevice::SetTxPowerOffsetQdb shifts every per-rate target that survives
the safe ceiling — cap first, then shift, so the calibrated per-rate spread
comes through the move intact — and writes it as the five packed dwords at
0x3a00..0x3a10, in place, with tracking left enabled. That is the same
shape fast_retune uses for its per-channel rewrite (#389); the alternative,
a TSSI disable/re-enable pair, costs ~165 ms and buys nothing here.
The capability report is the dBm model — index_max = 0, one qdB per step —
over [-128, +127]. Offset 0 is kSafeTssiTargetQdbm8733b (16 dBm), a
first-light clip sitting at or below this part's factory targets (18–20 dBm at
2.4 GHz, 16–19 at 5 GHz).
That range is asymmetric, and only one end is a hardware limit. The
0x3a00..0x3a10 bytes hold a signed int8 offset from the 64 qdBm anchor, so
the field itself spans [−128, +127] — targets from −16 dBm to +47.75 dBm.
Both ends are that field, and neither is re-clamped at something softer — the same clamped-only-at-the-hardware-rail answer Jaguar1 (±126) and Jaguar3 (±127) give. Where the chip stops responding is measured and written down below rather than enforced in the setter, because on this part both ends stop responding well inside the field and at points only a bench can find.
The positive half is deliberately not re-clamped at the factory table.
src/TxPower.h states for every family that headroom above the generated
table is the operator's call, and a per-unit EFUSE trimmed too cold is exactly
what a bench calibration exists to correct — clamping here would make this the
one backend where a measured operating point cannot be commanded. What that
costs is measured below.
The offset is sticky: a full SetMonitorChannel re-folds it against the new
channel's targets, and FastRetune carries it into the hop's in-place rewrite.
The flat-index and per-rate-diff knobs are not ported. The flat index has no measured dB-per-step slope on this part and, as above, cannot carry HT.
Measured on air. Two independent 6-point passes at ch36 against an
RTL8812AU witness (chip-RSSI ground station — the SDR saturates at this range,
see tests/txpwr_offset_onair.sh): the lever is monotone and worth
14.2 / 14.8 dB of received power for the full 16 dB of command, overall
slope 0.222 / 0.231 dB per qdB against the 0.25 nominal. Its adversarial
counterpart, in the same breath: the step is not constant. The bottom 12 qdB
delivered 0.125 and 0.126 dB/qdB — the only structure that reproduced
exactly across both passes — while everything above −52 qdB ran 0.233..0.242
and the mid-range scattered 0.219..0.252 between passes. Near the floor the
loop returns about half the dB commanded, and saturated_low does not warn
about it (that clamp fires only at −64). TxPowerCaps::step_measured
therefore stays false, the same call the 8822E gets for the same reason: a
controller should calibrate its own dB-per-qdB, or close the loop on the
ground's RSSI.
The control that makes those numbers readable: the pre-change binary, in the
same session and geometry, measured flat — 0.3 dB across the same 64 qdB of
command, because it had no actuator to move. One unit, one witness, near-field
geometry, integer-quantised RSSI, no SDR. Harnesses:
tests/rtl8733b_txpwr_regcheck.sh (registers, including that null control),
tests/txpwr_offset_onair.sh (slope), tests/rtl8733b_txpwr_selftest.cpp
(the offset math, in ctest).
An earlier cut of this work clamped the offset at −64 qdB, reasoning that a target below 0 dBm was meaningless. Sweeping past it says otherwise (MCS0, ch36):
| offset | target | witness RSSI | witness EVM |
|---|---|---|---|
| −32 | 8.0 dBm | 69.40 | −60.8 |
| −64 | 0.0 dBm | 62.07 | −59.3 |
| −80 | −4.0 dBm | 54.80 | −54.0 |
| −96 | −8.0 dBm | 52.96 | −53.1 |
| −112 | −12.0 dBm | 52.97 | −52.8 |
| −128 | −16.0 dBm | 52.99 | −52.8 |
Power keeps falling past the 0 dBm target — another 7.3 dB between it and −4 dBm — with no sign wrap, and pins from about −96 qdB, where three successive rungs agree within 0.03 dB. EVM softens from −59 to −53 across the whole extension and the link stays decodable throughout. So usable travel is ~23 dB below the clip rather than the ~16 dB the first cut allowed, which matters for near-field bench work and for a link that wants to sit quiet.
Sweeping the other way — up from the clip, MCS0 at ch36, with the witness reporting EVM beside RSSI:
| offset | witness RSSI | witness EVM | witness SNR |
|---|---|---|---|
| 0 | 75.4 | −62.0 | 62.0 |
| +16 (top of the PG table) | 78.2 | −50.2 | 63.7 |
| +32 | 84.1 | −18.0 | 57.9 |
| +48 | 83.7 | −18.0 | 58.1 |
| +64 | 83.8 | −18.0 | 58.2 |
The +16 rung is real gain, +2.8 dB, though the constellation has already given
up 12 dB of EVM to get it. The +32 rung is not gain: the witness hears
8.7 dB more energy while EVM collapses to −18, and +48 and +64 change nothing
at all — RSSI and EVM both pinned. That is the PA in hard compression, and
SNR never saw it (58..64 throughout), which is the failure mode
docs/bench-testing-near-field.md exists to warn about: strong RSSI plus poor
EVM means back power off, the opposite of the weak-link response.
Two things follow. The vendor's PG table lands about where this part stops being linear, so +16 qdB is the edge of usable overdrive even though the API allows +127 — a caller sweeping for its own operating point should watch EVM, not RSSI, and stop where EVM turns. And the negative half is clean by comparison: EVM sits flat at −58..−61 across all 16 dB of backoff.
The chip keeps two thermal-compensation curves, one for CCK and one for
OFDM/HT. The table is chosen once per channel set from the configured TX mode
and then left alone, which is what the vendor driver does —
_halrf_tssi_set_tmeter_tbl_8733b is only ever called from full TSSI setup,
keyed on phydm_get_tx_rate at that instant, and never re-selected at runtime.
There is deliberately no runtime switch, and the reason is worth recording so nobody adds one back.
The two curves are bit-identical for thermal deltas 0..+17 and first differ
at +18 — the swing ramp starts there, and the words either side of the
baseline are zero in both tables. A five-minute max-duty MCS7 soak on the
validation unit plateaued at +8 after two minutes and stopped climbing,
less than half the delta needed for the tables to differ by a single entry. So
on this silicon a runtime switch would spend 84 ms — 136 USB register round
trips, inside send_packet, capping a mixed-rate stream near 11 fps — to
install a table bit-identical to the one already loaded.
An opt-in knob for it was written, measured on air and then deleted on that evidence. Room temperature agreed too: 300 frames of 1 Mbps CCK delivered 300/300 with the OFDM table loaded and 299/300 with the CCK table, zero FCS errors either way. OpenIPC/devourer#389 has the cost breakdown and a validated in-place alternative (13.8 ms, tracking left enabled) — if a future board does reach +18, that is the implementation to revisit, not the teardown/rebuild.
A related property of the closed loop, worth knowing before reading power numbers from a fast rate-switching run: TSSI needs settling time. Alternating CCK and OFDM at roughly 9 ms per frame leaves CCK airing about 5 dB above its settled level, and the same stream paced to 86 ms per frame lands on the settled value. That is the tracking loop converging, not a descriptor or table-programming effect.
SetCcaMode(false) — the universal carrier-sense-enabled default, and the
state this backend's MAC bring-up leaves programmed — succeeds as a no-op.
SetCcaMode(true) (DEVOURER_DIS_CCA) throws: the HALMAC 87xx carrier-sense
gate has not been located and measured here, and the backend refuses rather
than silently pretending. The refusal leaves the session running.
The construction-time form of the same knob — DeviceConfig::tuning::disable_cca,
which every other generation applies during bring-up — cannot be honoured
either, and bring-up emits one warning saying so rather than dropping it
silently or failing to initialise. Silence is the only outcome that would let a
refused knob look like a granted one; a hard failure would be a harsh answer to
a request a caller may be making only through an inherited environment.
These results have not been claimed:
- Absolute output power is not measured and stays out of reach on this bench: no calibrated power meter exists and the B210 is a relative instrument (the mask, EVM, per-rate level and stability measurements in Hardware validation are all relative or witness-side). The runtime power lever's slope is a relative witness-RSSI measurement for the same reason: it says the lever moves ~14 dB, not what any rung radiates in dBm. Also instrument-limited: the 802.11n −40 dBr mask far points, the 40 MHz stitched mask beyond its −20 dBr width (the mask script's own controls refuse every workable gain), and CCK EVM (the witness reports none). All SDR-domain numbers are from the b733 sample alone — the f72b unit is not on the bench, so no RF-domain quantity has a second sample.
- The TX-power offset's dB-per-qdB is not constant across its advertised range
(0.125 in the bottom 12 qdB vs 0.233..0.242 above −52), so
step_measuredstays false and a controller must calibrate its own slope. RaisingkSafeTssiTargetQdbm8733babove 16 dBm is deferred — that wants a conducted measurement, not a witness receiver. - Narrowband is 10 MHz only: qualified by SDR occupied bandwidth and two-way peer cross-decode on both bands, while the 5 MHz BB mode airs no packets across the whole divider code space — with a continuous-carrier failure mode observed warm — and is refused ("Narrowband status" below). Narrowband spectral mask and EVM remain unmeasured like every other RF-domain quantity here.
- Bluetooth is out of scope. The combo module's Wi-Fi function is validated with the BT function enumerated and idle (see Hardware validation); BT under active traffic is unmeasured, and no coexistence controls are ported.
- Secure-firmware variants were not exercised. Both units are cut D and use the normal NIC image.
- SGI remains disabled: a descriptor with its short-GI bit set submitted successfully, but an independent RTL8812AU decoded both broadcast and unicast probes as long GI. STBC and LDPC remain rejected. ACK/BlockAck response, A-MPDU and throughput were not independently validated and remain unadvertised or unsupported by this backend.
- Fast retune is now ported and independently witnessed (intra-band,
same-width;
src/rtl8733b/CLAUDE.mdhas the measured contract and its counterparts): channel-state readback parity 7/7 hops, a 299/300 post-hop burst decoded by an RTL8812AU witness, ~55 ms call / ~10 ms p50 radio-live on this unit. The channel-bucketed TSSI-DE offsets are rewritten in place across the hop with tracking live (the first cut skipped them — caught in review): DE readback parity 22/22 across ch1→ch13 hops, tracking-enable field held at 7 throughout, settle p50 unchanged at 10.4 ms. Radiated power across a hop remains SDR-gated like every other RF-domain quantity here. - A vendor-kernel-driver cross-test was attempted with the 2024 source on Linux
6.14, but its module did not build: its Makefile omitted required include
paths, and after supplying the main include path compilation stopped on an
incompatible
MODULE_IMPORT_NSuse and a missingplatform_ops.h. No vendor module was loaded, so no vendor-driver parity result is claimed.
A second RTL8731BU f72b unit — the same chip the surviving DUT is, so it
would have covered the same README row — passed early identity, EFUSE and
firmware bring-up checks, then stopped enumerating and overheated while
attached to a custom power supply. It is excluded from all radio-validation
claims, and the RTL8731BU row therefore rests on a single surviving sample: a
one-unit result, not a population.
10 MHz is qualified and advertised; 5 MHz is refused. The path ports the
later workaround in rtl8733bu-20230626 (program the RF as 20 MHz, then apply
the small-bandwidth BB clock fields after the RF writes), and the vendor's own
capability mask never included either width — the qualification below is
devourer's, on the b733 unit, against an independent RTL8812CU (Jaguar3)
narrowband peer.
10 MHz evidence:
- Occupied bandwidth on a quiet channel (ch149, 6 Mbps flood,
tests/sdr_obw.py): OBW99 8.28 MHz, −20 dBr 9.06 MHz, centred within 50 kHz — a clean half-clocked emission against the 20 MHz control's 16.33 MHz. - Two-way cross-decode with the RTL8812CU peer at 10 MHz, ~3.2–4.2k frames per cell: b733→peer 97% (ch36) / 96% (ch13); peer→b733 97% (ch36) / 90% (ch13). The width is real, not cosmetic: the same b733 flood decodes 0% at a 20 MHz receiver.
- HT carries over the 10 MHz session: MCS0 and MCS7 each 1900/2000 (95%) at
the peer, decoded at their own rates (
rate_hw12 / 19). - 2.4 GHz OBW (ch13,
--top-db 10strong-block gate against the resident ambient): −20 dBr width 9.08 MHz, matching the quiet-channel shape within 20 kHz; OBW99 reads 11.1 MHz through the residual co-channel ambient the gate cannot fully reject, so the ch149 number is the shape reference and the ch13 decode evidence carries the band claim. - Spectral mask, stitched across three SDR tunes (
tests/sdr_mask.sh; its stitch and gain-transfer controls are enforced — a run whose captures disagree or compress fails instead of printing numbers). 20 MHz, MCS7, both bands, and every value is an upper bound on the emission (the B210's own front-end regrowth is not separable from a true skirt): ch149 at gain 40 reads −26.7/−27.5 dBr at ±11 MHz, −32.1 at ±20 and −34.1/−33.9 at ±25 — every reachable 802.11n breakpoint met; ch13 at the band's linear operating point (gain 20) reads −41 dBr at +11 MHz, −50 at +20 and −53 at +25 on the band-edge side where the spectrum is empty — 14+ dB deeper than the ch149 bounds, which shows the gain-40 numbers are partly instrument regrowth and the true emission is at least that clean. The ch13 negative offsets land on resident ambient (channels 5–9) and read the neighbours, not the DUT. At 40 MHz the −20 dBr width is 36.77 MHz from a single 50 Msps capture; the deeper stitched mask is refused by the script's own controls at every workable gain (skirt-gate starvation vs floor/compression) and stays instrument-bound, like the 20 MHz −40 dBr far points at ±30 MHz. - EVM at the top of the ladder, from the RTL8812CU witness's per-frame
rx.frameevents (near-field —docs/bench-testing-near-field.md; reproducer:tests/rtl8733b_rf_characterize.sh): MCS7/20 median −57 (ch149) / −51 (ch13), MCS7/40 −57, p90 within 1 dB of the median everywhere — no PA compression at the 16 dBm target, consistent with the overdrive study below. The witness reports no EVM for CCK frames, so CCK EVM stays unmeasured. - Per-rate radiated level (same reproducer; SDR mean ON-block level, relative within one gain+geometry): within 0.9 dB across 6M→MCS7 on ch149 and within 2.7 dB across CCK 1M→MCS7 on ch13 (CCK ~2 dB above MCS7; single-run levels repeat within ~1 dB). The 2.4 GHz table needs the SDR at gain 20 — at gain 40 its front end clips on this band (signal at −2 dBFS), and the first reading was compressed junk; check headroom before trusting a level.
- TSSI target stability over a bounded sustained MCS7/20 run (same
reproducer,
SOAK_SECS; 3-minute and 60 s runs concur): SDR level identical at the ends (−39.1 dB both reads), thermal delta pinned at +4 throughout, witness EVM median unchanged first-vs-last quarter (−58/−57 over 39.9k frames on the 3-minute run). The closed loop holds its target across the run. - Absolute output power stays unmeasured, and will on this bench: there is no calibrated power meter, and the B210 is a relative instrument. The 16 dBm TSSI target's absolute accuracy rests on the factory EFUSE calibration; everything above is relative to it.
- The stub divider codes are correct for 10 MHz: a DEVOURER_NB_DAC sweep of all eight 0x9b4[10:8] codes changed nothing that survives the peer-decode discriminator.
5 MHz, measured before it was refused: the BB small-BW mode (0x9b0[7:6]=1)
airs no packets in any tested configuration — the full DAC space
(0x9b4[10:8], 8 codes) and the full ADC space (0x9f0[3:0], 16 codes; the
first ADC sweep was silently limited to 0–6 by an env-parser truncation and
was re-run after fixing it). From a true VBUS-cold state the mode is simply
silent — the no-RF-output failure the vendor workaround describes. Earlier in
the same bench session it was also observed airing a continuous full-power
carrier (no inter-frame gaps, stopped only by session teardown; three
captures, both bands) that later stopped reproducing — a state-dependent
hazard whose trigger is unpinned. Either behaviour justifies the refusal, and
the carrier mode makes it a safety matter: channel_plan refuses WIDTH_5
outright and kBw5 is absent from the bandwidth mask. The divider-experiment
knobs (DEVOURER_NB_DAC/DEVOURER_NB_ADC) remain for any future attempt.
Measurement caveat that cost this port a wrong verdict first: a duty-gated average PSD on a busy channel counts ambient 20 MHz frames, which are wider than a narrowband signal — on ch36/ch13 the same 10 MHz emission read 11.6–16.2 MHz OBW and looked inert. Qualify narrowband OBW on a quiet channel, and prefer the cross-decode discriminator (a 20 MHz peer hearing nothing while a 10 MHz peer hears everything), which ambient cannot fake. One unit pair, no spectral-mask or EVM measurement — the same #390 limits as everything else here.
Firmware and parameter tables are reproducibly extracted from
libc0607/rtl8733bu-20230626
commit 9e5f6845b4e3393207720fc1886668bc80b6af1c, pinned as the
reference/rtl8733bu-20230626 submodule (reference/README.md). That tree is
also the source of the halmac_reg2.h and power-sequence constants transcribed
into src/rtl8733b/, so both the generated artifacts and the hand-carried
constants are re-verifiable from a fresh checkout. The extraction tools pin the
source-file hashes, output sizes and content hashes:
git submodule update --init reference/rtl8733bu-20230626
python3 tools/extract_8733b_fw.py --check
python3 tools/extract_8733b_tables.py --checkBoth accept --input / --source-root to point at a tree elsewhere.
The implementation was also compared with
libc0607/rtl8733bu-20240806
commit 2ec19e154cffbc2abd98d43d59278dffa6e50d49; it was not used to silently
replace the firmware or tables already validated on the device. The review
covered the later active-RF-path TSSI selection (already matched here), SRRC
band-edge setup, WLAN/Bluetooth GPIO pull-up changes, power-saving and C2H/TX
tasklet fixes. None justified replacing the pinned firmware/tables or widening
the raw-monitor capability claims above.
The backend covers the USB-first HALMAC 87xx scope of OpenIPC/devourer#233. It does not adopt that issue's provisional VHT/80 MHz assumptions, because both vendor trees describe this silicon as HT-only and 20/40 MHz.