F3: Selectable stream-carrier rate (Legacy / HT-MCS / VHT)#90
Merged
Conversation
The two stream demos (StreamDuplexDemo, StreamTxDemo) hard-coded a 13-byte 6-Mbps legacy-OFDM radiotap header. With the precoder stream link's FEC layers landed (#86 RaptorQ, #87 RLC), the carrier rate is now a useful robustness-vs-throughput knob. This v1 ships legacy-OFDM rate switching only: DEVOURER_STREAM_RATE=6M|9M|12M|18M|24M|36M|48M|54M (default 6M) DEVOURER_STREAM_RATE=12|18|24|36|48|72|96|108 (500 kbps units) Higher-rate HT-MCS / VHT paths deliberately stay out of this PR — they need a non-13-byte radiotap and would switch send_packet onto its VHT branch via the existing `radiotap_length != 0x0d` heuristic. F1 (#88) unlocks the HT-MCS path; a follow-up can extend RadiotapBuilder to emit HT and VHT headers behind the same env var. - New src/RadiotapBuilder.{h,cpp}: small helper exposing build_legacy_radiotap(rate_500kbps) -> std::array<uint8_t, 13> parse_stream_rate_env() -> uint8_t - Wired into StreamDuplexDemo and StreamTxDemo, replacing each binary's private kRadiotapLegacy6M[13] constant. The on-air length stays at 13 bytes so send_packet keeps these on the legacy path. - PrecoderDemo is intentionally NOT touched — its PoC plan locks the carrier to 6M. Defaults are unchanged on every code path, so the existing regression matrix and tools/precoder/precoder_stream_roundtrip.py stay byte-identical without setting the new env var. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the legacy-only v1 API with a single StreamRateCfg / build_stream_radiotap
pair that emits three radiotap variants under the same DEVOURER_STREAM_RATE
env var:
Legacy: 6M | 9M | 12M | 18M | 24M | 36M | 48M | 54M (13-byte radiotap)
HT: MCS0 .. MCS31 (13-byte radiotap)
VHT: VHT1SS_MCS0 .. VHT4SS_MCS9 (22-byte radiotap)
Plus DEVOURER_STREAM_BW (20|40|80|160), _SGI, _LDPC, _STBC.
Wire-format facts the chip-side send_packet (RtlJaguarDevice.cpp:42) relies
on:
- radiotap_length == 13 -> vht=false, rate_id=8 (legacy or HT path)
- radiotap_length > 13 -> vht=true, rate_id=9 (VHT path)
- the iterator parses *all* fields regardless of vht — so a 13-byte
HT-MCS radiotap (presence = MCS|TX_FLAGS, no RATE) is correctly
parsed via the IEEE80211_RADIOTAP_MCS case and stays on rate_id=8.
Verified against txdemo/main.cpp's own 13-byte HT beacon_frame[].
HT-MCS requires DEVOURER_TX_HT_MCS=1 (F1 gate in send_packet) to actually
fly at the requested rate; parse_stream_rate_env() emits a stderr warning
when an HT rate is parsed without that gate so users don't silently fall
back to 1M CCK. VHT has no such gate — send_packet's VHT branch
unconditionally honours the VHT info field.
Builders mirror exact field layouts already in use:
- HT 13-byte MCS layout cross-checked against txdemo/main.cpp's
beacon_frame[] (line 300) — same it_present, same MCS field positions
- VHT 22-byte layout cross-checked against txdemo/main.cpp's
DEVOURER_TX_VHT path (lines 374-409) — same known mask, same flag
bits, same coding/mcs_nss positions
Default behaviour (env var unset) is unchanged: 6M legacy OFDM,
bit-identical to the historic kRadiotapLegacy6M[13] constant.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the hard-coded 13-byte 6-Mbps legacy-OFDM radiotap header in
StreamDuplexDemoandStreamTxDemowith aDEVOURER_STREAM_RATE-controlled builder that emits Legacy / HT-MCS / VHT carriers under one env var.Env var grammar
Default (env var unset) = 6M legacy, bit-identical to the historic
kRadiotapLegacy6M[13].Wire-format facts the chip relies on
send_packet(RtlJaguarDevice.cpp:42):The radiotap iterator parses all fields regardless of
vht— so a 13-byte HT-MCS radiotap (it_present = MCS \| TX_FLAGS, no RATE) is correctly parsed via theIEEE80211_RADIOTAP_MCScase and stays on the HT path. Cross-checked againsttxdemo/main.cpp's own 13-byte HT beacon_frame[] layout (line 300).VHT uses the same 22-byte layout as
txdemo/main.cpp'sDEVOURER_TX_VHT=1path (lines 374-409):it_present = VHT \| TX_FLAGS,known = STBC \| GI \| BW, MCS/NSS in user-0 nibbles.F1 dependency for HT mode
HT-MCS in
send_packetis gated byDEVOURER_TX_HT_MCS=1(F1, PR #88, merged).parse_stream_rate_env()emits a stderr warning if an HT rate is parsed without that gate so users don't silently fall back to 1M CCK:VHT has no such gate —
send_packet's VHT branch always honours the VHT info field.Hardware verification (8/8 PASS)
TX = RTL8812AU (0bda:8812), RX = TP-Link RTL8821AU (2357:0120,
DEVOURER_VID=0x2357 DEVOURER_PID=0x0120). ~200 stream frames per case, RX-side<devourer-stream>rate=Nindex parsed.STREAM_RATE=6MSTREAM_RATE=24MSTREAM_RATE=54MSTREAM_RATE=MCS5 TX_HT_MCS=1STREAM_RATE=VHT1SS_MCS3 STREAM_BW=20STREAM_RATE=VHT1SS_MCS3 STREAM_BW=20STREAM_RATE=VHT1SS_MCS7 STREAM_BW=80Every case: 100 % of received frames carry the expected rate index. Default behaviour (no env var) is byte-identical to master.
What ships
src/RadiotapBuilder.{h,cpp}—StreamRateCfgstruct +build_stream_radiotap(cfg) -> std::vector<uint8_t>+parse_stream_rate_env() -> StreamRateCfg. Three internal builders (legacy 13B, HT 13B, VHT 22B) keyed offcfg.mode.StreamDuplexDemoandStreamTxDemoswitched from each demo's privatekRadiotapLegacy6M[13]constant to akStreamRadiotapvector built once at static-init from the env var.PrecoderDemointentionally untouched — its PoC plan explicitly locks the carrier to 6M.CMakeLists.txtupdated to compile the helper intoWiFiDriver.🤖 Generated with Claude Code