Skip to content

feat(anan): select the G2's receive band-pass filters and mute its speaker amplifier (RFC #6209). Principle VIII - #6673

Merged
jensenpat merged 3 commits into
aethersdr:mainfrom
tropo1234:anan-filter-bank
Oct 22, 2026
Merged

jensenpat merged 3 commits into
aethersdr:mainfrom
tropo1234:anan-filter-bank

Conversation

@tropo1234

@tropo1234 tropo1234 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements RFC #6209 parts 2 and 3 on the existing ANAN Front End page:

  • Filter bank: Follow the tuned frequency selects the receive band-pass filter on both ADCs as DDC0 tunes. Bypassed remains the default.
  • Mute the radio’s speaker amplifier applies the G2 amplifier mute independently of the existing title-bar audio mute.
  • The partial anan/frontend.set extension applies either option live without persisting it. The page owns persistence in the existing nested Anan settings document; connect seeds both options before the first High Priority packet.

Wire encoding stays inside the ANAN backend. The packet builder preserves the RX/TX word split, and every running High Priority sender uses the same options. Clean stop clears the amplifier mute and lets the existing bypass flags determine the banks.

The RadioModel changes are confined to its existing ANAN connect-parameter branch. There are no changes to the backend interface, Flex or Icom backends, shared audio controls, themes, or global defaults. The existing page visibility and capability gates remain intact.

Validation

  • macOS, Qt 6.12.0, RelWithDebInfo: app plus focused test targets built with 22 jobs at 818fb3c8a, working tree clean.
  • Seven socket-free CTest targets passed with QT_QPA_PLATFORM=offscreen: anan_p2_protocol_test, anan_settings_test, backend_family_switch_test, backend_seam_affinity_test, backend_seam_consumer_test, radio_setup_control_routing_test, and radio_setup_window_size_test.
  • Three isolated mutations of the protocol implementation each failed the intended assertions: 22 MHz threshold, ADC0 filter shifted into the TX word, and speaker mute moved to bit 0. The unchanged implementation passes.
  • Isolated, TX-disabled automation bridge: new controls, bypass disabling and refusal, re-enabling, and persistence across app restart. Explicit DEMO-0001 connection verifies the shared simulator path; no physical radio was connected during this landing.
  • All four engine/seam ratchets have identical per-file findings against the merge base. Test registration, frozen CI gate, seam probe generation, generated docs, and 707 log-rule checks pass.
  • Hosted Linux, macOS, Windows, Static checks, sanitizer configuration, and Docs checks passed on final head 818fb3c8a. Documentation builds ran in hosted CI.

Hardware evidence and limits

The contributor reports receive-only bench validation on one ANAN-G2: the selected filters passed signals above the measured dead-path floor; the 6 m preamp switched in; the speaker mute silenced the amplifier and persisted across reconnect. That bench evidence was not repeated during this landing. The 6 m passband and the 20 MHz filter on quiet 12 m/10 m bands remain unproven, as does clean-stop unmute by ear when the idle radio speaker is already silent.

The local build lacks QtKeychain, DFNR, and sherpa-onnx; their behavior is not validated here. No live Flex, Icom, RF, or TX validation is claimed. The PR adds no socket-owning test; the socket-free protocol and settings coverage remains.

Generated with OpenAI Codex (GPT-6 Astra)

@tropo1234
tropo1234 requested a review from a team as a code owner October 21, 2026 22:27

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

Issue fit

Yes. This is RFC #6209 parts 2 and 3, and it implements what @jensenpat approved on 2026-10-17 — including the parts of the approval that constrain it. The RFC's three required shapes are all present and correct: the verb is partial per field (AnanBackend.cpp:1484-1500, fields.contains() before each read), it persists nothing (the dialog writes AnanSettings then invokes), and it is gated on backendDeclaresExtension("anan") rather than a family string (RadioSetupDialog.cpp:5369-5379) — byte-for-byte the HL2 hw.set precedent at RadioSetupDialog.cpp:4586-4590. adcFrontEnd stays a fieldless record, so #5554 and the #5262 M2 boolean freeze both hold.

