Skip to content

jaguar2: a defined AGC table, fc and RF 0xBE for every 5 GHz channel - #501

Merged
josephnef merged 5 commits into
OpenIPC:masterfrom
snokvist:pr/jaguar2-gap-channels
Oct 27, 2026
Merged

josephnef merged 5 commits into
OpenIPC:masterfrom
snokvist:pr/jaguar2-gap-channels

Conversation

@snokvist

@snokvist snokvist commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

What this is

On the 8822B and 8821C (Jaguar2), the channel set writes the AGC table index (0x958[4:0] / 0xc1c[11:8]), the CFO-tracking fc (0x860[28:17]) and, on the 8822B, the RF 0xBE[17:15] VCO band only for channels in the vendor buckets (36..64 / 100..144 / 149+). Any other 5 GHz channel the synthesizer tunes kept whatever the previous channel left, so a receiver tuned there depended on its tuning history. The affected channels:

  • the 65..99 gap between the UNII-2 groups (5325..5495 MHz);
  • 145..148;
  • the fc-unlisted 49..51 and 117;
  • the vendor's unwritten RF 0xBE entries at centrals 50/114/130/163. A 40 MHz tune at 5255 MHz is central 51.

This PR gives every 5 GHz channel defined values. No vendor-defined channel changes value.

What changed

  • Shared helpers for the full path, the 8821C full path and fast_retune:
    • agc_table_5g: the vendor buckets. The channels between them take the RF18 sub-band they tune in: mid from cch 80 on the 8822B (RF18 bit 17), from 100 on the 8821C (its own RF18 split). 145..148 take high.
    • cfo_fc_5g: the vendor buckets, with the unlisted channels assigned as the 8822C's contiguous sco_for table does.
    • rf_be_for_8822b: for 15..148, the 6-step cycle that all 35 defined entries of the vendor's 36..144 tables follow, so only the unwritten entries change. The high table is kept, with 163 filled from 151/175 (both 0x5). It never returns the 0xff no-write sentinel now, so the sentinel and its two guards go.
  • Firmware fast hop (fastretune_fw): now invalidates the write-on-change caches on every hop, not only on a band change. The firmware rewrites the same registers, so a later software hop could skip a write and keep the firmware's value.
  • The uncharacterized-channel warning: tests the central channel and covers 65..99, on Jaguar2 and Jaguar3. It is gated on a 5 GHz primary, because central_ch wraps for out-of-spec 2.4 GHz HT40 configs (ch 13 HT40+ gives 15).
  • src/jaguar2/CLAUDE.md: one bullet with the facts and their caveats.

The 8822C (Jaguar3) needed no fix. Its rf18_for, select_agc_tables and sco_for are contiguous range checks.

