Repository navigation
RTL: analog multi-receiver audio, meters and release packaging - #6083
Conversation
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
There was a problem hiding this comment.
Issue fit
Yes, with one decision deferred. There is no Fixes #NNNN; the governing document is RFC #5468, whose own review-scope table names this PR: "#6083 — Analog multi-RX, SQL, meters, DAX/TCI/APRS routing and RTL packaging … Official builds require RTL; optional Digital remains OFF." Every feature group in the body maps to hunks in the diff, and I found no body claim the code contradicts. The RFC also rules that "Release packaging is an infrastructure/code-owner review" — so packaging is in scope here but routed to a different reviewer than the DSP work. That text lives in the RFC body (author-maintained), so whether a code owner has signed off on making RTL a hard release dependency is still open, not settled.
Reviewed increment only: 5e39790a..4b1816ac (124 files, +6655/-503), the single commit on top of #6082. The upstream stack (#5924 → #5973 → #5974 → #6082) is not re-reviewed here. Merge-base with main is 68cdf661.
CI on 4b1816ac: Full test suite — success, Static checks — success, Sanitizer option configures — success. The body said the full suite had "no completed verdict yet"; it has now passed, which is stronger evidence than the 39 local tests. I also re-ran the Python gates in the head checkout: touchpoint manifest up to date, test registration OK, capability records at the frozen 71, engine boundary 0 blocking, command plane 413/413 baseline, CI test gate matches the frozen list.
Scope
Grouped by feature — 124 files across ten concerns don't read usefully one row per file.
| Feature group | Representative files | Claimed? | Verdict |
|---|---|---|---|
| 2/4/8 RX bank + evaluation admission | RtlSdrBackend.cpp, RtlSdrWorker.*, RtlReceivePipeline.*, RtlCaptureTransaction.* |
yes | in scope |
Manual/Auto SQL + schema-1 automatic/marginDb |
RtlSquelchGate.h, RtlSliceSettings.cpp, SliceModel.*, AutomationServer.cpp |
yes | in scope; new persisted + bridge surface → maintainer decision |
Relative meters + receiveMetersEnabled + rtl/receive_meters.set |
MeterModel.*, MeterDef.h, SMeterWidget.*, SmartMtr*, VfoWidget.*, RtlDeviceSettings.cpp |
yes | in scope; new persisted key + new extension verb → maintainer decision |
| Native DAX receive | DaxReceiveModel.*, DaxApplet.*, PipeWire*, VirtualAudioBridge.*, hal-plugin/AetherSDRDAX.cpp |
yes | in scope; see finding 2 |
| Selected-receiver APRS / AX.25 receive | Ax25ReceiveModel.*, Ax25HfPacketDecodeDialog.*, MainWindow_DigitalModes.cpp |
yes | in scope |
| Stable TCI receiver mapping | TciServer.* |
yes | in scope |
| Spectrum averaging across monitor edits | SpectrumTemporalAverage.h, SpectrumWidget.* |
yes | in scope |
| WFM applet follows selected receiver | WfmApplet.*, RxApplet.*, AppletPanel.* |
yes | in scope |
| Release packaging | 4 workflows, CMakeLists.txt, cmake/AetherRequireRtl.cmake, 5 setup scripts, rtl-dependencies.json, check_rtl_package.py, test_require_rtl.py, build-installer.sh, build-windows-local.ps1 |
yes (title) | in scope per RFC #5468, routed to code-owner review |
ENABLE_HD_FM reserved option whose only effect is FATAL_ERROR |
CMakeLists.txt |
yes | in scope; nit |
AETHER_SETTINGS_DIR now suppresses profile migration + log pruning |
src/main.cpp |
no | explained by the isolated-evaluation requirement in rtl-multirx-analog.md; please add a body line |
PortAudio -Wno-error=implicit-const-int-float-conversion; *_LINK_LIBRARIES prefix preservation; macdeployqt added to the local macOS installer script |
setup-macos-deps.sh, CMakeLists.txt, packaging/macos/build-installer.sh |
no | consequences of the new pinned-prefix setup; correct, but undisclosed |
Nothing here is unrelated to the issue; the three undisclosed rows are all downstream of changes the body does claim. No scope blocker. Two rows add durable third-party-visible surface (a persisted schema field pair, a persisted device key, an extension verb) — flagged for a maintainer, not as a violation.
Blockers
1. Every packaging change in this PR has never executed, anywhere. All three release workflows are on: push: tags: ['v*'] + workflow_dispatch (appimage.yml:3-5, macos-dmg.yml:3-5, windows-installer.yml:3-5), and get_check_runs on 4b1816ac returns only Static checks, Sanitizer option configures and Full test suite. So REQUIRE_RTL=ON configure, setup-rtl-deps.sh/.ps1, the check_rtl_package.py invocations, the source/notice staging and the Windows POST_BUILD DLL loop will all run for the first time on a release tag. The body concedes "No new current-head Windows package … is claimed," but the consequence isn't stated: a mistake in any of those steps surfaces as a failed release, not a failed PR.
This is cheap to clear and I'd rather see it cleared than argued: workflow_dispatch all three on this branch and link the runs. If any can't be dispatched from a non-default branch, say so and I'll drop it to a nit. Inline on appimage.yml:252.
2. The Windows arm of check_rtl_package.py is materially weaker than the macOS arm, in exactly the place the new pthreads dependency lands. macOS walks every Mach-O's LC_RPATH/otool -L transitively and fails on any dependency not resolvable inside the payload. Linux checks that each rtlsdr/fftw3f/libusb DT_NEEDED name exists in usr/lib. Windows only checks that the three DLLs sit beside the exe and that the app's own import table mentions rtlsdr and fftw3f — it never walks rtlsdr.dll's imports for reachability.
rtl-release-packaging.md asserts "The pthreads source/build is a Windows upstream configure prerequisite; it is not an imported runtime of the library-only RTL target." I can't test that without a Windows build, and the deploy step copies only third_party\rtl-deps\bin\*.dll (rtlsdr.dll + libusb-1.0.dll), so pthreadVC2.dll never ships. If that assertion is wrong, the installer ships an rtlsdr.dll that cannot load, and the validator passes anyway. Either extend the Windows arm to dumpbin /dependents on rtlsdr.dll and assert every non-system import is present, or paste the dumpbin /dependents rtlsdr.dll output as evidence. Inline on check_rtl_package.py.
Nits (non-blocking)
drainFdis opened for every RX pipe, not only receive-only ones (PipeWireAudioBridge.cpp:312), so the pre-existing Flex DAX path changes too: AetherSDR now permanently holds a second reader on each FIFO, meaning the write side can no longer observeEPIPE/"no readers" ifmodule-pipe-sourcegoes away — writes fill the kernel buffer and then failEAGAIN, and the::write()return at line 459 is unchecked. AdrainFdopen failure also now aborts a channel that previously opened fine. Reasoned from code; not reproduced. Gating the::openonm_receiveOnlywould confine both to the new path.- Same function:
if (actual < capacity && m_receiveOnly)returns before theif (actual < 0)debug branch, so that branch is unreachable in receive-only mode. appimage.yml:252builds libusb + librtlsdr + float FFTW from source on every run with noactions/cache, whilewindows-installer.yml:137-147got a cache step andmacos-dmg.ymlfolds into the existing one. Linux release builds pay the compile each time and widen the per-build network surface.- HAL plugin
AetherSDRDAX.cpp:94: thecompare_exchange_strongis a real improvement over the barestore, but it can't detect an ABA reset to the same cursor value. Worth a comment so the next reader doesn't over-trust it. - The
ENABLE_HD_FMoption exists only toFATAL_ERRORwhen ON. Reserving the name is defensible; a one-line comment saying it is a reservation (not a buildable feature on this branch) would help. - GitHub's
archive/<sha>.tar.gztarballs are not guaranteed byte-stable across server-side git/compression changes, sortl-dependencies.json'srtlhash can break without the pin moving.git clone+git checkout <sha>or a release asset is sturdier.
What I tried to break (and could not)
- The evaluation-capacity gate.
RtlSdrBackend.cpp:131-137requiresAETHER_AUTOMATION == "1"and an exact"2"/"4"/"8", is#if-gated to the three declared platforms, and never persists.rtl_backend_test.cpp:56-71tests the env var alone (refused), the pair (accepted), and"9"(refused) — it fails if either half of the guard is removed. Doc and code agree;kQualifiedReceiverCapacitystays 1. - The
automatic/marginDbschema addition. Both fields are optional,marginDbis range-checked 5–20, non-boolautomaticis rejected, andreceiveMetersEnableddefaults totruewhen absent — so older documents load unchanged. I then tried to reach thereturn falseonautomaticSquelch && !squelchEnabled(RtlSliceSettings.cpp:144) from the UI:setSliceAutoSquelchforcessquelchEnabled = truewhen enabling (RtlSdrBackend.cpp:957) andsetSliceSquelchclearsautomaticSquelch(:940), and both the pipeline (RtlReceivePipeline.cpp:78) and the transaction (RtlCaptureTransaction.cpp:246) reject the combination at admission. The invalid state can't be produced, so the whole-document rejection is defensive only — and whole-document rejection is this schema's pre-existing contract, not a new behaviour. - Supply-chain ordering in the new setup scripts. Every URL in
rtl-dependencies.jsonis HTTPS; the RTL pin is an immutable commit SHA.fetch()insetup-rtl-deps.shand the loop insetup-rtl-deps.ps1both verify beforetar, and verify on a cache hit too, not only after a download._verify_sha256.shfails hard even when no hasher exists.setup-fftw.ps1movesConfirm-Sha256out of the download-if-absent block, so a cached zip is now verified — a real hardening improvement in this PR. I could not verify the five hashes themselves: this sandbox's proxy blocksfftw.organdcodeload.github.com, so treat the pin values as unverified by me. tools/test_require_rtl.py. I expected a tautology — 17 lines claiming 16 combinations. It isn't: it writes a probe script thatinclude()s the realcmake/AetherRequireRtl.cmakeand runscmake -Pfor all 16 products, asserting the exit code againstnot required or (enabled and rtl and fftw). It would catch an inverted or dropped condition. I couldn't run it (nocmakeon this box; it failsFileNotFoundError, which is also why it must stay out of any environment lacking cmake).- CMake ordering.
AetherSDRandaetherdare defined atCMakeLists.txt:1614/:1624, well before thePOST_BUILDloop at:2733, so theforeach(_rtl_app …)cannot hit "no TARGET has been created".RTL_FFTW3F_TARGETis set at:2697/:2700in the same directory scope the NR4 hunk reads at:3114, so that hunk is live, not dead. Thefind_path/find_libraryfallbacks do set the*_FOUNDvariablesaether_require_rtl()tests. - The
control_receive_test.cppedits. The body says two outdated assertions were updated. That is exactly what changed: the filter-mode count/WFM contract and thedBFS/bin→dBFS/2048-binunit label. Nothing else in that file moved, and the WFM branch is aconstexprfollow ofkQualifiedWfmEnabledrather than a hardcoded pass. - AX.25 teardown.
Ax25ReceiveModel::~Ax25ReceiveModelclearsstate->ownerand callsretire()under the mutex beforethread.quit()/wait(), so an in-flight worker result finds a null owner and is dropped rather than delivered into a half-destroyed model. Matches thedecoder-pcm-contract.mdclaim. - HAL memset bounds.
totalSamples = bytesCount / sizeof(float), so bothmemset(dst, 0, bytesCount)andmemset(dst + toRead, 0, (totalSamples - toRead) * 4)stay inside thebytesCountCoreAudio provides, including a non-multiple-of-4bytesCount. - Socket-test sweep (§2 disclosure). I grepped the added and modified tests in this PR's own sources, not the working tree:
ax25_receive_model_test.cpp,dax_receive_applet_test.cpp,pipewire_rx_bridge_test.cpp,tci_rx_audio_test.cpp,rtl_backend_test.cpp,control_receive_test.cpp. No test opens, binds, listens or connects. The only hits were a local helper namedbind(model, fixture)in the AX.25 test and aQWebSocketProtocol::CloseCodecallback on an injectedm_ioseam in the TCI test. The body's "eight concurrent TCI clients" is driven through that injected worker, not sockets — nothing to record, and no socket test was removed.
Everything above is reasoned from the head checkout, not reproduced at runtime. I have no build, no test execution and no GUI here, so I cannot confirm the 39-test local result, the Apple Silicon build, or the unresolved intermittent WFM-receiver withdrawal the body honestly leaves open. That last item is the one I'd most want settled before a capacity claim, and the body is right not to call three passing reruns a root cause.
The required CodeGuard security audit was launched over this file list; it had not reported when I posted, so I ran the highest-value checks from its brief myself — the SHA-before-extraction ordering, URL schemes, HAL memset bounds, workflow triggers and drainFd lifetime, all above. I did not independently confirm the 1082 bulk CodeGuard hits; the CS-PII-*, CS-SEC-AWS-SECRET, CS-SEC-PASSWORD, CS-VLN-XSS-* and CS-VLN-SQLI-SLEEP categories have no plausible home in a C++/YAML/shell tree like this one and I am not repeating them. The CS-VLN-CMDI-BACKTICK/-PIPE/-SUBSHELL hits I did check (appimage.yml:77,131,258,325,359,445; static-checks.yml:14,36,81,…) land on lines this PR does not touch and carry no untrusted input, so I'm dropping them rather than forwarding noise.
Recommendation
Needs maintainer decision, and specifically two different maintainers. The DSP/model half is the strongest-tested part of this PR — the schema additions are backward-compatible and range-validated, the evaluation gate is genuinely mutation-resistant, the setup scripts verify hashes in the right order, and every static gate plus the full Linux suite is green. I tried the obvious ways to break the capacity gate, the squelch invariant and the AX.25 teardown and the code held.
The packaging half is where I'd hold. Per RFC #5468 it belongs to a code owner, and the two blockers are both about evidence rather than design: a release path that has never run once, and a Windows validator that would not catch the one dependency the docs assert is absent. Concrete next step: workflow_dispatch the three packaging workflows on this branch and link the runs, and either strengthen the Windows arm of check_rtl_package.py or paste dumpbin /dependents rtlsdr.dll. Both are cheap and would turn the largest remaining unknown in this PR into a fact.
Thanks for splitting the digital decoder out — reviewing the analog tree on its own is a great deal easier, and the body's willingness to name what isn't proven (no fresh Windows package, no Linux ARM qualification, the unresolved WFM withdrawal) made this review much faster than it would otherwise have been.
🤖 aethersdr-agent · cost: $21.6032 · model: claude-opus-5
|
Three addenda to my review above — one correction of mine, one question, one piece of reading I finished afterwards. Correction to my own A question, not a finding, about
Binding keyed on Finally, on process: the CodeGuard security audit I noted in the review as not-yet-reported is still running. If it surfaces anything I did not already cover, I will verify it against the checkout and post it here rather than opening a second review — credited as "security-audit (CodeGuard)". The two blockers and the recommendation in my review stand as written either way. 🤖 aethersdr-agent · cost: $26.4508 · model: claude-opus-5 |
|
security-audit (CodeGuard) has completed. It surfaced two leads I had not covered. I verified both against the diff with Not a blocker for #6083 — but worth its own issueThe macOS DAX transport is POSIX shared memory on a predictable global name, created world-readable and world-writable:
The reason I'm raising a pre-existing issue at all is that
So the project has already ruled that mode-0666 DAX audio transport on a predictable path is a vulnerability, and fixed it for the Linux FIFO path. The macOS shm path appears never to have had the same treatment. The same two consequences read across: a local user can read operator RX audio, or write into the segment on a path that ends up as transmitted audio under the operator's callsign. There is also a pre-creation variant — squat the name at a smaller size and the "reuse" branch wins without any size or owner check, which on the HAL side faults inside a privileged audio daemon. I'd suggest a separate security issue (plausibly a GHSA, given the precedent), not an expansion of this PR. The shape of the fix mirrors the one already shipped on the Linux side: create with One small thing that is adjacent to the new code
Correcting myselfIn thinking about why the shm surface matters here I assumed this PR widened DAX from four channels to eight, because What the audit independently confirmedUseful as a cross-check on my own review rather than as new findings, and all of it matches what I posted:
My two blockers and the Needs-maintainer-decision recommendation stand unchanged. Nothing the audit found bears on either, and nothing it found is a reason to hold this PR. 🤖 aethersdr-agent · cost: $29.2118 · model: claude-opus-5 |
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
AppImage packaging is red on both architectures at
|
| Check | Result |
|---|---|
| Static checks | ✅ success |
| Sanitizer option configures | ✅ success |
| build-dmg (macos-15, apple-silicon, arm64) | ✅ success |
| build-dmg (macos-15-intel, x86_64) | ⏳ still running |
| build-windows | ⏳ still running |
| build-appimage (ubuntu-22.04, x86_64) | ❌ failure |
| build-appimage (ubuntu-24.04-arm, aarch64) | ❌ failure |
| Full test suite |
What failed, and what it is not
On the aarch64 leg the failing step is Build AppImage. Everything before it is green:
Build pinned RTL dependencies (including float FFTW)✅ —setup-rtl-deps.shbuilt libusb, rtl-sdr and float FFTW from sourceConfigure✅ — so-DENABLE_RTL=ON -DREQUIRE_RTL=ONresolved the pinned prefix correctlyBuild✅ andInstall to AppDir✅ — nothing fails to compile or install
So this is not a compile failure and not a missing-RTL configure failure. It is packaging.
It is also not ARM-specific and not CI infrastructure: the x86_64 leg went red too, and the only thing that changed for both legs is this PR's RTL packaging. (My tooling only returns the first failing job's step breakdown, so I can see the step name for aarch64 but not for x86_64 — if x86_64 died somewhere else, that's worth knowing and would change the picture.) The macOS arm64 DMG passing is useful evidence in the other direction: the shared setup-rtl-deps.sh and the macOS branch of check_rtl_package.py are fine. It's the Linux path.
The step now does two things, and I can't tell which one broke
At the PR head, .github/workflows/appimage.yml Build AppImage (≈ L412–426) runs linuxdeploy and then, newly:
python3 tools/check_rtl_package.py AppDir --platform linux
I can't read the step's output from this environment (the log API is blocked here), so I'm not going to guess at an error string. The single most useful thing you can paste is the tail of that step, and specifically: did RTL-PACKAGE-OK: print before the job died?
- If it printed → linuxdeploy itself failed, or the Wayland assertion/mksquashfs stage did.
- If it didn't → the new validator refused the AppDir, and its message names the exact library.
Where I'd look first
The Linux branch of tools/check_rtl_package.py is strict about location, not just presence:
libdir = root / "usr/lib"
...
if any(token in name for token in ("rtlsdr", "fftw3f", "libusb")) and not (libdir / name).is_file():
raise SystemExit(f"Unreachable RTL dependency: {binary}: {name}")and the earlier rglob gate raises Missing RTL package dependency: libusb-1.0.so* if the library isn't anywhere in the AppDir at all. The PR adds third_party/rtl-deps/lib to LD_LIBRARY_PATH so linuxdeploy can resolve the DT_NEEDED entries — but resolving is not the same as copying. linuxdeploy carries a hardcoded exclude list of libraries it deliberately refuses to bundle (host-provided things it expects to find on the target system), and it matches by soname, not by path — so a library on that list is skipped even when it came from your own prefix. Worth checking whether libusb-1.0.so.0 is on the list for the linuxdeploy build the Download linuxdeploy + Qt plugin step pulls.
If that's the cause, LD_LIBRARY_PATH can't fix it; the lever is naming the libraries explicitly on the linuxdeploy invocation (--library <path>, if the downloaded build has it) so they're staged regardless of the exclude list — or --exclude-library handling, depending on version.
One process suggestion
Split Build AppImage into two steps — linuxdeploy, then check_rtl_package.py. Right now a red there is ambiguous between "bundling broke" and "the new validator rejected the payload", which is the position this comment is in. Two steps makes the next failure self-describing, and it's cheap.
Full test suite is cancelled, not passed
The Linux full suite run linked in the PR body as current validation came back cancelled — probably superseded by a newer push. Cancelled isn't a pass, so that link doesn't yet carry evidence for this head and will need a re-run. Your body is already careful to say "dispatch is not a pass", which is the right instinct; this is just the specific one to redo.
Still open
Windows and the Intel DMG are still running. Copilot's question about whether rtlsdr.dll's own imports resolve without pthreadVC2.dll will get a real answer from the Windows leg — if docs/rtl-release-packaging.md's assertion is right, the installer build and its check_rtl_package.py --platform windows pass; if it's wrong, you'll see it there rather than having to reason about it. I'll hold off on that one until the job finishes.
The nit about no actions/cache for third_party/rtl-deps is real but orthogonal — it only costs wall-clock (libusb + rtl-sdr + float FFTW rebuilt from source on every leg, every run), and while you're iterating on this failure that cost will add up.
Happy to re-check once you've got the step tail or a fresh run.
🤖 aethersdr-agent · cost: $5.9494 · model: claude-opus-5
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
…#5924) ## Summary RTL receive changes now publish only after hardware readback and DSP adoption. Capture browsing preserves each slice's frequency and settings, parks receivers outside usable capture, and resumes them when capture returns. Offscreen markers reveal the receiver through a confirmed capture transaction. Zoom uses a continuous 65,536-sample FFT (36.621 Hz bins at 2.4 MS/s), independently of audio and capture bandwidth. FM/FM-N have symmetric capability-bounded filters, 48 kHz per-receiver PCM, squelch, session/revision fences, rollback and accepted-state persistence. Requested or refused state never becomes saved live state. Spectrum averaging preserves RF history through display movement, and coherent capture coverage supports the 3D waterfall. PPM/DC settings use confirmed backend controls. Production admission at this checkpoint remains one receiver. Implements the bounded receive runtime of RFC #5468; respects the backend seam obligations in #5554/#5262. WFM stereo, optional digital decoding, live PPM interaction and multi-RX delivery are downstream increments. ## Integration and validation Reconciled with main at `a798558ac` (#6118). The reconciliation preserves main's typed tune/filter/mode/APF intents, HL2 squelch and WDSP changes. Each slice's SQL intent uses the existing common audio-intent binding once; the merge does not add a duplicate backend connection. The confirmed-pan dispatch comment now distinguishes an accepted request awaiting observation from refusal. Runtime documentation now describes the implemented 65,536-bin display FFT. Checkpoint `efc358a1` native Apple Silicon/Qt 6.12 build and CTest: **5/5 pass**, no skips, in 11.29 seconds (`receive_intent_routing_test`, `slice_apf_agc_off_seam_test`, `flex_slice_mode_intent_test`, `rtl_model_acceptance_test`, `slice_model_letter_test`). These are socket-free model/dispatch regressions; they do not claim live-radio, GUI or hardware timing qualification. The downstream #6083 application and broader tests are separately revision-pinned there. This requests human review and preserves the outstanding in-scope maintainer decisions; it does not mark the free-pan amendment approved. HD decoding is outside this PR and HD consent is not a merge condition here. Upstream's single spectrum outlet and socket-free test rehoming are preserved. The real model derives paced waterfall rows from spectrum observations; retired orphan waterfall signals are not restored. The new upstream receive-contract test now expects RTL's implemented FM/FM-N filter capability. Generated seam/touchpoint manifests are regenerated. Earlier downstream aggregate evidence (not rerun on this individual head): the aggregate builds AetherSDR with native Apple Silicon/Qt 6.12, RTL required and the optional decoder OFF. Focused downstream tests already pass for Flex mode dispatch, confirmed RTL model/capture state, FFT averaging, WFM, the PPM widget and seam consumers. This is aggregate evidence, not a claim that every platform was rerun on this individual head. Fresh hosted checks are required on this PR. The operator reports that the delivered `7601593f` build ran stably for days. That is live hardware feedback on the delivered build, not an instrumented soak of the new upstream integration. Current-head sanitizer, ARM throughput and full release-artifact qualification are not claimed. ## CI integration repair Upstream Radio Setup test targets (including #6008's `peripheral_auth_dialog_test`) compile `RadioSetupDialog.cpp`, so they also list the RTL widget and its availability registry. Both repaired tests build and pass on the native Mac decoder-OFF integration (2/2, 4.00 seconds). This follow-up changes test linkage only; earlier application/regression measurements retain their stated revisions. New-head CI is required and remains distinct from those measurements. ## Maintainer decisions All of the decisions below were made by Jeremy, the maintainer. - **2026-09-23 rulings.** The [mode-transition, seam and scope rulings](#5924 (comment)) stand. - **2026-10-03, free capture browsing.** The RFC #5468 amendment of 2026-09-25 (free capture browsing with parked slices) is **approved as RTL-only**; other backends keep their existing pan behavior. - **2026-10-03, later seam additions.** The seam pieces added after 09-23 are **ratified as RTL-owned**, on the same terms as 09-23: a second backend must justify reuse. They are `SquelchLevelScale`'s `unit` and `modesExclusive` fields; `requestReceiveTune(view)`, `recenterReceiveCapture` and `ReceiveCapturePlacement`; and the `BackendPanAveraging` persistence/description fields. Averaging persistence and coherent 3D presentation are part of that ratification. - **2026-10-04, one squelch record.** RTL's squelch is described by #6127's `RadioCapabilities::squelchLevelScale`, not a second record. `unit` carries RTL's dBFS/bin, and `modesExclusive` marks the record as the receiver's own gate. HL2 FFT-FPS/dBm persistence coordination stays separate, with sibling ClientDisplay tables preserved. ## New gui/ touchpoints (ratified) | Header | New includer | Why | |---|---|---| | `core/backends/RadioCapabilities.h` | `src/gui/ModeFilterPresets.cpp` | FM/squelch availability reads the declared `ReceiveFilterControl`/`SquelchLevelScale` records | | `models/PanadapterModel.h` | `src/gui/PanFrameGuard.h` | Snapshots the pan's geometry when a frame arrives, so a confirmed FFT is never painted against a newer range | | `models/RadioModel.h` | `src/gui/ReceiveCaptureAction.h`, `src/gui/RtlReceiverSettingsWidget.cpp` | Capture-recenter action and the RTL device settings both go through RadioModel's capability-gated verbs | | `models/SliceModel.h` | `src/gui/MainWindowHelpers.cpp` | `requestSlicePanCenter` resolves the slice's pan and sends confirmed backends a Center tune instead of a pan request | ## Effects on other radios - **Squelch availability** follows one rule in both the RX applet and the VFO (`ModeFilters::squelchAvailableInMode`). A `squelchLevelScale` with `modesExclusive` (RTL only: FM/FM-N, −120 + 1.2·level dBFS/bin, Auto SQL) lists the supported modes, keeps each receiver's manual/Auto intent, and has Auto start from the absolute threshold. Otherwise the #2504 rule disables SQL in DIGU/DIGL/NT/RTTY/CW, unless the radio declares mode-independent squelch. Every other family's scale leaves `modesExclusive` false, so its SQL line, Auto SQL and availability are unchanged. - **SQL's unavailable reason** stays on the button. Where SQL is unavailable in the current mode, the RX applet and the VFO no longer replace the registry's accessible reason with the Auto-SQL description. - **FM-family passband availability** follows `ModeFilters::fmFilterAdjustable`. Declared FM rows are authoritative (RTL). A radio-owned filter with no FM row refuses FM edits (Flex, per FlexLib `Slice.cs`). HL2, ANAN, Icom and the Demo keep their existing adjustable passband. - **WFM/WBFM** use carrier-centered FM edges and no longer inherit the SSB preset ladder. - **Cancelled VFO drags** release the drag hold without the final retune (`SpectrumWidget::sliceDragCancelled`). - **Off-thread `SliceModel` setters** are refused and logged. ## CI The Linux `build` job now deletes unused host toolchains (Android, .NET, GHC, CodeQL) before building. The full RelWithDebInfo tree, which now includes this PR's RTL tests (about 3.6 GiB of test binaries), outgrew the hosted runner's free disk. Jeremy authorized the workflow change. ## Integration with #6107 #6107 gated a stranded RTL worker on the `connectRadio()` wiring this PR replaces. This PR's `startCapture()` already detaches the worker before stopping it, and gates every relay on producer identity and the capture session, so that wiring is kept. #6107's read-error guarantee is pinned in `rtl_capture_worker_test`'s stop-timeout block. That test fails if the producer gate is removed. Principles II, V and XIV govern confirmed state and persistence; VIII/XI govern evidence and regression tests. --- 73, Ozy **K6OZY** · Codex Generated with OpenAI Codex (GPT-6 Astra) --------- Co-authored-by: jensenpat <patjensen@gmail.com> Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Jeremy [KK7GWY] <105957118+ten9876@users.noreply.github.com>
ten9876
left a comment
There was a problem hiding this comment.
This implements the analog #5468 delivery increment at e03365cd9ee855bb570fce4afc535475efe4cda5: evaluation receiver banks, receiver-specific audio consumers, squelch/meters and required RTL packaging. The analog/Digital split is real, and normal-launch admission remains one receiver. Request changes: the exact-head Linux packaging run fails on both supported architectures because the promised pinned libusb runtime is not bundled.
Scope
| Files/group | Claimed? | Verdict |
|---|---|---|
| RTL backend/worker/pipeline, SQL, settings and meter state | Yes: 2/4/8 opt-in banks, independent controls, truthful meters | In scope; production default remains one |
| WFM/RX/VFO/meter/settings applets and model wiring | Yes: selection, stale-binding protection, relative presentation | In scope; existing controls remain capability-shaped |
TciServer, DaxReceiveModel, native bridges/HAL |
Yes: stable TCI mapping and receive-only DAX | In scope; preserve existing Flex TX ownership |
Ax25ReceiveModel, decoder/dialog wiring |
Yes: selected-receiver APRS | In scope; decoder source/worker epochs are explicit |
| CMake, dependency recipes/manifests, release workflows and package checker | Yes: mandatory RTL, Digital OFF, portable runtime/source/notices | In scope, but Linux delivery currently fails |
src/main.cpp isolation and other-family audio/build effects |
Explicitly disclosed | In scope; not misrepresented as RTL-only |
| Tests, generated manifests, runtime/package docs | Yes | In scope; full Git inventory is 125 paths (API summary stops at 100) |
No unrelated Digital decoder or default multi-receiver enablement found. Reviewed the actual parent-branch increment, not the optional Digital follow-on.
Blocker
-
Bundle pinned libusb before building the AppImage.
.github/workflows/appimage.yml:405relies on automatic dependency deployment, but linuxdeploy blacklists this dependency. Both aarch64 and x86_64 in the exact-head AppImage run 37098245180 say:Skipping deployment of blacklisted library .../third_party/rtl-deps/lib/libusb-1.0.so.0 Missing RTL package dependency: libusb-1.0.so* Process completed with exit code 1.The AppImage itself is assembled before the new validator at line 411 refuses it; neither architecture produces the promised qualified delivery. This is distinct from the earlier Wayland filename failure, which this head has passed. Explicitly stage the pinned SONAME library under
AppDir/usr/libbefore the linuxdeploy invocation /--output appimage, keeping the validator enabled. The direct staging line is:cp -L third_party/rtl-deps/lib/libusb-1.0.so.0 AppDir/usr/lib/libusb-1.0.so.0
Then rerun both AppImage legs and retain their
RTL-PACKAGE-OK/artifact evidence. Do not replace the missing library with a host-only dependency or weaken the presence check.
Non-blocking notes and qualification limits
tests/tests.cmake: exact-head Full Suite run 37098243451 stopped while linkingtx_applet_power_reconciliation_test:/usr/bin/ld: final link failed: No space left on device. This is runner storage exhaustion, not evidence that this PR's test assertions failed. The body should update its “in progress” statements and obtain a completed qualification run after restoring runner capacity.src/core/backends/rtl/RtlSdrBackend.cpp:131: keeping 2/4/8 behind bothAETHER_AUTOMATION=1and the explicit evaluation count correctly avoids inferring Linux ARM qualification from Mac ARM. No current-head hardware soak, sanitizer or per-architecture integrated CPU headroom was established here; preserve the one-receiver production boundary until the RFC gates are met.src/core/backends/rtl/RtlReceivePipeline.cpp: the disclosed historical intermittent withdrawal still lacks a demonstrated root cause. The bounded-exhaustion test below proves its intended refusal path, not that every earlier real-time withdrawal was explained.
What I verified and tried to break
- Exact-source standalone production DSP harness, GCC 16.2.1 / Qt 6.11.2, isolated FFTW wisdom:
rtl_squelch_test,rtl_receive_pipeline_test, andrtl_wfm_pipeline_testcomplete without assertion failures. The WFM suite exercises multiple native analog receivers with independent signal content and mono/stereo state, and reportsrtl_wfm_pipeline_test: 0 failures; intentional held-consumer exhaustion reportssuccessful_blocks=8 repair=1. - Automatic-SQL negative control: replaced its estimated floor-plus-margin threshold with a fixed floor. The test fails; restoring exact production source restores the passing test. This pins actual noise rejection, not merely a settings round-trip.
- Python Windows dependency-closure tests pass 4/4 (transitive missing DLL, nested-copy refusal, system imports/cycles, parse refusal); REQUIRE_RTL configuration matrix passes 16 combinations. These are injected parser/configuration tests, not native Windows artifacts. Linux deployment failure above is direct hosted evidence.
- Read source/retirement paths for sparse TCI binding identity, per-receiver DAX producer leases, receive-only bridge TX exclusion, APRS queue/reset generations, parked receiver silence and relative meter units. These paths beyond the DSP probes were inspected, not all replayed locally.
- Four boundary gates pass against head and parent, with the same 102 tracked engine findings, 71 capability booleans and 401 frozen raw commands; manifests current. The test preflight included the complete changed-file list: TCI sends are injected, and the PipeWire regression uses local FIFOs; no synthetic firmware/network peer was run.
Did not drive the GUI or access hardware: the demo does not provide RTL capture/multi-receiver USB or the platform DAX devices needed for these acceptance claims. Separate automated code-review skill unavailable. Native Mac/Windows artifacts, actual PipeWire/CoreAudio device delivery and fresh radio/ARM performance remain explicitly unverified by this review.
CI freshness: earliest head run 2026-10-03 04:56:21 UTC precedes current main 2026-10-04 18:26:40 UTC. Static checks are green, but strict: false does not make that a current-main build; branch-dispatch packaging tests the branch itself. Recommendation: Request changes for Linux packaging, then rerun the affected qualification and follow the stack merge order.
— Jeremy KK7GWY + Codex
ten9876
left a comment
There was a problem hiding this comment.
Issue fit: the analog #5468 delivery increment is real. It adds opt-in 2/4/8 receive banks, independent manual and Auto SQL, selected-receiver APRS, a stable TCI mapping, receive-only native DAX, relative meters and required RTL packaging. Normal launch still admits one receiver. Request changes: the Linux packaging this PR exists to deliver fails on both architectures, and the PR inherits #5973's stale base.
Reviewed d4ef62d5..e03365cd (this PR's own base..head) at head e03365cd9ee855bb570fce4afc535475efe4cda5. This is an independent second pass after the earlier ten9876 + Codex review on the same head.
Stack: main ← #5973 ← #5974 ← #6082 ← #6083. This PR's base is #6082's head branch.
Scope
| Files / group | Claimed? | Verdict |
|---|---|---|
| RTL backend, worker, pipeline, squelch gate, startup trace, settings, meters | Yes | In scope |
| WFM/RX/VFO/S-meter/SmartMtr applets and model wiring | Yes | In scope |
TciServer, DaxReceiveModel, VirtualAudioBridge, PipeWireAudioBridge/PipeWireNativeRxSource, hal-plugin/AetherSDRDAX.cpp |
Yes: stable TCI mapping, receive-only DAX | In scope, cross-family: Flex DAX and TCI users run this code. One undisclosed change is listed under nits. |
Ax25ReceiveModel, the AX.25 dialog, MainWindow_DigitalModes, plus a new DaxConsumer::Ax25Decoder in flex/PanadapterStream.{h,cpp} |
Yes: selected-receiver APRS | In scope. The Flex DAX-holder enum gains a value. |
RadioCapabilities.h (receiveAudioExport) and map row, declarations in every backend |
Yes | In scope. The map row is present (unlike #5974's broadcastFmReceive). |
touchpoint-tags.json: two new headers tagged universal |
Implicit | Fine. These are additions, not a re-classification of an existing vendor header. EB counts are unchanged. |
CMakeLists.txt, cmake/AetherRequireRtl.cmake, scripts/setup/*, tools/check_rtl_package.py, tools/test_* |
Yes: mandatory RTL, pinned deps | In scope. Maintainer decision (see below). |
.github/workflows/{appimage,macos-dmg,windows-installer,static-checks}.yml |
Yes | CI/release edits. Maintainer decision. |
src/main.cpp: AETHER_SETTINGS_DIR suppresses legacy-profile migration and moves log pruning |
Yes, disclosed as cross-family | In scope. The default path is unchanged: SettingsPaths::configDir() returns the same GenericConfigLocation/AetherSDR when the variable is unset. |
CMakeLists.txt:2733-2736: new option(ENABLE_HD_FM … OFF) whose only effect is FATAL_ERROR when ON |
Body: "keeps its existing name" | Not an existing option on main (git grep ENABLE_HD_FM origin/main finds nothing). See maintainer decisions. |
| Tests (25 files), generated manifests, docs | Yes | In scope. See the socket note below. |
No CHANGELOG entry.
Step-0 socket preflight: no new or modified test opens a socket.
tci_rx_audio_testconstructsTciServerbut never starts a listener. Its transport is injected, and itstests.cmakeblock says so.pipewire_rx_bridge_testuses a localmkfifoand does not runpactl.- The
bind(...)hits in the AX.25 tests are fixture helpers.
Blockers
-
The Linux AppImage fails on both architectures at this head. Confirmed in run 37098245180. The earlier review's finding holds. Both the aarch64 and x86_64 legs log:
Skipping deployment of blacklisted library …/third_party/rtl-deps/lib/libusb-1.0.so.0 Missing RTL package dependency: libusb-1.0.so* ##[error]Process completed with exit code 1.libusb-1.0.so.0is on the upstream AppImage excludelist (AppImageCommunity/pkg2appimageexcludelist, line 189), solinuxdeployat.github/workflows/appimage.yml:405will never bundle it, and the new validator at:411correctly refuses the result. There are two coherent fixes, and they are a packaging-policy call:- (a) Stage the pinned SONAME into
AppDir/usr/libbeforelinuxdeploy, as the earlier review suggested. This overrides the excludelist on purpose. - (b) Treat libusb as a host library, like the excludelist intends: every supported distro ships
libusb-1.0.so.0, and udev/kernel interplay is why it's excluded. Teachcheck_rtl_package.pyto accept a host-resolved libusb on Linux only.
Either way, a passing AppImage run on this head is the merge gate.
- (a) Stage the pinned SONAME into
-
Inherited: rebuild on the rebased #5973/#5974. Every head in the stack descends from the pre-squash #5924 snapshot and lacks
fe91a982f(SQL/FM-filter rules for non-RTL radios), which is now on main. A trial merge of this head against main conflicts in 61 paths. This PR edits the sameRxAppletandVfoWidgetsquelch code thatfe91a982frewrote (RxApplet.cpp+50/−,VfoWidget.cpp+71), so the new manual/Auto SQL must be re-expressed on top ofModeFilters::squelchAvailableInModerather than resolved in this branch's favour. It also has to route through #5919'srequestSliceSquelch/ReceiveDispatchpath, which landed on main after this stack forked.
For the maintainer (@ten9876)
- Official builds require RTL. Linux, macOS and Windows release workflows now pass
-DENABLE_RTL=ON -DREQUIRE_RTL=ON. A missing librtlsdr/libusb/float-FFTW fails configuration, and every installer ships those libraries. RFC #5468's 2026-10-02 scope note says "Official builds require RTL", but that text is the author's. I found no ruling from you that ratifies it. Your 09-07 conditional approval predates it. ENABLE_HD_FM. It is a new build option whose only behaviour on main is to refuse configuration, and it exists to reserve a name for draft #6002 / RFC #6079. I'd drop it here, together with the three-DENABLE_HD_FM=OFFworkflow flags, and let #6002 introduce it if that RFC is approved.- CI edits: a new Static Checks step (
tools/test_require_rtl.py) and the three release workflows. - The libusb packaging choice in Blocker 1.
Non-blocking
- Body is stale. It says "Intel Mac and Windows remain in progress", but the Windows Installer (37096568845) and macOS DMG (37096567337) runs at
4a242e4fboth completed successfully. The "corrected full suite … in progress" run 37098243451 failed at link time withNo space left on device(runner storage, not a test assertion). No completed full-suite run exists for this head. - Undisclosed cross-family TCI change.
TciServer::sendInitBurst(TciServer.cpp:3120-3145) now posts the whole init burst as one worker job instead of onesendClientTextper command. The rationale is sound (8 receivers × 8 clients would overflow the 256-job mailbox). Message order and the per-messagesendTextlifetime/backlog checks are preserved. This is the WSJT-X reconciliation path every Flex TCI user hits at connect, so please name it in the body. - Unresolved withdrawal. The intermittent Mac WFM receiver withdrawal is still unexplained. It is disclosed; keep the one-receiver production default until it is understood.
What I verified, and what I tried to break
- Re-checked the prior review.
- The AppImage blocker is real: I re-read the failed logs above.
- The "Full Suite stopped on disk" reading is right.
- The scope claims hold.
- The prior review didn't flag
ENABLE_HD_FMbeing new to main, the stale "in progress" statements, the TCI init-burst change, or the #5973 base problem.
- Receive-only DAX TX safety.
PipeWireAudioBridge::open(…, receiveOnly=true)creates no TX pipe and starts no TX poll timer.setTransmitting()returns early when receive-only, and the Flex path (receiveOnly=false) keeps the original sink and timer.openRxDrainis a no-op for Flex, so the sole-reader/EPIPE behaviour is untouched there. - Evaluation gate.
RtlSdrBackend::RtlSdrBackend(RtlSdrBackend.cpp:123-137) raises capacity only whenAETHER_AUTOMATION=1andAETHER_RTL_EVALUATION_RECEIVERS ∈ {2,4,8}, on the three qualified OS/CPU pairs only. A normal launch, or Linux ARM, stays at one receiver. main.cpppath change. WithAETHER_SETTINGS_DIRunset,SettingsPaths::configDir()returns exactly the old literal, so ordinary users' log directory doesn't move.- Built this head and ran two focused tests (Arch/GCC, isolated settings):
pipewire_rx_bridge_test→PipeWire bridge: 0 failures;rtl_squelch_test→ exit 0. - Mutation: removed the receive-only early return in
PipeWireAudioBridge::setTransmitting(PipeWireAudioBridge.cpp:501).pipewire_rx_bridge_teststill passes (0 failures). Low impact — with no TX pipe the only effect would be an RX silence fill if a TX edge ever reached a receive-only bridge — but the receive-only TX exclusion the body leans on is pinned only by construction (loadPipeSinknot called), not by a test. Non-blocking; a one-assertion test onisReceiveOnly()+setTransmitting(true)leavingm_transmittingfalse would pin it. Restored, sha256 matchesHEAD. - Demo drive (bridge, offscreen, isolated
AETHER_SETTINGS_DIR,AETHER_AUTOMATION_NO_TX=1, socketaethersdr-pr6083). Connected by raw{"cmd":"connect","action":"local","value":"serial DEMO-0001"};get radio→"model":"AetherSDR Demo","serial":"DEMO-0001". On a non-RTL radio the WFM surface stays out of the way:dumpTreeshows theWFMbar buttonvisible:false,ContainerWidget WFM/WfmAppletvisible:false, andwfmAudioMode/wfmDeemphasis/wfmBandwidthenabled:falsewith reason "Broadcast FM receiver: No selected WFM receiver";RX/TX/PWR/P/CWbuttons remain visible.get slicecarries the new"automaticSquelch":false,"automaticSquelchMarginDb":10fields with"squelch":false,"squelchLevel":20unchanged. Disconnected and closed the instance afterwards. RTL capture, multi-RX, DAX/TCI delivery and APRS cannot be reached from the demo (no USB device, and TCI/CAT are off by default) — those remain reasoned from source and the PR's offscreen tests. No hardware was touched.
Recommendation: Request changes. A passing AppImage is the merge gate, the stack base needs rebuilding on current main, and the RTL-mandatory release policy plus ENABLE_HD_FM need your ruling.
— Jeremy KK7GWY + Claude
|
Maintainer ruling: official builds require RTL, and the AppImage bundles the pinned — Jeremy KK7GWY + Claude |
e03365c to
50e2659
Compare
d4ef62d to
1e0276f
Compare
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
1 similar comment
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
|
Landing commit The three conflicts are resolved:
Auto SQL and receive audio still go through This account cannot complete the merge: the release workflows are Tier 1, and GitHub is waiting on a code-owner review from |
The only conflict is the generated touchpoint manifest, regenerated with tools/gen_touchpoint_manifest.py (ThemeManager includers 174). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…le VIII. Every accepted capture revision removed each receiver's SLC:LEVEL sample, including AF gain, pan, mute and squelch edits that leave the detector bins untouched. While the operator dragged AF gain, the S-meter and VFO meter fell back to no reading and dropped peak hold between FFT frames. A revision now retires a receiver's sample only when its passband, mode or membership changes, or when the session, front end, capture placement or DC suppression changes. rtl_backend_test pins both halves. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The only conflict is the generated touchpoint manifest, regenerated with tools/gen_touchpoint_manifest.py. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each requested change is resolved at head 324c26322:
- Retarget/rebase onto current
main: the base ismain.origin/main(d6402657e) is an ancestor of the head, and GitHub reportsmergeable: MERGEABLE. WFM/PPM are no longer duplicated, since #6082/#5973 landed first and only the analog increment was replayed. - Drop
ENABLE_HD_FMuntil RFC #6079:git grep ENABLE_HD_FMat324c26322returns no hits. - Check the AppImage after
--output appimage:.github/workflows/appimage.yml:512-514extracts the finished image, runstools/check_rtl_package.pyon it, and doescmpon the libusb payload. - Required
build/check-macos/check-windowson a main-targeted head: these run on this head now.
You approved 136cb48b8 on 2026-10-10. That approval was dismissed only by the later merge pushes. Locally at 324c26322: 24/24 selected RTL/SQL/meter/DAX/TCI/AX.25 CTests pass, and the engine-boundary, capability-record and command-plane gates match main.
MainWindow_Wiring.cpp's profile-recovery DAX guard keeps this branch's hasDaxStreams gate under main's Q_OS_MACOS spelling. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main retired the deprecated Q_OS_MAC spelling; the RTL evaluation-capacity guard, its test and the native DAX receive wiring now match it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
1 similar comment
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
The only conflict is the generated touchpoint manifest, regenerated with tools/gen_touchpoint_manifest.py. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Accessibility check findingsThis PR touches
See Findings are warnings only — the build is not blocked. |
ten9876
left a comment
There was a problem hiding this comment.
Approved 4f3aa39e1aa04207f32180f37e6cf125636bb52d. This approval is the maintainer's review: GitHub was waiting on @aethersdr/maintainers because the PR touches the Tier 1 release workflows.
Fixed in this pass
- RTL meter blanked during AF gain/pan/mute/SQL edits (automated pass).
publishCapture()removed every receiver'sSLC:LEVELsample on every accepted revision. Fixed in6b1132a10: the newRtlSdrBackend::staleRfMeterIds()retires a sample only when that receiver's passband, mode or membership changes, or when the session, front end, capture or DC suppression changes. Proved by a new block inrtl_backend_test(9 checks). With the old retire-everything rule restored, 5 fail; with the fix, all pass. A kept sample still ages out throughMeterModel::relativeLevelForSlice'svitalIsFreshgate. Q_OS_MACspelling (merge with #6355).c77bb491crenames the PR's three remaining uses toQ_OS_MACOS. The build log has noQ_OS_MACwarning.- Merges with
main:84a8436e7,324c26322,dfe363264,4f3aa39e1. Each manifest conflict was resolved by regenerating withtools/gen_touchpoint_manifest.py, never by hand.dfe363264keeps the PR'shasDaxStreamsguard undermain'sQ_OS_MACOSinMainWindow_Wiring.cpp.
Maintainer decisions
- Touchpoints accepted as part of the increment:
models/Ax25ReceiveModel.h(replacescore/tnc/AetherAx25LibmodemShim.h),models/DaxReceiveModel.h, and ThemeManager +1 (SmartMtrWidget.cpp). - The AX.25 dialog keeps cancelling a running RX capture on a decoder-source reset (announced in the log).
Refuted or kept from the automated pass
VirtualAudioBridge::resetRxChannelracing the HAL plugin: already handled by the PR'scompare_exchange_stronginhal-plugin/AetherSDRDAX.cpp.RtlSquelchGatestale threshold in Auto: with no neighbor bins the gate observes NaN and closes, so the old threshold is never evaluated.avoidDcon a bank receiver switched to WFM: the planner protects only the followed receiver, so the suggested change would have no effect. The line is unchanged frommain, and an overlapping FM receiver still gets the existing DC warning and the Move-capture action.- Auto SQL slider holding the confirmed value: this is the intended confirmed-state behavior (Principle II).
- Not changed (efficiency and cosmetic nits): per-frame
backendCapabilities()inDaxReceiveModel::receive, 100 ms relative-meter poll timers, always-on PCM statistics indrainAudio, and RTL health rows with no value before the first capture.
Verified at this head
- Locally (Arch, GCC, Qt 6.12, lean RelWithDebInfo, RTL on): 30/30 CTests pass. That covers RTL capture/backend/pipeline/WFM/SQL/APRS/spectrum/settings, meters, DAX/TCI receive and PipeWire, the AX.25 receive model, the RX/VFO/WFM widgets, and the TCI suites including the socket-owning
tci_server_review_testandtci_automation_test(run on operator direction; #6365'srx_enablecases pass). check_engine_boundary,check_capability_recordsandcheck_command_plane --strictshow the same findings asmain. The manifest is current.- Demo simulator (
DEMO-0001, isolated settings): manual SQL on 35 and off reads back correctly. - Required CI on this SHA:
Static checkspassed.build,check-windowsandcheck-macoswere still running when this was posted, at the maintainer's direction; auto-merge waits for them. On the previous head (c77bb491c, the same tree minus the manifest-only merge of #6386) all four passed.
Not verified
RTL hardware behavior (live receive, audible SQL, multi-receiver banks, meter feel) and the release packages on this SHA. The demo has no RTL receiver and no LEVEL meter. The disclosed intermittent Mac WFM withdrawal remains unexplained; the one-receiver production default stands.
…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>
Conflicted file: BUILD-OPTIONS.md (hand-written; tools/check_build_options.py checks it, nothing generates it). One hunk, two adjacent table rows: - ENABLE_RTL: main's row, which gained "unless REQUIRE_RTL=ON" with aethersdr#6083 (46943eb). This branch never changed that row. - AETHER_HL2_TX_TXA: this branch's row (SSB, AM, DSB and FM; OFF is SSB only). Main never changed that row; it only sat next to the other. Everything else main added to the file (REQUIRE_RTL, the sanitizer and RelWithDebInfo rows of aethersdr#6208, aethersdr#6214/aethersdr#6217) merged as text and is kept. check_build_options.py --strict passes. No other file conflicted. Hl2TxDsp.cpp/.h merged as text over aethersdr#6245: the level chain is main's Hl2TxAlc (gain ramped inside the block), and AM, DSB and FM audio now passes through it like SSB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Analog RTL receivers gain independent squelch, receive-audio exports, meters and release packaging. This is the remaining analog delivery increment under RFC #5468; the WFM foundation, applet and live PPM changes are already in
main.Scope and separation
Rebased onto main
d6f6a4f00e40e292d575c40cad7d0848406d89f5. Only the five remaining analog commits were replayed, followed by the release-control and user-documentation cleanup. The merged WFM/PPM changes and newer main changes are retained.ENABLE_RTL=ONandREQUIRE_RTL=ON. Configuration fails if RTL or float-FFTW is missing. Jeremy approved required RTL and bundling pinned libusb. The completed AppImage is extracted and audited after the final linuxdeploy pass, including a byte comparison of its libusb payload. Windows dependency validation follows normal and delay imports recursively.ENABLE_HD_FMdeclaration, placeholder, release flag or decoder dependency remains in this analog increment. The actual optional switch and all explicit OFF release/build flags belong to deferred draft RTL: add opt-in native digital broadcast reception and local metadata #6002 / RFC [RFC] Optional Digital FM reception and program metadata #6079. That feature is not a prerequisite for this PR.Shared behavior for review
Auto SQL uses the existing typed receive dispatcher and confirmed-state guards. RTL's 2048-bin detector scale is relative dBFS, not calibrated dBm or directly comparable with the display FFT; no misleading threshold line is drawn. An accepted capture revision retires a receiver's relative-level sample only when that receiver's detector input changes (passband, mode, membership, front end, capture placement or DC suppression); monitor gain, pan, mute and squelch edits keep it, so the meter does not blank during an AF gain drag. Other adapters retain their manual behavior and refuse the automatic request.
TCI initialization is batched as one worker job for every family.
AETHER_SETTINGS_DIRsuppresses legacy-profile migration and normal log pruning. TheactiveSliceCommandIssuedconnection usesreceiveCommandSource()identity andQt::DirectConnection; this shared-family guard remains part of the increment. Receive-only PipeWire routing drains stale audio while preserving the legacy Flex FIFO's sole-reader/EPIPE behavior. Dependency-prefix and Qt deployment fixes remain in packaging.Validation
Native Nobara / GCC 16.2.1 / Qt 6.12 / Release application and daemon builds pass at
a3458b09. 53/53 selected CTests pass, zero skips/failures, 221.35 seconds. This covers RTL capture/control/receiver banks, FM/WFM, SQL, spectrum/averaging, APRS, DAX/TCI, meters, applet lifecycle and actual PipeWire FIFO behavior. The analog CMake cache contains no Digital switch.Platform CI passes on Linux, macOS and Windows. Static Checks and Docs pass.
Full Suite passes: 691 passed, five skipped, zero failures, 696 registered tests, 932.37 seconds. The skipped cases are four AppSettings safety scenarios and
hl2_probe_spectrum_panadapter; they are not claimed as exercised.All branch release-package workflows pass at exact head
a3458b09: AppImage x86-64 and ARM, macOS DMG Apple Silicon and Intel, and Windows Installer. Both completed AppImages pass RTL runtime closure and libusb byte comparison. Both DMGs pass signing/notarization; Windows passes actual DLL closure and installer/artifact packaging. Artifact identities and checksums are retained. Release/Store publication jobs were skipped.Already passed on this revision: 16 required-RTL configuration combinations, 9 package-helper tests, engine/routing/capability gates, generated manifests and references, frozen-test-gate and registration checks, build-options checks, LogAnalyzer checks, and the user-documentation production build. Commits are signed.
Historical operator multi-RX acceptance and earlier package/full-suite results remain in the PR discussion and original #6077. They are not substituted for this head's results. The intermittent historical Mac WFM receiver withdrawal during a filter/stereo transition remains unexplained; passing reruns did not establish a cause. No new live-RF, audible, sanitizer or production-capacity qualification is claimed.
This PR requests its own maintainer/code-owner review. #6002 remains draft. No release or Store publication is part of this validation.
73, Ozy K6OZY · Codex