The approval's one explicit reservation was that "RFC approval does not establish hardware proof for the untested filter selections" — the RFC had only 3 of 7 confirmed. The bench table in the body now covers all seven on a G2, including the 1.5 MHz BPF, the 20 MHz BPF and the 6 m preamp, and it names what it could not show (the 6 m passband; the 20 MHz filter on 12 m and 10 m, where the band sat at the receiver's own floor). Reporting the dead-path floor as a fourth state, so "Follow is 20 dB above a removed signal path" is a measurement rather than an inference, is the right way to evidence this control. That is a genuine Principle VIII fit, not a checkbox.

Scope

Everything in the diff is explained by RFC #6209's own "Parts 2 and 3" file list, and the two files beyond it are required consequences.

File(s) Change Claimed? Verdict
P2Protocol.{h,cpp} AlexRxFilter, alexRxFilterBits/For, HighPriorityOptions trailing param Yes RFC scope
P2Client.{h,cpp} two live setters, highPriorityOptions(), sendRunningHighPriority(), m_ddc0FreqHz Yes RFC scope
AnanSettings.{h,cpp} two fields inside the existing nested Anan document Yes RFC scope; Principle V holds (no flat key)
AnanBackend.{h,cpp} frontend.set, two connect params, test-access friend Yes RFC scope
RadioModel.cpp two params seeded in populateFamilyParams Yes RFC scope
RadioSetupDialog.cpp combo, mute checkbox, pushLive, reseed Yes RFC scope
tests/anan_{front_end,p2_protocol,settings}_test.cpp, tests.cmake new target + ~50 checks Yes In scope
docs/architecture/radio-capabilities-map.md adcFrontEnd row Yes In scope
docs/user/docs/{anan-g2,radio-setup}.md Setup table, two troubleshooting entries Yes Required by the checklist
docs/user/src/components/LogAnalyzer/rules.json 13 RadioModel.cpp line anchors, +7 Yes ("refreshes the line numbers that moved") Mechanical and correct — I re-resolved all 13 anchors against the head tree and every one matches its recorded text; 0 mismatches

No file is unexplained, no new public surface arrives as a side effect (the verb is the RFC's whole subject), and I read the - lines for removed guards: the only deletions are the four inlined buildHighPriority call sites folded into sendRunningHighPriority(), and the Q_UNUSED(arg) that arg now genuinely uses. No guard, early return or confirmation was removed.

Personal-preference check: the two modified existing elements are the bypass-checkbox hint text and the page intro, both of which change because the sentence they asserted ("no band filter is ever selected") stops being true. The bypass checkboxes are disabled, not removed, and the default is byte-identical to today. No default changed, nothing was restyled or reordered for taste. This is a feature, not a smuggled preference.

Socket test (recording per §2, not a finding). tests/anan_front_end_test.cpp is new and binds a socket: QUdpSocket on UDP 127.0.0.1:1027 (kHighPriorityPort). tests.cmake:1320-1331 discloses it in the block comment ("Loopback only, no radio. Binds 127.0.0.1:1027 (kHighPriorityPort)") and sets SKIP_RETURN_CODE 77, and the test exits 77 on a bind failure rather than consuming its timeout. This is legitimate under canon: the listener is inert — it never replies and nothing in the test needs a reply, so it is not a synthetic radio peer standing in for p2app. The subject is our own P2Client's outbound bytes. CI does not run it on PR: the only ctest -R steps in ci.yml are the DV, cross-needle, mac_nr_filter_test and asr_gpu_probe_test ones, so this and the two extended tests gate at merge/weekly via full-suite.yml (#5405's frozen gate), not here.

Blockers

None.