Verification

  • Tables. A script reproducing config_phydm_switch_channel_8822b / _8821c was compared with the helpers for every cch from 15 to 253. There are 0 mismatches on vendor-defined channels, odd channels included.

  • Retune history, on hardware.

    • Setup: an 8812BU (8822B) on USB 3, 40 MHz, fixed gain (IGI 0x30, carrier sense off). The same unit was used before (master at that time) and after.
    • Method: one SoapySDR session over the LA ring visits each target from each predecessor. The table shows the spread of the mean noise floor.
    5350 MHz (cch 70) 5450 MHz (cch 90) 5310 MHz (cch 62, in-table control)
    spread by predecessor (5180 / 5510 / 5745), before 3.6 dB 4.5 dB 0.7 dB
    after (5 visits per cell) 0.4 dB 0.7 dB 0.8 dB

    Before the fix, the fixed external carriers in the span moved with the floor while the SNR stayed the same, so this was a gain offset. The stale AGC table is what mattered.

    The counterpart: the in-table control itself spreads 0.8 dB, so the "after" figures mean "no longer resolvable", not zero. It is also one unit at one width.

  • Stale RF 0xBE alone (centrals 51 and 163, before their fill): nothing resolvable, below ~1.5 dB. So the RF 0xBE values written there are untested either way.

  • Full-vs-fast register parity, RX-only. Same 8812BU, same canary set as tests/hop_parity_check.sh. That script injects frames with txdemo, and most of these channels are outside any allocation, so rxdemo was used instead:

    • DEVOURER_RX_SWEEP (FastRetune) against DEVOURER_RX_SWEEP_FULL=1 twice (the full-vs-full control).
    • 20 MHz: 36, 72, 149, 84, 165, 96, 40.
    • 40 MHz: primaries 36, 68, 149, 88, 100, 64, 153, 92 (centrals 38..155).
    • Every gap target follows a predecessor from another AGC, fc or VCO bucket.
    • Result: 0 fast-vs-full breaks on all 38 canary registers at every channel. The only run-variant register was 0x82c at central 155, in the full-vs-full control too.

    The counterpart: no 80 MHz on this unit (the second unit below covers it), and the firmware hop is not covered. The 20 MHz targets avoid 52..64 and 100..144 on purpose, where the pre-existing 0x838 gap below would show.

  • A second unit (josephnef's hardware check, RX-only, 8822BU 0bda:b812): on the full path, canary registers at four affected targets (20 MHz ch 72/92, 40 MHz centrals 70/50) match across two predecessors each; master differs in 0x860, 0x958, RF 0xbe. Fast vs full, all 38 canary registers with two full runs as control, matched on every dwell at 40 MHz (81) and 80 MHz (67); at 20 MHz (87 dwells) the only difference was 0x838 on ch 117, the pre-existing DFS CCA tweak below (master shows the same two values). That adds 80 MHz, on one unit.

  • 8812CU (8822C) in the gap: spread by predecessor 0.1..0.4 dB at 20 MHz width, against a 0.1 dB in-table control. No change needed (one unit).

  • ctest: 100 % passed (83, 2 skipped as on master).

The raw data and the scripts are in snokvist/wifi-sdr (evidence/gap-ch65-99.txt, tools/retune_history.py).

Not verified

  • The 8821C half is code-covered only (no 8821C on the bench).
  • The firmware fast hop's own handling of gap channels is unverified. If the firmware mirrors the vendor C, fw_switch_confirm falls back to the full path there.
  • The every-hop cache invalidation is reasoned from the code, not measured. Its cost is one extra write of each cached register on the first software hop after a firmware hop.

Pre-existing, not in this PR

A review turned up two fast-path gaps that this PR does not touch:

  • fast_retune never rewrites the 20 MHz DFS-channel CCA tweak in 0x838[7:4].
  • fast_retune does not clear a spur_calibration notch left by a full set.

Both are full-path-only state that a software fast hop carries along. tests/hop_parity_check.sh at BW=20 with INIT=36 TARGET=52 should show the first.

🤖 Generated with Claude Code

snokvist and others added 3 commits October 27, 2026 09:03
The 8822B/8821C channel set only wrote the AGC table index (0x958 /
0xc1c), the CFO-tracking fc (0x860[28:17]) and, on the 8822B, the RF 0xBE
VCO band for channels in the vendor buckets (36..64 / 100..144 / 149+).
Anything else - the 65..99 gap between the UNII-2 groups, 145..148, the
unlisted fc channels 49..51 / 117, and the vendor's unwritten RF 0xBE
entries at centrals 50/114/130/163 (a 40 MHz tune at 5255 MHz is central
51) - kept whatever the previous channel left, so a receiver tuned there
depended on its tuning history.

Measured on one 8812BU (40 MHz, fixed gain), same unit before and after:
5350 / 5450 MHz read 3.6 / 4.5 dB apart (noise floor and a fixed carrier
alike, SNR unchanged) depending on whether they were reached from 5180 or
from 5510/5745. After: 0.4 / 0.7 dB, inside an in-table control's 0.8 dB.
The AGC table was what mattered: a stale RF 0xBE alone (centrals 51, 163)
showed nothing beyond noise, so the RF 0xBE values written there are
untested either way.

- agc_table_5g: the vendor buckets, the channels between them taking the
  RF18 sub-band they tune in (mid from 80 on the 8822B, 100 on the 8821C).
- cfo_fc_5g: the vendor buckets, unlisted channels assigned as the
  8822C's contiguous sco table does.
- rf_be_for_8822b: 15..148 from the 6-step cycle that all 35 defined
  entries of the vendor's 36..144 tables follow (only the unwritten ones
  change); the high table with 163 filled from 151/175. It never returns
  the 0xff no-write sentinel now, so the sentinel and its guards go.
- The full path, the 8821C full path and fast_retune share the helpers.
  Checked against config_phydm_switch_channel_8822b/_8821c for every cch
  15..253: no vendor-defined channel changes value.
- A firmware fast hop (fastretune_fw) now invalidates the write-on-change
  caches on every hop, not only on a band change: the firmware rewrites
  the same registers, and a later software hop could otherwise skip a
  write and keep the firmware's value.
- The uncharacterized-channel warning tests the central channel and
  covers 65..99, on Jaguar2 and Jaguar3.

