Skip to content

jaguar1: HT40 centre channel follows the offset (vendor rtw_get_center_ch) - #500

Merged
josephnef merged 2 commits into
masterfrom
jaguar1-ht40-center-channel
Oct 26, 2026
Merged

josephnef merged 2 commits into
masterfrom
jaguar1-ht40-center-channel

Conversation

@josephnef

@josephnef josephnef commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

What this is

Jaguar1 (8812AU/8811AU/8814AU/8821AU) took the 40 MHz centre channel from a hand-written lookup table that ignored the HT40 offset. On 2.4 GHz the table only listed primaries 4 and 8, so any other primary stayed uncentred. At ch6 HT40+ the RF synthesizer was tuned to channel 6 instead of 8, 10 MHz low. Every HT40 frame was lost, while 20 MHz on the same channel worked. The table was also wrong on UNII-3 (153→155, 161→163) and for the non-default offset of the pairs it did list.

This replaces the table with the vendor rule from rtw_get_center_ch: UPPER → primary − 2, otherwise primary + 2. That is the rule Kestrel already uses, so DONT_CARE defaults to HT40+ on both.

Found while working the follow-ups in #499.

Evidence

Vendor-driver control first. On Realtek's own driver (reference/rtl8812au), the same 8812AU decodes ch6 HT40+ at 52–61 %. On devourer it was 0 % in 12 of 12 sessions, so the receive hardware is fine.

Register diff, same unit, ch6 HT40+. I read the BB/MAC registers live with chipstate --no-claim while each driver owned the chip. The bandwidth registers (0x483, 0x668, 0x838, 0x8ac, 0xa08) were already identical. RF 0x18 was the one structural difference:

RF[A/B] 0x18
vendor driver 0x07408: BW 40, channel 8
devourer, master 0x07406: BW 40, channel 6
this PR 0x07408; HT40− gives 0x07404

On air. TP-Link T3U (8822B) transmitting from devourer. The 8812AU on devourer is the DUT; an 8812BU (b812) receives in parallel as an independent witness. 1500 frames per session, 3 reps:

cell 8812AU, this PR witness 8812AU, master
ch6 HT40+ MCS1/40 45.5 / 51.7 / 35.9 % 74.9 / 77.5 / 57.3 % 0 %
ch6 HT40+ MCS7/40 53.9 / 52.2 / 0.0 %¹ 71.3 / 70.7 / 0.0 %¹ 0 %
ch6 HT40− MCS1/40 53.5 / 54.4 / 52.7 % 73.1 / 65.5 / 70.7 % —
ch36 HT40+ MCS7/40 95.5 / 96.3 / 95.9 % 94.7 / 96.9 / 95.3 % 94–98 %
ch40 HT40− MCS7/40 93.7 / 96.5 / 96.6 % 86.1 / 95.7 / 96.0 % —

¹ Both receivers read zero together in that session. That is a transmit-side failure of the 8822B on 2.4 GHz HT40 (tracked in #499), not this bug.

  • ctest: 86/86 pass.

Grid-edge fit (second commit, from review)

Offset arithmetic alone can leave the band on 2.4 GHz: ch13 HT40+ gives centre 15 (classified as 5 GHz), and ch1 HT40− wraps to 255. A 2.4 GHz HT40 request now has to fit channels 1–13. HT40+ needs a primary of 1–9, HT40− one of 5–13; a request outside that takes the side that fits, with a warning. 5 GHz is left to the caller.

The requested offset is kept apart from the fitted one, so a FastRetune hop back from the edge restores the requested side.

On air, 8812AU transmitting to two 8822Bs:

  • ch13, requested HT40+: RF 0x18 0x0740B (centre 11), 91 / 89 %.
  • ch1, requested HT40−: 0x07403 (centre 3), 91 / 87 %.
  • A 6, 13, 6 hop at HT40+ reads 0x07408, 0x0740B, 0x07408 on both the fast and full paths.

Not verified

  • The 8814AU and 8821AU go through the same function, but neither was measured.
  • No 2.4 GHz channel other than 6 was tested.
  • The 2.4 GHz numbers are on a busy channel; the 20 MHz MCS1 control was ~60–70 %.

🤖 Generated with Claude Code

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix Jaguar1 HT40 centre-channel tuning for channel offsets

🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

AI Description

• Replace Jaguar1’s incomplete HT40 centre-channel table with offset-based tuning to prevent missed
 frames.
• Apply the same rule during full channel setup and cached fast retunes.
Diagram

graph TD
  A["Channel request"] --> B{"40 MHz?"} --> C{"Upper offset?"} --> D["Primary minus 2"] --> F["RF centre"]
  C --> E["Primary plus 2"] --> F
  B --> G["Existing BW rules"] --> F
Loading
High-Level Assessment

Offset-based arithmetic is preferable to expanding the lookup table: it handles either HT40 offset across primaries and follows the cited vendor rule. Keeping it in the existing centre-channel function also covers both tuning paths.

Files changed (1) +6 / -35

Bug fix (1) +6 / -35
RadioManagementModule.cppDerive Jaguar1 HT40 centre channel from the offset +6/-35

Derive Jaguar1 HT40 centre channel from the offset

• Removes the incomplete primary-to-centre lookup table and its unordered-map dependency. For HT40, an upper offset now selects primary minus two; lower and unspecified offsets select primary plus two.

src/jaguar1/RadioManagementModule.cpp

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Channel 13 HT40 switches to 5 GHz ✓ Resolved
Description
rtw_get_center_ch() adds two to a 40 MHz primary without checking whether its secondary channel
fits the 2.4 GHz grid. With the RX example's default offset, DEVOURER_CHANNEL=13 DEVOURER_BW=40
produces centre channel 15, which Jaguar1 classifies as 5 GHz and uses for RF tuning and
channel-dependent configuration; channel 1 with the opposite offset can also wrap to 255.
Code

src/jaguar1/RadioManagementModule.cpp[R213-214]

+    center_ch = chnl_offset == HAL_PRIME_CHNL_OFFSET_UPPER ? channel - 2
+                                                           : channel + 2;
Evidence
The RX example accepts an environment-supplied channel and defaults wide-channel offset to 1 without
validating the pair. Jaguar1 passes that pair to the changed calculation, then uses its result as
the channel for RF and band selection. The repository's channel-grid validator explicitly rules out
these edge pairs, but this setup path does not call it.

examples/rx/main.cpp[2100-2125]
examples/rx/main.cpp[2549-2556]
src/chanmig/ChannelDef.h[136-161]
src/jaguar1/RadioManagementModule.cpp[208-255]
src/jaguar1/RadioManagementModule.cpp[1687-1712]
src/jaguar1/RadioManagementModule.cpp[1720-1733]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Jaguar1 accepts channel-and-offset pairs that do not form a valid HT40 pair. The new centre calculation can turn those requests into a wrong-band channel or an unsigned wraparound value.
## Fix Focus Areas
- src/jaguar1/RadioManagementModule.cpp[209-214]
- src/jaguar1/RadioManagementModule.cpp[230-255]
## Recommended Fix
Validate the primary, width and offset before calculating or applying the centre channel. Reject pairs outside the 2.4 GHz grid, including an upper offset on channel 1 and a lower offset on channel 13, rather than passing a computed invalid channel to the RF path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/jaguar1/RadioManagementModule.cpp
josephnef and others added 2 commits October 26, 2026 14:55
…r_ch)

The 40 MHz centre came from a hand-written channel table that ignored the
HT40 offset. On 2.4 GHz it only listed primaries 4 and 8, so any other
primary stayed uncentred: ch6 HT40+ tuned RF 0x18 to channel 6 instead of 8,
10 MHz low, and every HT40 frame was lost while 20 MHz worked. The table was
also wrong on UNII-3 (153->155, 161->163) and for the non-default offset of
the pairs it did list.

Use the vendor rule instead (UPPER -> primary - 2, otherwise + 2, as Kestrel
already does) and drop the table.

8812AU, ch6 HT40+: RF 0x18 0x07406 -> 0x07408 (vendor driver: 0x07408);
delivery 0 % -> 36-54 % (vendor driver on the same unit: 52-61 %).
ch36/ch40 HT40 unchanged at 94-97 %.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Offset arithmetic alone can leave the band: ch13 HT40+ gives centre 15,
which Jaguar1 classifies as 5 GHz, and ch1 HT40- wraps to 255. The vendor
never reaches rtw_get_center_ch with such a pair because its channel plan
rejects it first. On 2.4 GHz a pair must stay inside 1-13 (HT40+ needs a
primary of 1-9, HT40- one of 5-13), so a request that does not fit takes the
side that does, with a warning. 5 GHz is left to the caller.

The caller's requested offset is kept apart from the fitted one, so a
FastRetune hop back from the grid edge restores the requested side instead
of inheriting the forced one.

8812AU TX, two 8822B receivers: ch13 requested HT40+ -> RF 0x18 0x0740B
(centre 11), 91 / 89 %; ch1 requested HT40- -> 0x07403 (centre 3), 91 / 87 %.
Hop 6,13,6 at HT40+: 0x07408, 0x0740B, 0x07408 on both the fast and full
paths. ch6 and ch36 unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@josephnef
josephnef force-pushed the jaguar1-ht40-center-channel branch from cf58fb5 to b5ffaf4 Compare October 26, 2026 12:06
@josephnef
josephnef merged commit f7e0731 into master Oct 26, 2026
28 of 29 checks passed
@josephnef
josephnef deleted the jaguar1-ht40-center-channel branch October 26, 2026 12:16
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