Nits (all non-blocking)

  • src/core/backends/anan/P2Client.cpp:196-199 — the stop-packet comment overclaims. It reads:

    // change them, so a clean stop hands the radio back bypassed and
    // unmuted.

    "Unmuted" is unconditional and correct (byte 1400 is zero). "Bypassed" is not: stop() passes m_bypassAdc0Filters/m_bypassAdc1Filters with default opts, so with a bypass box unticked and Follow on, the stop packet writes 0x0000 into both bank words — neither a filter nor HF Bypass, which is no signal path at all, not "bypassed". Your own second test block sets up exactly that configuration (bypassAdc0Filters = false, bandFiltersEnabled = true) and asserts 0x0000 for the live case, but never checks that block's stop packet. Harmless in practice — no client is listening by then — but the same sentence is in the PR body's design notes and in docs/user/docs/anan-g2.md, so it is worth making conditional:

          // The clean-stop packet, mirroring metisStop(): run=0 reaches the
          // radio before the socket that would carry any further keepalive
          // goes away. Without highPriorityOptions(): p2app keeps the last
          // byte 1400 and Alex words it was sent, and no client is left to
          // change them, so a clean stop hands the radio back unmuted, and
          // with the bypass flags -- not a band filter -- deciding the banks.
  • src/core/backends/anan/P2Client.h:267-270 — this comment is now stale:

        // Set once from Params at start(); every buildHighPriority() call for
        // the rest of this session (the keepalive tick included) carries these
        // same values, since there is no live setter for either (see Params'
        // own comment on why these are connect-time-only).

    After this PR a run=1 packet carries these values only while m_bandFiltersEnabled is false; otherwise highPriorityOptions() replaces them with a filter bit in the same relay position. The members are still connect-time-only, so the second half holds — it is the "every call carries these same values" clause that no longer does. Worth one clause noting the override, since this is the comment a future reader checks when the bypass flags appear not to take.

  • src/gui/RadioSetupDialog.cpp:5376-5378 — the page intro says "The filter bank and the speaker amplifier mute take effect at once" with no qualifier, while anan-g2.md correctly scopes the same claim to "a connected G2". The page is shown with nothing connected (RFC ruling (a)), where "at once" is vacuous. The tooltips and the user guide cover it; only the intro sentence is unqualified.

  • tests/tests.cmake:1328-1331 — the fixed 1027 bind means a developer with a real p2app, a second AetherSDR, or another process holding the High Priority port gets a silent SKIP rather than a failure. No sibling collides (anan_speaker_audio_test binds kSpeakerAudioPort; anan_link_telemetry_test binds nothing), so ctest -j is clean today, but a RESOURCE_LOCK property would keep it that way if a future ANAN test wants the same port.

What I tried to break

  • The byte-offset trap this PR's own header comment warns about. The "32-bit Alex0" at 1432 is two 16-bit words to p2app, so a receive filter written into 1432-1433 reaches the TX relays. writeU32be(&pkt[1432], alex0) puts 1u << 24 in byte 1432 and the 16-bit filter in 1434-1435 — correct — and anan_p2_protocol_test.cpp:303-321 pins readU16(hp0, 1434) == 0x0002 and readU16(hp0, 1432) == 0x0100 separately, so a filter leaking into the TX half fails loudly. These tests are not self-confirming: gut highPriorityOptions() and the "20 m selects the 13 MHz filter" checks fail. I also confirmed the opts1 case asserts ADC0 keeps ANT1 | bypass as a full readU32, so a cross-contamination between the two banks is caught too.
  • A stale filter surviving a session restart. m_ddc0FreqHz is reset in start() at P2Client.cpp:92, adjacent to the pre-existing m_ddc0FreqWord = 0 at line 84, so the two cannot drift — and anan_front_end_test.cpp:230 pins "the next session's first packet bypasses rather than reselecting 20 m", which is the one check that mutation kills. A session that never retunes stays on Bypass (AnanBackend.cpp:844 guards m_sliceFreqHz > 0.0), which is the fail-safe direction: signal flows.
  • A sender that forgets the options. I enumerated every buildHighPriority call site in src/: exactly two remain, sendRunningHighPriority() (reached from start, retune, both live setters, and the keepalive) and the single run=0 packet in stop(). There is no fifth run=1 sender to miss, and setDdcRateLive() touches only the DDC-Specific packet, which carries no Alex word. The #5547 single-source-of-truth property is preserved rather than just claimed.
  • Non-finite, negative and zero tuning. alexRxFilterFor guards !std::isfinite before the ladder, which is the live bug it would otherwise have: every NaN comparison is false, so an unguarded ladder falls through to Preamp6m, i.e. silence on HF. Both NaN and infinity are tested, and negative or sub-1.5 MHz takes the Bypass arm. alexRxFilterBits is a total switch with a Bypass — not zero — fallback, so even a garbage enumerator yields a valid relay rather than an open path.
  • Whether m_pendingParams is the right place to record the verb's change. It is, and for a non-obvious reason: the rate-change restart at AnanBackend.cpp:1178-1179 re-start()s the client from m_pendingParams without re-reading AnanSettings, so a live option that only lived in P2Client would be lost on a zoom-driven restart. The verb mirrors setStepAttenuationDb's existing precedent at AnanBackend.cpp:1317-1321 ("resends everything from m_pendingParams") exactly. An external reconnect still re-reads AnanSettings, which is why the verb-without-persist caveat is true as documented.
  • Slice vs. DDC centre. I expected the filter to follow the DDC centre while the docs say "the receiver's frequency", which would mis-select near a band edge at 1536 ksps. It does not diverge: on ANAN m_sliceFreqHz is both the tune target (AnanBackend.cpp:845, 921) and the pan centre (:1751), so there is one frequency, not two.
  • The dialog's reseed loop. Every control on the page persists on change and two now also push to the radio, so an unblocked setCurrentIndex() during reseed would write back and transmit what it just read. QSignalBlocker b5(feBankCombo) … b9(ampMuteChk) covers both new controls, and syncBypassEnabled(followTuned) is then called explicitly so the dimming is not left behind by the blocked signal. No gap.
  • Qt lifetime and threading. Both live setters are Q_INVOKABLE and reached by Qt::QueuedConnection, so highPriorityOptions() only ever reads its members on the client's own I/O thread. Both GUI lambdas are connected with this as the context object, so they die with the dialog. friend struct P2ClientTestAccess at P2Client.h:213 pre-exists; the new methods sit after the private: at line 212, not under the private slots: at 206, so moc does not register them. The test flushes with Qt::BlockingQueuedConnection rather than assuming queued calls run without an event loop — which is the detail that would have made this test pass vacuously, and it doesn't. one() returns an empty array on a wrong count, and u16At/byteAt then return sentinels that match no expected value, so an empty capture fails rather than passes.
  • CodeGuard, all 40 flags on the changed files. I ran the required security audit and refute every one. The CS-VLN-CMDI-BACKTICK hits are the known lexical matcher firing on prose comments and Qt stylesheet strings — no process execution exists in any audited file. CS-PII-BANK/-DL-1/-PHONE-2/-IP are spec page citations (p.91, pp.34,36) and MAC/IP field descriptions in comments. CS-VLN-SSRF-LOCALHOST/-HEX are the test's loopback fixtures and hex bit masks. rules.json:921-936's CS-SEC-PASSWORD is a log-analyzer regex matching the words "username or password" in a diagnostic string, not a credential. The audit independently confirmed the file list is main's tip rather than this diff — RigctlProtocol.*, FlexBackend.cpp, MeterModel.cpp, TransmitModel.*, PanadapterModel.*, SpectrumOverlayMenu.* and SliceTroubleshootingDialog.cpp have no textual intersection with this PR and match #6657–#6660 already on main. Credit: security-audit (CodeGuard), verdict no findings. It separately confirmed the new writes are all compile-time-constant offsets inside the 1444-byte std::array (1432→1436, 1430→1432, 1400, 1443 = last valid index), that nothing in HighPriorityOptions is used as an index, and that the two new AnanSettings accessors end in QJsonValue::toBool(false) and so cannot yield a non-bool from a hand-edited settings row.
  • Reachability of the new verb. The audit enumerated every invokeExtension/invokeBackendExtension caller in the tree. AutomationServer.cpp has no generic (ns, verb, arg) passthrough — it invokes a fixed hardcoded verb per command — so frontend.set is reachable only from the dialog lambda. The anan namespace was already declared for droop.apply, so extensionNamespaces grows no new externally-visible entry. The path stays RX-only: buildHighPriority writes byte 4 bit[0] and has no PTT parameter, so Principle VI is not in play.
  • Governance. RFC #6209 carries RFC Approved with an explicit maintainer ruling, so GOVERNANCE.md's RFC-first requirement is satisfied. No CHANGELOG.md entry was added — correct. No credential or flat settings key is introduced. There is no touchpoint-manifest or seam-table obligation: aetherd-touchpoint-tags.json lists no extension verb, including the existing droop.apply, hw.set and nb.get. All 11 checks on c01eca7 are green, including check-windows — relevant here because that is the only -DENABLE_ASR=OFF lane, and Static checks is where the a11y, capability-record and colour-ratchet gates live.

What I could not check. Everything above is reasoned from the code and from the head checkout at /tmp/aetherclaude/pr-6673; I have no build, no test run and no G2, so nothing here was reproduced at runtime. Three claims rest on sources outside this repository and I am taking them on trust rather than verifying them: that p2app's InHighPriority.c reads 1434-1435 as ADC0's receive word and 1430-1431 as ADC1's; that p2app applies byte 1400 on every High Priority packet including run=0; and that the six band edges (1.5 / 2.1 / 5.5 / 11 / 22 / 35 MHz) are deskHPSDR's for SATURN and ORION2. The bench table is the evidence that matters for the third, and it is consistent with the ladder as coded — the 21.2 MHz reading sitting on the 13 MHz filter rather than the 20 MHz one is the counter-intuitive case, and it was measured. What would settle the first two is a p2app source citation by line, as RFC #6209 gave for the bit table.

Recommendation

Approve with nits. This is careful work: the one wire-level trap in the change (the 1432 vs 1434 split) is called out in the header, implemented correctly, and pinned by tests that fail when mutated; the four run=1 senders are collapsed into one so a future sender cannot drop the options; the fail-safe direction is chosen everywhere (NaN → Bypass, untuned → Bypass, unreachable enumerator → Bypass, default off); and the eight disclosed mutation runs plus the dead-path-floor bench method mean the test plan is evidence rather than assertion. The approval's one open reservation — hardware proof for the four unconfirmed selections — is now answered, with the two bands that could not be shown named as such.

The four nits are all comment- and prose-accuracy, and three of them are the same underlying fact: a filter selection replaces the bypass flags, which makes two older comments and one doc sentence conditional where they read as absolute. None affects a byte on the wire. Next step is the maintainer's call on whether to take those wording fixes in this PR or as a follow-up; the code itself needs no change.

Thanks for the bench table with the fourth attenuator state — reading the removed-path floor so "Follow is 29 dB above dead" is measured rather than assumed is the thing that makes this control reviewable at all.


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

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Cisco CodeGuard — static analysis of this PR (231 finding(s))

  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in docs/user/src/components/LogAnalyzer/rules.json /tmp/aetherclaude/pr-6673/docs/user/src/components/LogAnalyzer/rules.json:89
  • [HIGH] CS-VLN-PATH-UNC — Vulnerability: CS-VLN-PATH-UNC in docs/user/src/components/LogAnalyzer/rules.json /tmp/aetherclaude/pr-6673/docs/user/src/components/LogAnalyzer/rules.json:744
  • [HIGH] CS-VLN-PATH-UNC — Vulnerability: CS-VLN-PATH-UNC in docs/user/src/components/LogAnalyzer/rules.json /tmp/aetherclaude/pr-6673/docs/user/src/components/LogAnalyzer/rules.json:1003
  • [HIGH] CS-PII-DL-1 — PII detected: drivers_license in docs/user/src/components/LogAnalyzer/rules.json /tmp/aetherclaude/pr-6673/docs/user/src/components/LogAnalyzer/rules.json:88
  • [MEDIUM] CG-PATH-001 — Potential path traversal in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:576
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1733
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1736
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:235
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:576
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:896
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1145
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1415
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1495
  • [MEDIUM] CS-PII-PHONE-2 — PII detected: phone in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:2104
  • [HIGH] CS-PII-BANK — PII detected: bank_account in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1733
  • [HIGH] CS-PII-BANK — PII detected: bank_account in src/core/RigctlProtocol.cpp /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.cpp:1736
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/RigctlProtocol.h /tmp/aetherclaude/pr-6673/src/core/RigctlProtocol.h:153
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:146
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:293
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:430
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:568
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:591
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:654
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:704
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:767
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:864
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:937
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/RadioCapabilities.h /tmp/aetherclaude/pr-6673/src/core/backends/RadioCapabilities.h:995
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/anan/AnanBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/anan/AnanBackend.cpp:1086
  • [LOW] CS-PII-IP — PII detected: ip_address in src/core/backends/anan/P2Client.cpp /tmp/aetherclaude/pr-6673/src/core/backends/anan/P2Client.cpp:236
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/anan/P2Protocol.h /tmp/aetherclaude/pr-6673/src/core/backends/anan/P2Protocol.h:245
  • [HIGH] CS-PII-BANK — PII detected: bank_account in src/core/backends/anan/P2Protocol.h /tmp/aetherclaude/pr-6673/src/core/backends/anan/P2Protocol.h:89
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:1747
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:1782
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:1784
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:1790
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:561
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:742
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:794
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/backends/flex/FlexBackend.cpp /tmp/aetherclaude/pr-6673/src/core/backends/flex/FlexBackend.cpp:1067

Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them.


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

tropo1234 added a commit to tropo1234/AetherSDR that referenced this pull request Oct 22, 2026
… the loopback tests' port. Principle VIII.

Four nits from the aethersdr#6673 review. Nothing on the wire changes.

The stop packet's comment said a clean stop hands the radio back
bypassed. It hands it back with the bypass flags deciding both banks,
which is bypassed only while both boxes are ticked. The bypass members'
comment said every packet carries them; while the bank is on, a run=1
packet's filter replaces them.

The page intro now says the filter bank and the mute apply at once to a
connected G2, as the user guide does.

anan_front_end_test counts the High Priority packets that reach
127.0.0.1:1027. anan_speaker_audio_test and anan_link_telemetry_test
start their own clients against 127.0.0.1, which send there too, so
under ctest -j a stray packet could fail a count. The three tests now
share a RESOURCE_LOCK. Full Suite runs ctest serially, so only a local
-j run was exposed.

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

Copy link
Copy Markdown
Contributor Author

All four nits are fixed in the second commit. Nothing on the wire changed.

  1. Stop-packet comment (P2Client.cpp). It now reads "a clean stop hands the radio back unmuted, with the bypass flags, not a band filter, deciding both banks". The PR body's design note now says the same. anan-g2.md needed no change: it already says only "A clean disconnect hands the radio back unmuted".
  2. Bypass members' comment (P2Client.h). It now says that while the bank is on, a run=1 packet's band filter replaces the bypass flags, and that the stop packet always uses them.
  3. Page intro (RadioSetupDialog.cpp). It now says the two options "also apply at once to a connected G2", the user guide's wording.
  4. Port 1027. A sibling does collide today. anan_speaker_audio_test and anan_link_telemetry_test each start() a P2Client against 127.0.0.1, and start() sends a High Priority packet to 127.0.0.1:1027. Under ctest -j the front-end test's listener can receive that packet and fail a one() count. All three tests now share RESOURCE_LOCK anan_p2_loopback. CI was never exposed, because Full Suite runs ctest serially.

The two p2app claims, by line (laurencebarker/Saturn, sw_projects/P2_app/InHighPriority.c):

  • The receive words. Lines 204-208: rd_be_u16(UDPInBuffer+1430) goes to AlexManualRXFilters(Word, 2), commented "Alex 1 RX word", and rd_be_u16(UDPInBuffer+1434) goes to AlexManualRXFilters(Word, 0), "Alex 0 RX word".
  • Byte 1400 on every packet. Lines 95-112 read the run bit. For run=0 they clear SDRActive and TX without returning. Lines 154-156 then pass byte 1400's bit 1 to SetSpkrMute(). Both sit in the one size == VHIGHPRIOTIYTOSDRSIZE block (lines 90-223), so every 1444-byte packet is applied whatever its run bit.

The band edges: deskHPSDR src/new_protocol.c, case NEW_DEVICE_SATURN: / case NEW_DEVICE_ORION2: at lines 1892-1893. ADC0's ladder is lines 1919-1933 and ADC1's is lines 1954-1968: under 1.5, 2.1, 5.5, 11, 22 and 35 MHz, then the 6 m preamp.

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Welcome to AetherSDR, @tropo1234! Thanks for your first pull request.

A few things that might help:

  • Our CONTRIBUTING.md covers coding conventions and the PR process
  • CI will run automatically — if it fails, I'll post a comment explaining what went wrong
  • Jeremy (KK7GWY) reviews all PRs before merge
  • Come say hello on the AetherSDR Discord — most of the community hangs out there, and merged contributors get the Contributors role

If you have questions, feel free to ask here or in Discussions.

— AetherClaude (automated agent for AetherSDR)


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

tropo1234 and others added 2 commits October 22, 2026 11:31
…eaker amplifier (RFC aethersdr#6209). Principle VIII.

The follow-up RFC aethersdr#6209 approved: a filter-bank selector for both ADCs,
the speaker-amplifier mute, and the anan/frontend.set verb that applies
both live.

Filter bank. "Follow the tuned frequency" selects the Alex band-pass
filter covering the receiver's frequency on ADC0 (bytes 1434-1435) and
ADC1 (bytes 1430-1431), the receive words p2app's InHighPriority.c
reads. The edges are deskHPSDR's for SATURN/ORION2 boards: 1.5, 2.1,
5.5, 11, 22 and 35 MHz. The default stays Bypassed, byte-identical to
before. The bypass checkboxes switch the same relay, so they are
disabled while the bank follows the frequency.

Speaker amplifier mute. High Priority byte 1400 bit 1 (p2app
SetSpkrMute). Sending the radio silence leaves the amplifier running and
hissing; this stops it. Bits 0 and 2 stay clear.

Both options are live. P2Client builds every run=1 High Priority packet
in one place, so start, retune, attenuator and keepalive all carry them.
The stop packet carries neither, so a clean disconnect hands the radio
back bypassed and unmuted. Both are seeded at connect from AnanSettings.
frontend.set is partial and persists nothing; the page writes
AnanSettings and then invokes it.

Tests: anan_front_end_test reads the High Priority datagrams P2Client
sends on loopback and drives frontend.set through AnanBackend; the
protocol and settings tests pin the ladder, the bits and the defaults.
Eight mutations each failed their own checks.

Bench, one G2 through the automation bridge: every selection passes a
signal well above the receiver's dead-path floor (1.5, 6.5, 9.5 and 13
MHz filters by 20-31 dB; the 20 MHz filter by at least 5 dB on WWV at
25 MHz; 15 m by 5 dB). The 6 m preamp switches in (+12 dB); its
passband is unproven, the band was quiet. The mute silences the speaker
and persists across a reconnect. A clean disconnect's unmute could not
be heard: the speaker is silent with no client either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the loopback tests' port. Principle VIII.

Four nits from the aethersdr#6673 review. Nothing on the wire changes.

The stop packet's comment said a clean stop hands the radio back
bypassed. It hands it back with the bypass flags deciding both banks,
which is bypassed only while both boxes are ticked. The bypass members'
comment said every packet carries them; while the bank is on, a run=1
packet's filter replaces them.

The page intro now says the filter bank and the mute apply at once to a
connected G2, as the user guide does.

anan_front_end_test counts the High Priority packets that reach
127.0.0.1:1027. anan_speaker_audio_test and anan_link_telemetry_test
start their own clients against 127.0.0.1, which send there too, so
under ctest -j a stray packet could fail a count. The three tests now
share a RESOURCE_LOCK. Full Suite runs ctest serially, so only a local
-j run was exposed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jensenpat jensenpat self-assigned this Oct 22, 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.

Final-head landing review at 818fb3c8ad1722743fc57b04e2629331cca158fb.

No remaining blockers. At the operator’s request, 818fb3c8a removes the UDP capture test, its registration/resource locks, and the otherwise unused AnanFrontEndTestAccess friend. The socket test was not executed during this landing. ctest -N -R '^anan_front_end_test$' reports Total Tests: 0; registration validation passes. Contributor commits and attribution are retained.

Cross-radio and seam audit:

  • RadioModel.cpp:740-750: the two new namespaced parameters are inside the existing ANAN-only branch of populateFamilyParams(). Flex’s connect path and Icom’s credentials/CI-V parameters remain unchanged.
  • RadioSetupDialog.cpp:5373-5380: live writes require the declared anan namespace and use the existing invokeBackendExtension() channel. Flex declares flex, Icom declares icom, and neither can receive this UI dispatch. No backend interface, capability record, raw Flex command, vendor include, signal route, or TX gate is added.
  • UI edits remain within buildAnanFrontEndTab(). Page visibility/capability policy is unchanged from approved RFC #6209; the existing disconnected page remains available for pre-connection configuration. Title-bar mute, shared audio routing, global theme tokens, common controls, and global defaults are unchanged.
  • The four architecture ratchets produce identical per-file findings at merge base a87f11c6 and final head: 101 pre-existing engine-boundary findings, no new violation; 70 frozen booleans; 125 pre-existing command-plane entries; touchpoint manifest unchanged. Color ratchet delta is zero on all three metrics.

Evidence on the final tree:

  • macOS Qt 6.12.0 RelWithDebInfo app and focused targets build successfully with 22 jobs; clean tree. Seven headless CTests pass: protocol, settings, family switch, seam affinity, seam consumers, Radio Setup routing, and Radio Setup window size. Family-switch proof includes Flex and Icom construction/teardown and stale-delivery isolation; it is not live firmware validation.
  • Three isolated protocol mutations are rejected: 22 MHz < changed to <= (1 failed assertion), ADC0 filter shifted into TX half (3), mute moved to bit 0 (1). Original implementation passes.
  • Final-head bridge, private settings, explicit /tmp/aethersdr-pr6673-land, and AETHER_AUTOMATION_NO_TX=1: Follow disables both bypass boxes and an attempted toggle returns disabled:true; Bypassed restores both; filter and amplifier-mute values survive process restart. Demo then reports serial:DEMO-0001, sliceCount:1, panCount:1, transmitting:false, and whoami.txAllowed:false. The ANAN controls become invisible on connected sim via the unchanged capability gate. Owned instances were closed after validation.
  • Registration, frozen CI gate, generated reference pages, seam probes, diff whitespace, and all 707 log-rule checks pass. Final-head hosted CI and Docs results were checked before approval.
  • Verified the external packet facts against p2app InHighPriority.c (receive words at 1430/1434 and mute at byte 1400 even on stop) and deskHPSDR new_protocol.c (SATURN/ORION2 ladder).

The prior review’s four nits are satisfied: corrected stop/bypass comments, connected-G2 qualification, and no remaining new listener/port-lock requirement after the requested test removal. The advisory CodeGuard comment’s prose/backtick, numeric-mask, field-description, and unrelated-file hits do not identify executable injection or exposed secrets in this diff. No unresolved threads or changes-requested reviews remain.

Limits: no live Flex/Icom/G2 or RF/TX test was run in this pass. Contributor G2 bench evidence is attributed, not reproduced; quiet-band passbands and audible idle unmute remain unverified. The local build lacks QtKeychain, DFNR, and sherpa-onnx. Automated code-review was unavailable; the adversarial pass was performed manually. No post-merge CI result is claimed.

Generated with OpenAI Codex (GPT-6 Astra)

@jensenpat
jensenpat merged commit f8d46dc into aethersdr:main Oct 22, 2026
11 checks passed
@tropo1234
tropo1234 deleted the anan-filter-bank branch October 22, 2026 20:52
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.

2 participants