The 8821C changes are code-covered only. The 8822C needed no change: its
rf18_for / select_agc_tables / sco_for are contiguous, and a measured
8812CU showed no history dependence in the gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The uncharacterized-channel warning tests the central channel, which
central_ch wraps for out-of-spec 2.4 GHz HT40 configs (ch 13 HT40+ ->
15): gate it on a 5 GHz primary, on Jaguar2 and Jaguar3. The jaguar2
notes state what the after-fix spread can and cannot show, and record
the RX-only full-vs-fast parity run (20/40 MHz, gap channels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Define Jaguar2 5 GHz tuning values for previously skipped channels

🐞 Bug fix 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Assign AGC, CFO and 8822B VCO values to 5 GHz channels previously left with stale settings.
• Share channel mappings between full and software-fast retunes; invalidate caches after every
 firmware hop.
• Warn on uncharacterized central channels in Jaguar2 and Jaguar3.
Diagram

graph TD
  B["8822B full set"] --> H(["5G mapping helpers"]) --> R["Tuning registers"]
  C["8821C full set"] --> H
  S["Software fast hop"] --> H
  F["Firmware fast hop"] --> I["Invalidate caches"] --> S
  F --> R
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Explicit per-channel lookup tables
  • ➕ Makes every extrapolated value directly inspectable without tracing range rules or a cycle.
  • ➖ Duplicates repeated vendor values and creates more entries to maintain across full and fast paths.

Recommendation: Keep the shared helpers: they prevent full/fast mapping drift while preserving vendor-defined values. An explicit table would aid inspection but is less maintainable here. Reviewers should pay particular attention to the extrapolated RF values, which were not independently validated on hardware; the 8821C and firmware-gap behavior also remain unverified.

Files changed (4) +119 / -116

Bug fix (3) +99 / -116
HalJaguar2.cppDefine Jaguar2 tuning values across the 5 GHz channel range +80/-104

Define Jaguar2 tuning values across the 5 GHz channel range

• Shares AGC and CFO mappings across both variants' full and software-fast paths, and replaces 8822B RF 0xBE no-write entries with defined values. Invalidates channel-keyed caches after every firmware hop and warns using the 5 GHz central channel, including the 65–99 gap.

src/jaguar2/HalJaguar2.cpp

HalJaguar2.hDeclare shared Jaguar2 channel-mapping helpers +7/-1

Declare shared Jaguar2 channel-mapping helpers

• Declares the AGC and CFO helpers used by full and fast retunes. Updates the RF 0xBE contract to remove the no-write sentinel.

src/jaguar2/HalJaguar2.h

RadioManagementJaguar3.cppWarn on uncharacterized Jaguar3 central channels +12/-11

Warn on uncharacterized Jaguar3 central channels

• Extends the one-shot warning to central channels 65–99 and reports both primary and central channels. Gates the warning on a 5 GHz primary to avoid false positives from wrapped 2.4 GHz HT40 centrals; Jaguar3 tuning mappings are unchanged.

src/jaguar3/RadioManagementJaguar3.cpp

Documentation (1) +20 / -0
CLAUDE.mdDocument gap-channel mappings and verification limits +20/-0

Document gap-channel mappings and verification limits

• Explains why previously skipped channels inherited tuning state, the new mapping rules, and the measured 8822B result. Records the limits of RF-value, 8821C, bandwidth and firmware-hop verification.

src/jaguar2/CLAUDE.md

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

qodo-free-for-open-source-projects Bot commented Oct 27, 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. Fast hops into the channel gap go unwarned ✓ Resolved
Description
The new uncharacterized-channel check runs only in set_channel_bw() and set_channel_bwmode(),
while both Jaguar2 and Jaguar3 fast-retune paths can tune a central channel in 65–99 without calling
either setter. A session that starts on a characterized channel and then fast-hops into the gap
receives no warning that its power tables and per-channel settings are extrapolated.
Code

src/jaguar2/HalJaguar2.cpp[R889-890]

+  if (!_warned_uncharacterized && channel > 14 &&
+      ((wch >= 15 && wch <= 35) || (wch >= 65 && wch <= 99) || wch > 177)) {
Evidence
The new warning is inside the full setters, but each device calls its full setter after a fast hop
only when fast retuning returns false. Both chips' fast paths accept same-band hops into the gap,
including firmware hops, so the new warning is skipped on those transitions.

src/jaguar2/HalJaguar2.cpp[883-895]
src/jaguar3/RadioManagementJaguar3.cpp[51-63]
src/jaguar2/RtlJaguar2Device.cpp[1063-1080]
src/jaguar3/RtlJaguar3Device.cpp[1576-1601]
src/jaguar2/HalJaguar2.cpp[1416-1455]
src/jaguar3/RadioManagementJaguar3.cpp[665-724]

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

## Issue description
The new warning for uncharacterized central channels is limited to full channel setters. Successful software or firmware fast hops into channels 65–99 bypass it.
## Fix Focus Areas
- src/jaguar2/HalJaguar2.cpp[883-895]
- src/jaguar2/HalJaguar2.cpp[1400-1455]
- src/jaguar3/RadioManagementJaguar3.cpp[51-63]
- src/jaguar3/RadioManagementJaguar3.cpp[665-724]
## Recommended Fix
Apply the same one-shot, 5 GHz central-channel warning when either fast-retune path accepts a destination, ideally through a shared check per chip so full and fast tuning use identical conditions.

ⓘ 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 describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the helpers against the old tables for every central channel 15..253 (odd ones included, both AGC split points): 0 mismatches on vendor-defined channels; only the gap channels get new values. The fw-hop cache invalidation and the channel > 14 gate on the warning both look right. Two nits inline. The two pre-existing fast-path gaps (0x838[7:4] DFS CCA tweak, leftover spur_calibration notch) should go into a follow-up issue.

Comment thread src/jaguar2/CLAUDE.md Outdated
Comment thread src/jaguar2/HalJaguar2.cpp Outdated
@snokvist

Copy link
Copy Markdown
Collaborator Author

/review

Comment thread src/jaguar2/HalJaguar2.cpp Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit bbd44ee

- warn_uncharacterized (Jaguar2, Jaguar3): the one-shot extended-channel
  warning is a helper called by the full channel set and both fast paths
  (software and firmware), so a fast hop into 65..99 (or below 36 / above
  177) warns like a full set does (Qodo). It runs once a destination is
  accepted; after the first warning it is a bool test.
- set_channel_bw computes cch once, before the 8821C dispatch (review).
- src/jaguar2/CLAUDE.md: the bullet states what holds now (the gap's
  assignments, the measured spread with its control, what is untested)
  instead of the history, at about half the length (review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@josephnef

Copy link
Copy Markdown
Collaborator

Hardware check on an RTL8812BU-class 8822BU (0bda:b812), RX-only. The scripts compare DEVOURER_DUMP_CANARY register dumps; nothing was injected on the gap channels.

Retune history, full path. The same target reached from two different predecessors, registers compared on the target:

target predecessors master this PR
20 MHz ch 72 36 vs 149 0x860, 0x958 (1 vs 3), RF 0xbe differ identical
20 MHz ch 92 36 vs 149 same 3 differ identical
40 MHz ch 68 (central 70) 36 vs 149 same 3 differ identical
40 MHz ch 48 (central 50) 36 vs 100 0x860, RF 0xbe differ identical

Fast vs full, every dwell. DEVOURER_RX_SWEEP against _SWEEP_FULL=1 twice as the control, all 38 canary registers. The two full runs never differed from each other.

  • 40 MHz, 81 dwells, centrals 38/50/66/70/90/94/102/151/155/163: no differences.
  • 80 MHz, 67 dwells, centrals 42/58/74/90/106/155: no differences. That adds 80 MHz to what the PR body covers.
  • 20 MHz, 87 dwells, channels 36/40/50/72/84/96/117/146/149/163/165: one difference. On ch 117, 0x838 is 0x86666351 after a full set and 0x86666361 after a fast hop. master gives the same two values, so it is the pre-existing DFS CCA tweak from the PR body, not this change.

Not covered here: the 8821C, the firmware hop and its cache invalidation, and anything on air (this shows register state only). Same single-unit caveat as the PR's own numbers.

A second 8812BU-class unit (maintainer's RX-only canary dumps) matches
full-path registers at four filled targets across predecessors, and
full-vs-fast parity at 20/40/80 MHz except the pre-existing 0x838[7:4]
DFS CCA tweak. The firmware hop and the 8821C stay untested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@snokvist

Copy link
Copy Markdown
Collaborator Author

Thanks for the hardware run. The 80 MHz result and the register-level history check go beyond what I could cover here. Both are now in the PR body and in src/jaguar2/CLAUDE.md (394e23b), attributed to the second unit with its scope: four targets, canary registers only, RX-only. The firmware hop and the 8821C stay listed as untested. The ch 117 0x838 difference is the pre-existing DFS CCA tweak, left for the follow-up issue.

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both nits addressed in 106c210, and the fast-hop warning is placed after every early return on both generations. The hardware results in my earlier comment were on bbd44ee. 106c210 doesn't change any register writes, so they still apply. The two pre-existing fast-path gaps are tracked in #502.

@josephnef josephnef added the skip-qodo-gate Bypass the Qodo review gate (outage / maintainer decision) label Oct 27, 2026
@josephnef
josephnef merged commit f46c28a into OpenIPC:master Oct 27, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-qodo-gate Bypass the Qodo review gate (outage / maintainer decision)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants