Skip to content

fix(hl2): RX audio leaves WDSP at the AGC level, not x4. Principle VIII. - #6254

Merged
jensenpat merged 2 commits into
aethersdr:mainfrom
on8st:fix/hl2-rx-panel-gain-unity
Oct 10, 2026
Merged

jensenpat merged 2 commits into
aethersdr:mainfrom
on8st:fix/hl2-rx-panel-gain-unity

Conversation

@on8st

@on8st on8st commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Refs #5942

Hermes-Lite 2 receive audio becomes 12.04 dB quieter, at every slider position. The panel does not depend on the mode. The level is measured in USB and LSB. AM is checked only for the setting surviving a rebuild, and CW and FM are not measured. That covers the speaker (and the HL2's codec, which the speaker mix feeds), the per-slice audio sent to TCI, and the in-app decoders. Today the chain output sits above full scale: WDSP's panel multiplies by 4.0 after the AGC has limited at about -0.16 dBFS. No maintainer has ruled on this level change. It needs a release note: anyone who pulled the slice AF slider down to about 25 to stop strong stations distorting can put it back at 100.

Limits first

What changes

  • WdspChannel::Config gains std::optional<double> rxPanelGain. When it is unset, open() leaves WDSP's create_rxa() value of 4.0 untouched, so every existing owner is unaffected. When it is set, open() calls SetRXAPanelGain1 after the FM block, so a set value wins over the FM recipe's 1.0 (no caller sets both). validateConfig() refuses a set value that is non-finite, negative, or on a transmit channel.
  • Hl2RxDsp::buildChannel() sets it to Hl2RxDsp::kRxPanelGain = 1.0, so every HL2 build, rebuild and rate change opens at unity.
  • Two comments in hl2_rxdsp_unmute_staleness_test that described the x4 are updated (its marker is still above its own floor). docs/HERMES.md §AGC notes that its recorded peaks (3.186, 2.664) include the x4. It also notes that the stages between the AGC and the panel mean the AGC's ceiling does not bound the output.

The test: hl2_rx_panel_gain_test (socket-free, about 1 s)

leg this commit HL2 without the panel line (= main)
AGC off, 1500 Hz tone at 0.01: chain gain +10.01 dB (the 10.00 dB fixed gain alone) +22.05 dB
control: the built config re-opened directly -0.01 dB vs the HL2 chain -0.01 dB
negative control: the same config, panel unset +12.03 dB vs the HL2 chain -0.01 dB (fails, as it should)
AGC med (39 dB ceiling), tone at 0.1, USB peak 0.830 (-1.62 dBFS), rms 0.587 3.318 (+10.42 dBFS)
the same, LSB 0.828 (-1.64 dBFS) 3.311 (+10.40 dBFS)
rebuild to AM at 96 kHz keeps unity yes no

The ratio between the two AGC-med columns is 4.00 (12.04 dB), and the AGC-off gain moves by the same amount. The negative control takes Hl2RxDsp::channelConfig() and clears only rxPanelGain. That isolates the panel from everything else the HL2 sets.

Red on mutation (scratch commits, never pushed; the full suite each time):

mutation result
Hl2RxDsp does not set rxPanelGain (main's HL2 behaviour) RED: the gain, negative-control, both AGC-med and both config checks
WdspChannel::open() ignores rxPanelGain (the field is set but never applied) RED: the gain, negative-control and both AGC-med checks, while the config checks pass. So the level legs check the effect, not the field.

Full ctest suite on macOS (Qt 6.12.0, offscreen), on 121340c0 (head 594d96cf): 654 of 658 pass at -j8 and 655 after the serial rerun. The three remaining failures are known on clean main on this machine, each with only its known check lines: control_receive_test (#6248), peripheral_auth_dialog_test (#6008) and unified_title_bar_test (#6224). vkamp_connection_test (#5254) failed at -j8 and passed serially. This PR touches none of their files. The figures in the table above were re-read from hl2_rx_panel_gain_test on this head and are unchanged; the main-column figures and the two mutations are from before the rebase. The rebase brought in #5973 (RTL wide FM, inside WdspChannel) and #5975 (HL2 CW carrier level, transmit side); neither touches the panel gain or Hl2RxDsp. On the second mutation vkamp_connection_test also failed at -j8 and passed serially (#5254). The level figures and the mutations below were taken at e9a0c396; 594d96cf changes comments and one doc line, no executable line.

73, ON8ST

🤖 Generated with Claude Code

WDSP's create_rxa() builds the RXA patch panel with gain1 = 4.0 and xpanel()
applies it after the AGC whatever its run flag says. Nothing on the HL2 path
called SetRXAPanelGain1, so every HL2 mode left the chain 12.04 dB above the
AGC's -0.16 dBFS ceiling (aethersdr#5942): a strong station peaked at 2.6-3.9 into a
clamp at 1.0.

WdspChannel::Config gains an optional rxPanelGain; unset keeps WDSP's 4.0,
so ANAN and RTL are unchanged. Hl2RxDsp opens every channel with
kRxPanelGain = 1.0. HL2 receive audio is 12.04 dB quieter at every slider
position: the speaker mix (and the codec it feeds) and the per-slice
consumers (TCI, the in-app decoders).

hl2_rx_panel_gain_test runs a real Hl2RxDsp offline: AGC off, the chain's
gain is the fixed gain alone; the same built config with the panel unset
reads +12.04 dB (negative control); AGC med, a strong tone peaks under full
scale in USB and LSB; a rebuild keeps the setting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@on8st
on8st requested a review from a team as a code owner October 7, 2026 17:49

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1. Issue fit

Partially, by design, and honestly scoped. This is step 1 of #5942 (plus step 4's test, HL2 only), which is exactly what the triage ruling on #5942 asked for: "Step 1 is right and is the whole defect… call it from WdspChannel::open() alongside applyRxAgc(), from a Config field defaulting to unity." Step 2 (slider → SetRXAPanelGain1) and step 3 (Meter::AgcGain) are deferred, which the triage also asked for. The body says Refs #5942, not Fixes, and lists the deferrals — correct, the issue should stay open.

Two things a reader of the triage will want corrected: SetRXAPanelGain1 is already declared in third_party/wdsp/include/aether_wdsp.h:86 on main (it came in with the FM recipe at WdspChannel.cpp:1461), so no header change was needed and none is missing. And the sibling at src/core/backends/anan/AnanRxDsp.cpp:113 plus the non-FM RTL path still open at WDSP's 4.0 — disclosed in the body's first limit, and a one-line change each if the maintainer wants them in the same PR.

2. Scope

File What it changes Claimed? Verdict
src/core/dsp/WdspChannel.h:107-109 adds std::optional<double> rxPanelGain to Config yes in scope. Internal C++ field, not protocol/settings/capability surface — no new public surface.
src/core/dsp/WdspChannel.cpp:1294-1298 validate: set ⇒ receive-only, finite, ≥ 0 yes in scope; matches the direction-gated pattern of the FM-deviation and APF clauses above it.
src/core/dsp/WdspChannel.cpp:1470-1472 open() pushes it when set; unset leaves 4.0 yes in scope. Opt-in, so ANAN/RTL/TX are bit-identical.
src/core/backends/hl2/Hl2RxDsp.h:73-75, .cpp:166 kRxPanelGain = 1.0, set on every build yes in scope. buildChannel() is also the direct entry the live rate-change path uses, so a rate change carries it too.
tests/hl2_rx_panel_gain_test.cpp, tests/tests.cmake:1476-1482 new socket-free test, registered yes in scope.
tests/hl2_rxdsp_unmute_staleness_test.cpp:97-104, 485-489 two comments that described the ×4 yes in scope — and necessary, those comments are now false.
docs/HERMES.md:609-611 notes the recorded peaks include the ×4 yes in scope (see nit 1 on the wording).

Everything in the diff is explained by the issue. No CHANGELOG entry (correct). No unrelated file, no formatting churn, no removed guard — the only - lines are the two stale comments, replaced in place.

Socket tests: none. hl2_rx_panel_gain_test.cpp constructs only QCoreApplication, Hl2RxDsp and WdspChannel; no QTcpServer/QUdpSocket/QLocalServer/bind/listen/connectToHost, no Fake* peer. Nothing removed either.

3. Blockers

None.

4. Nits (non-blocking)

  • src/core/backends/hl2/Hl2RxDsp.h:73-74 and docs/HERMES.md:610-611 overclaim the ceiling, and the PR body already knows it. The header says:

        // RXA panel gain: unity, so the AGC's ceiling (-0.16 dBFS) is the output
        // ceiling. WDSP's 4.0 put every HL2 mode 12.04 dB past it (#5942).

    and HERMES.md: "12.04 dB quieter, so the AGC's -0.16 dBFS ceiling is the output's." But the body's own limit says "Unity does not promise |out| <= 1.0. Speak, mpeak and the second bandpass pass all run between the AGC and the panel", which is the triage's point too — in xrxa() xwcpagc is call 26 and xpanel is call 41, with xbandpass bp1 pass 2, xspeak and xmpeak in between. Your own leg 2 measures 0.830, not 0.982, so the AGC ceiling demonstrably isn't the output's. Suggest saying what is actually true — the panel no longer adds 12 dB after the AGC — rather than naming a ceiling the chain doesn't hold. The test file two doors down warns that "reasoning of this kind goes stale silently"; this is worth not shipping born-stale.

  • src/core/dsp/WdspChannel.cpp:1460-1472 — the new block sits after the FM branch, so a set rxPanelGain silently overrides the FM recipe's deliberate SetRXAPanelGain1(m_channelId, 1.0) and its "Normalize inside the FM chain" reasoning. No caller does that today (HL2 passes 1.0, and fmReceive is never set on the HL2 path), so this is latent only. A one-line comment saying the explicit field wins over the FM normalization, or gating the new call on !m_config.fmReceive, would keep the next person from having to re-derive it.

  • The body's "in every mode" is measured for USB, LSB and AM only. HL2 CW, and HL2 FM (plain Mode::Fm; Hl2RxDsp::buildChannel() never sets fmReceive, so no FM limiter either) are reasoned rather than measured. The reasoning is sound — the panel is mode-independent — but the claim is broader than the evidence.

5. What I tried to break

  • "The level legs test the effect, not the field." Confirmed by reading, not by running. Leg 1 compares the chain's AGC-off gain against wc.agcFixedGainDb within 0.5 dB, and the negative control compares the same built config with only rxPanelGain cleared against ±0.1 dB of +12.04 dB. Empty the body of the new open() block and the HL2 chain reads 22 dB while the negative-control step collapses to 0 dB — both red, with the config-echo checks still green, exactly as the body's second mutation reports. Leg 2's peak < 1.0 fails on main's ~3.3. Leg 3 is carry-only and would survive a field-set-but-unapplied mutation; Legs 1–2 cover that, so the suite as a whole isn't self-asserting.
  • Does anything undo the setting after open()? SetRXAMode (RXA.c:864, 886) touches panel.p->run only, and per #5942 the gain loop sits outside that guard, so a runtime mode change cannot restore 4.0. reconfigure() (WdspChannel.cpp:582-595) does close() then open(), so it re-pushes. setMode(), setFilter(), RXASetNC/RXASetMP touch no panel state. The live HL2 rate change enters buildChannel() directly (per its own comment at Hl2RxDsp.cpp:68-70) and that is where the field is set, so it is carried there too — configure() is synchronous (Hl2RxDsp.cpp:82-92), so the test's configure-then-feed ordering is deterministic.
  • Does the 12 dB drop break a meter or a calibration? No: the HL2 S-meter reads Meter::SignalAverage → WDSP's smeter, which xrxa() runs at call 8, long before the panel, and AdcPeak is earlier still — so the needle and the IQ-derived pan trace stay where they are, and the "a meter that moved while the trace stayed put" worry at Hl2Backend.cpp:915-916 doesn't fire. I also looked for an in-tree compensator that would now double-attenuate: nothing in the HL2 audio path scales by 0.25 or ÷4, and the per-slice tap at Hl2Backend.cpp:899 is pre-gain, so the slider does not interact. (WbfmReceive::outputGain = 0.25 is RTL wide FM, which this PR deliberately leaves alone.)
  • The refusal leg isn't vacuous. A default-constructed Config (1024/1024 blocks, 48 k rates, 2048 taps, 150/3000 Hz) passes every other clause of validateConfig(), so the NaN, negative and transmit cases really are being refused by the new clause rather than by an unrelated one firing first.
  • The adjacent test's level controls still hold. hl2_rxdsp_unmute_staleness_test keeps kMarkerAmplitude = 0.02; with +22 dB → +10 dB the marker peak falls from roughly 0.25 to roughly 0.06, still clear of both ends of its own guard (peak < 0.95, peak > 0.01) with ~16 dB of margin instead of ~28. The updated comments match the new arithmetic.
  • Decoder thresholds. The CW "Sens" control (src/gui/PanadapterApplet.cpp:215-224) is a decode cost/confidence threshold, not an amplitude gate, so a 12 dB quieter feed doesn't move it. I did not audit every in-app decoder for an absolute-magnitude threshold; if one exists it would be a separate issue, not a defect in this diff.
  • CI: all 5 checks green on e9a0c396 (build, check-macos, check-windows, Static checks, Sanitizer configures). Note ctest in ci.yml is -R-filtered, so green does not mean the new test ran there.
  • Everything above is reasoned from the head checkout at /tmp/aetherclaude/pr-6254; I have no build or test run, so no runtime figure in this review is mine.

No findings from CodeGuard or Copilot were supplied for the changed files; I'm not claiming a scan ran clean.

6. Recommendation

Needs maintainer decision — on the level change, not on the code. The engineering is the triage's own prescription: opt-in field so no other backend moves, direction-gated validation in the same shape as its neighbours, set in the one place every HL2 build and rate change passes through, and a test whose level legs fail on main and on both disclosed mutations. The two nits are wording, and can ride a follow-up.

What's left for you is the part the PR correctly refuses to decide: (a) accept that every HL2 receives 12.04 dB quieter at a given slider position — speaker, codec, per-slice TCI tap and in-app decoders alike — with the release note the body asks for; and (b) whether ANAN (AnanRxDsp.cpp:113) and non-FM RTL move in this PR or in their own. #5942 carries maintainer-review precisely for (a), and the PR says so in its own first line.

Thanks for the honest limits section — "offline only", "unity does not promise |out| <= 1.0", and "step 5 of #5942 is still unrun" are all things a reviewer would otherwise have had to find.


🤖 aethersdr-agent · cost: $10.3163 · model: claude-opus-5

Review on aethersdr#6254. The Hl2RxDsp.h comment and docs/HERMES.md said that
at unity panel gain the AGC's -0.16 dBFS ceiling becomes the output
ceiling. In xrxa() the AGC (xwcpagc) runs well before the panel
(xpanel), with the second bandpass pass, the noise reducers' post-AGC
positions, cbl, speak, mpeak and ssql between them, and the test's own
USB leg peaks at 0.830, not 0.982. Both now say only what holds: unity
removes the x4, so the output no longer sits 12.04 dB past the AGC's
level, and no output peak is bounded by it.

WdspChannel::open() notes that a set rxPanelGain wins over the FM
recipe's 1.0 above it. No caller sets both today. No behaviour change.

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

on8st commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Bad news first: nit 1 is right, and the wording was wrong in two places, not one.

Nit 1, the output ceiling. Correct. In the vendored xrxa() (third_party/wdsp/upstream/RXA.c) the AGC (xwcpagc) runs well before the panel (xpanel), with the second bandpass pass, the post-AGC noise-reducer positions, cbl, speak, mpeak and ssql in between. The test's own USB leg peaks at 0.830, not at the AGC's ~0.982. 594d96cf rewords the Hl2RxDsp.h comment and the docs/HERMES.md line. They now say only what holds: unity removes WDSP's x4, so the output no longer sits 12.04 dB past the AGC's level, and no output peak is bounded by it.

Nit 2, a set rxPanelGain overrides the FM recipe. Correct, and it is latent: no caller sets both. I added a one-line comment at the call in WdspChannel::open() saying the set field wins over the FM value. I did not gate it on !fmReceive, because then a caller that sets both would get a silent no-op instead.

Nit 3, "in every mode". Right, and the evidence is narrower than your summary of it: the level legs run USB (AGC off and AGC med) and LSB (AGC med) only. The AM leg checks only that the setting survives a rebuild to AM at 96 kHz. It does not measure a level. CW and FM are reasoned (the panel does not depend on the mode), not measured. I will narrow the body to say so.

Your other points, checked against the source: SetRXAPanelGain1 is declared in aether_wdsp.h on main, so no header change is missing. The ANAN and non-FM RTL paths still open at 4.0. That is deliberate, as the body says. The level change itself (12.04 dB quieter on every HL2) still needs a maintainer ruling. This round does not touch it.

Tests. Full ctest on macOS (Qt 6.12.0, offscreen), head 594d96cf on 121340c0: 654 of 658 pass at -j8, 655 after the serial rerun. hl2_rx_panel_gain_test passes. The three remaining failures fail on clean main on this machine too, each with only its known check lines: control_receive_test (#6248), peripheral_auth_dialog_test (#6008) and unified_title_bar_test (#6224). vkamp_connection_test (#5254) failed at -j8 on its known check and passed serially. Our harness reports RESULT: RED pr6254-review 594d96cf because #6248 is not on its waiver list; I checked each failing line by hand. This commit changes two comments and one doc line and nothing executable, so no mutation was run for it.

Not verified: no Linux or Windows run, nothing on hardware, and no level figure re-measured. The figures in the body are unchanged, because nothing executable changed.

73, ON8ST

@Ozy311 Ozy311 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-review of 594d96cf, the one commit since the aethersdr-agent review at e9a0c396. It is comments only, and it takes both wording nits:

  • Hl2RxDsp.h:73-75 and docs/HERMES.md:609-612 no longer claim that the AGC's −0.16 dBFS ceiling is the output's ceiling. They now say that bandpass, speak and mpeak run between the AGC and the panel, so unity panel gain bounds no peak. That matches the PR's own leg-2 figure (0.830).
  • WdspChannel.cpp:1470 says that a set rxPanelGain wins over the FM recipe's value. No caller combines the two today, so that is a comment-level fix for a latent case, which is all the nit asked for.

No code changes, and the branch merges cleanly with current main. Nothing new from me. What is left is the maintainer's call on the 12 dB level change for every HL2 install, and whether ANAN (AnanRxDsp.cpp:113) and non-FM RTL move in this PR or a separate one.

73, K6OZY


Generated by Claude Code

@jensenpat jensenpat self-assigned this Oct 10, 2026

@jensenpat jensenpat 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.

Verdict

Approve. Reviewed 594d96cf69bc513506a20aeac885336b859c8e38. Fits #5942 step 1 (and the HL2-only half of step 4). The 12.04 dB HL2 level change is the change this PR is for; this review was asked to merge it if the seam stayed intact.

The radio seam holds. rxPanelGain is an optional on shared WdspChannel::Config. Unset leaves create_rxa()'s 4.0. The only production writer is Hl2RxDsp::buildChannel() (kRxPanelGain = 1.0). AnanRxDsp, RtlReceiverRegistry, and Hl2TxDsp still call WdspChannel::create without setting it. Flex does not construct a WdspChannel. No new vendor include above the seam, no new raw Flex command text (command-plane baseline stays 368/32), no family-string branch, no IRadioBackend / RadioModel / GUI change. validateConfig() refuses the field on a transmit channel. On the merged tree, check_engine_boundary.py --strict, check_command_plane.py --strict, and check_wdsp_test_links.py --strict report nothing that would block. The new test includes Hl2RxDsp.h and MetisProtocol.h for kEp6BlockSamples only: QCoreApplication, no socket, and it does not reach a WDSP header.

open() applies SetRXAPanelGain1 only when the field is set, after the FM recipe, and RXASetNC does not rebuild the panel, so the value survives the rest of open(). xpanel() multiplies by gain1 outside its run guard (patchpanel.c).

Scope

Path What it does
src/core/backends/hl2/Hl2RxDsp.{h,cpp} HL2 opens every receive channel at unity
src/core/dsp/WdspChannel.{h,cpp} Optional field; unset keeps 4.0 for every other owner
tests/hl2_rx_panel_gain_test.cpp, tests/tests.cmake Socket-free level test, registered in the default graph
tests/hl2_rxdsp_unmute_staleness_test.cpp Comments only; marker amplitude unchanged
docs/HERMES.md Recorded AGC peaks include the old x4; unity bounds no peak

Findings

None blocking. Earlier wording nits are already taken in 594d96cf.

Verification

  • Local merge of 594d96cf onto origin/main (191d806e1) was clean. The merge commit in the scratch tree is 71a812da6 and was not pushed.
  • Qt 6.12.0, RelWithDebInfo, AppleClang. Built hl2_rx_panel_gain_test and hl2_rxdsp_unmute_staleness_test only. The app was not built.
  • QT_QPA_PLATFORM=offscreen ctest -R 'hl2_rx_panel_gain_test|hl2_rxdsp_unmute_staleness_test': 2/2 passed.
  • Direct run of the panel test: AGC-off chain +10.01 dB (fixed gain +10.00); same config reopened -0.01 dB; panel unset +12.03 dB; AGC med USB peak 0.830 (-1.62 dBFS), LSB 0.828 (-1.64 dBFS); AM rebuild keeps unity; NaN, negative, and transmit are refused.
  • GitHub checks on 594d96cf are green: build, check-macos, check-windows, Static checks, Sanitizer option configures. The per-PR workflow does not execute this test. full-suite.yml runs it after merge.

Non-blocking follow-ups

  • #5942 steps 2 (slider into SetRXAPanelGain1), 3 (Meter::AgcGain), and 5 (whether the slider-25 crossover holds across LNA gain on hardware) are still open. Nothing here was measured on a radio.
  • ANAN, and RTL outside the existing narrow-FM panel of 1.0, stay at 4.0 on purpose. CW and FM levels on HL2 are not measured; the panel gain does not depend on mode.
  • RtlReceiverRegistry::boundedDsp() does not mention rxPanelGain. No RTL caller sets it. WdspChannel::create still refuses a non-finite, negative, or transmit value.

@jensenpat
jensenpat merged commit 5538b8b into aethersdr:main Oct 10, 2026
5 checks passed
ten9876 added a commit that referenced this pull request Oct 11, 2026
…remote operation over CGNAT (#6389)

## Summary

Release prep for **v26.10.2**: the six release files plus the Stable
docs snapshot. Metadata and docs only; no source, test or CI changes.

Cutoff: `origin/main` at `46943eb3c` (#6083, merged 2026-10-11).
Anything that merges after it is folded in before the tag.

**106 merged changes** from **15 human contributors**, AetherClaude (2)
and Dependabot (0). First-time contributor: **@dacacioa**. This was
confirmed by checking each login for a merged PR before the previous
tag, not from `author_association`.

## Changelog

The range `v26.10.1..46943eb` was mapped through the commit-to-PR API
(`repos/…/commits/<sha>/pulls`), not from commit subjects. It holds 106
PRs and no direct commits, and every one is cited in the section.
`v26.10.1` is an ancestor of `main`, so nothing is excluded. #6083
merged before the branch was cut, so it is inside the range and needs no
unmerged-PR banner.

The notes lead with:
- the unified 52 px title bar (#6198);
- docs.aethersdr.com (#6286, #6298, #6294);
- remote operation over CGNAT through the in-radio tailnet shim (#6273,
#6318, #6325, #6319);
- the canon windows (#6227, #6334, #6339, #6340, #6341, #6364, #6372,
#6386).

Then come DVK import, FFT Smooth/De-comb, TCI tune and receiver events,
RTL-SDR multi-receiver delivery, HL2 and ANAN-G2 state memory, the
IC-7300MK2 fixes, the first clean sanitizer runs (stated beside #6350's
TSan red) and Authenticode signing.

Numbers come from the PR bodies. "On upgrading" callouts cover the
behavior changes the bodies flagged:
- HL2 receive audio is 12.04 dB quieter (#6254);
- HL2 host CW is +6.02 dB at a fixed drive (#5975);
- ANAN AGC-T is remapped (#6282);
- sleep prevention is on by default (#6195);
- the Tailscale MTU cap has no opt-out (#6319);
- IC-7300MK2 Auto SQL is withdrawn (#6184).

DeepFist (#5953, #6103) is described as applying only to
`ENABLE_DEEPFIST_EXPERIMENT` builds.

## Docs refresh, not just version strings

- **README**:
- Highlights: FFT Smooth/De-comb on the spectrum line; AetherMap naming;
remote over CGNAT and TCI tune/receiver events on the SmartLink/TCI
line; pre-NR Copy Assist; a new docs.aethersdr.com line.
- Supported Hardware: the KPA1500, marked as not yet validated on
hardware.
- Other radio families: HL2 state restore extended; ANAN-G2 gains AGC-T,
NB2, supply voltage/link and per-radio memory; RTL-SDR now "bundled in
every official package", with WFM stereo, PPM, per-receiver SQL/meters
and DAX/TCI feeds, and multi-receiver explicitly an evaluation.
- Roadmap section: the CTR2 line advanced and a visual-canon line added.
  - Maturity labels are unchanged.
- **ROADMAP**:
- In flight: a v26.10.2 sentence each for aetherd, Icom, ANAN-G2,
RTL-SDR (remaining is now production multi-receiver qualification and
digital FM, RFC #6079), Hermes-Lite 2 and the CTR2 relay (RFC #6332
extensions). A new entry for the visual canon (RFC #6226 / #6239).
  - Queued: RFC #4214 (approved 2026-10-09) added.
- Open RFCs, checked against `label:rfc`: #4214 leaves (approved); #4764
and #4861 leave (shipped by #6198 and #6216); #6346, #6232, #6212 and
#6209 join. #6332 is approved and folds into the CTR2 entry.
- Recently shipped: nine new `(v26.10.2)` entries, still within the
v26.10.x cycle, so no month-rollover trim.
- Backends (six: anan, flex, hl2, icom, rtl, sim) and NR engines (seven)
were checked against the tree; both are unchanged.

## Judgment calls flagged for the maintainer

All of these were decided through AskUserQuestion:
- Version **26.10.2**, date **2026-10-11**, opened ready for review.
- **#6350**, the TSan red in the weekly sanitizer lane: proceed, and
name it below.
- RFCs **#4764** and **#4861** come off Open RFCs as shipped. The issues
themselves are left open for you to close.
- RFC **#4214** goes under Queued (next cycle).
- A **visual canon** In-flight entry, plus a README Roadmap line.
- The **KPA1500** is listed in README Supported Hardware with "not yet
validated on hardware".
- Including **#6083**: you asked for it, and it merged before the
cutoff, so it ships as an ordinary part of the range.

## Known failing on `main`

- **`ctr2_hid_thread_port_test`** fails in the weekly TSan lane (runs of
2026-10-08 and 2026-10-10; #6350). `main`'s CI and Full Suite were green
at `414082197`. The CHANGELOG headline and intro say so: the lanes
achieved their first clean runs this cycle, and the test remains failing
under TSan. This prep does not fix the test, and the release ships with
it red in that lane.

## Scope

| file | change |
|---|---|
| `CMakeLists.txt` | `project(AetherSDR VERSION 26.10.2)` |
| `README.md` | Current version line, plus the prose refresh above |
| `AGENTS.md` | Current version line |
| `CHANGELOG.md` | new `[v26.10.2] — 2026-10-11` section; `[Unreleased]`
stays and stays empty; history below byte-identical |
| `packaging/linux/io.github.aethersdr.aethersdr.metainfo.xml` |
`<release version="26.10.2" date="2026-10-11"/>` first in `<releases>` |
| `ROADMAP.md` | cycle heading → post-v26.10.2, plus the prose refresh
above |
| `docs/user/versioned_docs/version-stable/**`,
`docs/user/versioned_sidebars/version-stable-sidebars.json`,
`docs/user/stable-version.json`,
`docs/user/src/components/LogAnalyzer/rules.json` | docs snapshot
(`tools/docs/snapshot_stable.py 26.10.2`), in its own commit. This is
the first snapshot since #6286. |

## Validation

Output of `check_release_prep.py --mapping mapping.json` at the branch
head: 30 checks, 1 FAIL (explained below), 1 WARN (the snapshot files,
listed in the scope table).

```
PASS v26.10.1 is an ancestor of origin/main
PASS all six version spots say 26.10.2
PASS CHANGELOG heading date equals metainfo release date
PASS appstreamcli validate
PASS metainfo <release> list is date-descending with unique versions
PASS `## [Unreleased]` stays and stays empty
PASS `## [v26.10.2]` sits directly under `[Unreleased]`
PASS the previous section is `[v26.10.1]`
PASS CHANGELOG diff is exactly the inserted section
PASS new section opens with a `### <headline>`
PASS intro sentence has the house shape
PASS new section has a `### Contributors` block
PASS release_contents.py --check (citations, intro and Contributors counts)
     PRs in range: 106  cited: 106; intro 106/15 matches; Contributors (16 entries) matches; first-time ['dacacioa']
     citations outside the range: #4764, #4861, #5468, #6192, #6226, #6271, #6332 (RFCs), #6238, #6350 (issues)
PASS code fences balance / no duplicated table headers or doubled `---` (README, ROADMAP, new section)
PASS every local link in README.md and ROADMAP.md resolves
PASS no heading removed from README/ROADMAP is still linked elsewhere
PASS Recently shipped `(vX)` markers run newest-first and lead with the new version
PASS Recently shipped intro names the current cycle (v26.10.x)
FAIL only the four current-version lines lose the `26.10.1` string outside CHANGELOG
     historical mentions must stay: "label": "26.10.1"
PASS diff touches no src/, tests/, third_party/ or .github/workflows/ path
PASS all six release files are in the diff
WARN files beyond the six (the docs snapshot)
PASS git diff --check
```

- **The one FAIL is the snapshot's version label, not a historical
mention.** `docs/user/stable-version.json`'s `"label"` is the docs
site's current Stable version, and `snapshot_stable.py` rewrites it by
design. The validator predates #6286 and knows only the four
current-version lines. Run on the six-file commit alone, the same check
passes (29 checks, 0 FAIL, 0 WARN).
- The docs build passes: `npm ci && npm run build` in `docs/user`. `node
tools/docs/test_log_rules.mjs` reports 707 checks passed for 45 rules.
The line moves it reports were already on `main`.
- Four merged PRs (#6340, #6341, #6364, #6372) named screenshots to
recapture after merge (`psk-reporter-sidebar.png`, `menu-tools.png`,
`connect-dialog.png`, `rx-agc-calibration.png`), and #6386 named the
panadapter shots (`panadapter.png` and the main-window captures, which
still show the old title strip). They were not recaptured, so the Stable
snapshot carries the older images.
- No configure, build or test was run for this prep; it is metadata
only, and CI builds it.
- `main` CI at the cutoff `46943eb3c`: Docs and CodeQL - Code Quality
are green; CI, Full Suite, CodeQL and the System Libraries Canary were
still running when this was opened. At `414082197`, every one of them
was green.

After merge: tag the resulting **main** commit, not this branch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ten9876 added a commit that referenced this pull request Oct 12, 2026
… Principle VIII.

An audit of every page against the 106 PRs in v26.10.2, each fact checked
against the source at the release commit:

- Menu paths: AetherMap, AetherModem, Callsign Lookup and FreeDV Reporter
  are on View, and Waveforms is on Tools.
- New coverage: Windows Firewall rules and the Network Diagnostics page
  (#6289), live CW contacts (#6268), NB2 on the HL2 and ANAN-G2 (#5966),
  ANAN-G2 AGC-T and per-radio memory (#6282, #6129), HL2 state restore
  and its level changes (#6259, #6254, #5975), DVK import (#6247, #6262,
  #6251), Copy Assist from unprocessed audio (#6216), keyboard-operable
  status-bar indicators (#6267), the KPA1500 (#5905), CTR2 extensions
  and the AetherKnob (#6338), live PPM (#6082), and Icom COMP and the
  squelch scale (#6174, #6184).
- Known issues whose GitHub issues are closed are removed.
- The style guide now asks for US spelling, and every page follows it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ten9876 added a commit that referenced this pull request Oct 12, 2026
… and re-take the Stable snapshot. Principle VIII. (#6396)

## Summary

This brings the user guide (docs.aethersdr.com) up to date with
everything in v26.10.2 and re-takes the Stable snapshot so the default
site shows it. Docs only, apart from the TCI page prose in
`tools/docs/gen_reference.py`; no application source, test or CI
changes.

Every page was audited against the 106 PRs in the release. Every fact
that was added or kept was checked against the source at `f753dbfc4`,
the v26.10.2 release commit, not taken from the PR bodies alone.

## What changed

- **Menu paths.**
- AetherModem, Callsign Lookup, AetherMap and FreeDV Reporter are on
**Tools**. They are created on View and then moved to Tools
(`MainWindow_Menus.cpp:1519-1520`, `:1569-1575`); the style guide's
example says **Tools → AetherMap...**.
  - **Show live CW contacts** is on **View**.
  - Waveforms is on **Tools**, not File.
  - The VUDU applet button was called TXDSP.
  - Labels now match the source's `...` vs `…`.
- **Gaps from this cycle.**
- **Windows and Network Diagnostics:** Windows Firewall rules and the
Network Diagnostics Windows Firewall page (#6289); the `aether.network`
and `aether.nvafx` log categories (#6289, #6308).
  - **Remote access:** Tailscale shim revocation semantics (#6325).
  - **Sleep, App Nap and power throttling (#6195):** per-OS notes.
- **Hermes-Lite 2:** state restore, RX audio at unity, CW at TUNE level,
multi-interface discovery and a discovery troubleshooting entry (#6259,
#6254, #5975, #6009, #6293).
- **ANAN-G2:** AGC-T range, a "What is remembered" section, and supply
voltage and link status (#6282, #6129, #6137, #6211).
  - **NB2** on the HL2 and ANAN-G2 (#5966).
- **DVK:** WAV import requirements, the slot menu without Delete, status
text and licensing (#6247, #6262, #6251).
  - **Copy Assist** from unprocessed audio (#6216).
  - **Live CW contacts** replace the decoder's contact card (#6268).
  - **Status-bar indicators** work from the keyboard (#6267).
- **Volume:** controllers, hotkeys and TCI `volume` follow the active
output (#6129, #6260).
- **Peripherals:** the KPA1500 (#5905), CTR2 link extensions and the
AetherKnob (#6338), TMate 2 udev (#6250), MIDI RADE Modem and FDV modes
(#6031, #6038).
  - **RTL-SDR:** live PPM (#6082).
  - **Icom:** COMP and the MK2 squelch scale (#6174, #6184).
- **Known issues.** Entries whose GitHub issues are closed are removed
(each checked with `gh issue view`). As decided, this includes three
closed today without a demonstrated fix: #5107, #4824 and #5665.
- **US spelling.** `docs-style-guide.md` asked for British spelling; it
now asks for US, by the maintainer's decision, and every page is
converted. The generated TCI pages are converted at their source, the
prose strings in `tools/docs/gen_reference.py`. Code spans, links and
URLs were left alone. UI labels were checked against the source strings,
and none of them are British. Two in-page anchors and two Log Analyzer
rule anchors (`#bnr-is-grayed-out-…`) follow the renamed headings.
- **Stable snapshot.** `snapshot_stable.py 26.10.2` was re-run on the
release tree with these corrections, before `main` was merged in, so
Stable carries no post-release docs. `docs/` also has `main`'s
post-release pages, in US spelling. Stable alone keeps the IC-9700
waterfall-rate entry (#5996): #6402 fixed it after the 26.10.2 tag, so
26.10.2 still has it. The PDF manual already attached to the v26.10.2
release keeps the tag's docs.

## Validation

- `npm ci && npm run build` in `docs/user`: success. This is the
broken-link and broken-anchor check.
- `node tools/docs/test_log_rules.mjs`: 707 checks passed for 45 rules.
- `python3 tools/docs/gen_reference.py --check`: up to date (8 pages).
- `git diff --check`: clean.
- Menu paths on a build of this branch (offscreen, isolated settings, no
radio), `menu list`: `Tools → AetherModem...`, `Tools → Callsign
Lookup...`, `Tools → AetherMap...`, `Tools → FreeDV Reporter...`, `Tools
→ Waveforms...`, `View → Show live CW contacts`.

## Screenshots now out of date (not recaptured)

These need a recapture pass on the radio:
- **Old panadapter title strip (#6386):** `panadapter`, `main-window`,
`first-session-*`, `multi-slice-two-slices`, `split-pair`,
`tnf-markers`, `offscreen-vfo-indicator`, `pan-3d-stacked-trace`,
`default-light-theme`.
- **Pre-canon windows (#6334, #6339, #6341, #6372):** `aetherrx*`,
`aethertx*`, `aethermodem-aprs`, `bnr-panel`, `connect-dialog`,
`connect-by-ip-card`, `connect-icom-rows`, `connect-smartlink-card`,
`rx-agc-calibration`.
- **Other:**
  - `title-bar` (lowercase tab states, #6281);
  - `menu-help` (#6294);
  - `display-panel` (#6371);
  - `support-diagnostics-dialog`;
  - `waveforms-dialog` and `tailnet-remote-access` (show shim 0.4.0);
  - `about-window` (v26.10.1);
  - `radio-setup-qrz`.

## Found during the audit, not fixed here

- **App string bug:** `TailnetShimDialog.cpp:628` tells the user to
"Connect remotely with Connect → Manual"; the real path is **File →
Connect to Radio... → Connect by IP**.
- **ANAN AGC-T tooltips:** the RX applet and VFO tooltips show the
slider position as dB, which is wrong on ANAN. #6282 lists this as a
follow-up.
- **MIDI Master Volume on non-Flex radios:** it is refused while PC
Audio is off, whereas hotkeys, FlexControl and HID drive the line out.
The docs describe the code as it is.
- **RTL mode names:** `rtl-sdr.md` writes "FM-N" and "CW-R", but the
source strings are `FMN` and `CWR`. Left as is until someone confirms
what the mode menu displays.
- **IC-705 COMP readback:** inferred from the MK2 (#6174) and not
verified on an IC-705.
- **Not yet documented:** the #6230 Radio Health row "Forward responses
skipped", and the Store/MSIX package.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

3 participants