Skip to content

RTL: live PPM adjustment on the analog-only stack - #6082

Merged
jensenpat merged 1 commit into
mainfrom
codex/rtl-live-ppm
Oct 7, 2026
Merged

jensenpat merged 1 commit into
mainfrom
codex/rtl-live-ppm

Conversation

@Ozy311

@Ozy311 Ozy311 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Rebased on current main — 2026-10-06

Current head: c667b41cb8e6f825445954fcaaaf7eaca31229f5. Base stack starts at main 5f09371e25ffc032666d36f0ea265dc506621066; parent PR/branch is unchanged. This is a signed replay of the existing increment. It preserves main's unified window chrome and release debug-symbol archives, alongside the RTL dependency packaging and explicit Digital-OFF release flags. Generated touchpoint counts were regenerated from the combined sources.

Fresh rebase validation on analog ca3a36bd: native Apple Silicon / Qt 6.12 / RTL required / Digital OFF application build succeeds. 51/52 selected CTests pass in 226.55 seconds: all 47 RTL regressions and four additional UI checks pass. The new upstream unified_title_bar_test fails its Alt+F/menu-rounding assertions under Mac offscreen rendering; an independent rebuild and run on clean main 5f09371e reproduces the identical two failures. This is an inherited test failure, not a green full-suite claim. Static engine/routing/capability, generated manifests, registration/frozen-gate, theme and build-options checks pass; package-helper tests pass 9/9 and required-RTL configuration checks pass 16 combinations. The Digital increment and vendor trees are unchanged by replay; decoder-ON tests and release packaging were not rerun for these heads. Hosted checks are being refreshed by the push.

All validation recorded below predates this rebase and remains evidence for the specific revisions named there. No new live-RF, capacity, or release-package qualification is claimed. Readiness is unchanged; Digital #6002 remains draft.


Previous checkpoint record — 2026-10-05

Replacement for #6003, which GitHub automatically marked merged during ancestry rearrangement without landing it in main. Its original reviews and hardware history remain preserved.

Scope

Five-file live PPM increment under analog RTL RFC #5468, based on repaired WFM #5974. It changes RtlReceiverSettingsWidget and the three focused RTL widget/device/capture tests. No Digital implementation or approval dependency is introduced.

Head: 1e0276fca118df05215c3e7c752be524bb74170e. Parent: #5974.

Completed arrow/keyboard edits apply after a 200 ms debounce; Apply submits immediately. Enter commits the field without activating the parent dialog's Close button. Requested values remain separate from hardware readback and accepted per-device persistence. Newer edits survive older completion or refusal.

Hide, page switch, minimize or session change cancels unsent work. This includes Enter followed by hide before the debounce expires; closing does not undo an already submitted adjustment. Hiding an already-refused edit restores confirmed state. A refusal arriving after the page was hidden with a request pending instead retains that submitted/refused value for explicit retry, with applied readback separate. There is no automatic retry. Returning to the applied value while other backend work is pending can deliberately submit a recapture.

Review regression details are retained: the stale-frame test uses the named FFT size and a paired valid-current-token frame; rapid-edit bursts do not pump the event loop between edits; preservation checks use FM-N, asymmetric filtering, gain 37, pan 68, mute and enabled SQL at 43. Comments distinguish cancellation of unsent edits from completion of submitted work.

Validation and decision

Exact checkpoint, native Apple Silicon / Qt 6.12 / optional decoder OFF: all three rebuilt focused tests pass (3/3, 20.92 seconds): rtl_capture_worker_test, rtl_device_controls_test, rtl_receiver_settings_widget_test. Current worker pacing and the previous named-frame/positive-control, debounce and nondefault SQL regressions are preserved. Static ratchets and generated manifests pass.

Earlier 72065d84 Mac and 7911324a Nobara results and operator-reported live adjustment/stability are historical; they are not fresh calibration or reacquisition measurements.

Apply-on-edit remains this PR's specific maintainer UX decision. The current review identified no separate blocking code defect in this five-file increment. Existing notes about pending no-change requests and window-system hides remain disclosed. This does not reopen #5924's approved seam or gate the DSP foundation on Digital approval.

Principles II, V/XIV and XI: confirmed state, scoped persistence and demonstrated behavior.


73, Ozy K6OZY · Codex

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1. Issue fit

Yes, for the increment it claims. There is no Fixes #NNNN; the PR is governed by analog RTL RFC #5468 and is the re-opened replacement for #6003, so I reviewed it against its own stated scope: live per-device PPM adjustment with a 200 ms debounce, immediate Apply, Enter committing the field without closing the parent dialog, requested values kept separate from hardware readback, and newer edits surviving older completion/refusal. Every one of those maps to a hunk, and each has a corresponding assertion in rtl_receiver_settings_widget_test.cpp. No requirement in the body is unaddressed.

Two framing notes, because they change what this review can and cannot claim:

  • The base is not main. 5e39790a^ is not an ancestor of origin/main (49 commits off), and src/gui/RtlReceiverSettingsWidget.cpp and all three tests do not exist on main at all. GitHub's diff-vs-main therefore carries the whole #5974 stack. I reviewed only the five-file increment at 5e39790a; the stack base is #5974's review, not re-reviewed here.
  • No build or test job ran on this head. get_check_runs(5e39790a) returns exactly two checks — "Static checks" and "Sanitizer option configures", both green. There is no compile job and no ctest on this SHA, and the body is straight about why ("not a separate exact-head rebuild of this checkpoint"; results recorded against #6083). So "CI is green" here means the static gates passed, not that the three modified tests ran. I could not execute them either. Everything below is reasoned from the code in the head checkout, not reproduced at runtime.

Socket tests: none. Grepping the three modified test sources for QTcpServer|QTcpSocket|QUdpSocket|QLocalServer|QWebSocketServer|bind(|listen(|connectToHost|Fake* returns nothing. All three are socket-free and registered in tests/tests.cmake (lines 1426, 1432, 5433), with the widget test pinned to QT_QPA_PLATFORM=offscreen. The widget/model/injected-worker layer is the right one per AGENTS.md's test-layer table for GUI state-machine and refusal behavior.

2. Scope

File / group What it changes Claimed in body? Verdict
src/gui/RtlReceiverSettingsWidget.cpp/.h debounce timer, submitPpm(), hideEvent(), RtlPpmSpinBox Return override, setAutoDefault(false), explanation text Yes In scope
tests/rtl_receiver_settings_widget_test.cpp real-keyboard + dialog-host fixture, burst/arrow/typed/refusal/hide/rebind/synchronous cases Yes In scope
tests/rtl_capture_worker_test.cpp PPM preserves capture geometry; stale-token spectrum rejected Yes ("injected worker/model checks") In scope
tests/rtl_device_controls_test.cpp PPM preserves capture identity / receiver / RF view plus a rework of the shared fixture (Fm→Fmn, filters ±8000→-6500/+7500, and newly audioMute=true, squelchEnabled=true, squelchLevel=43) Partly — the view/centre reasoning is in the test comment, the fixture's audio/squelch change is nowhere Nit — see N4
Stack base (AudioEngine, WDSP, RadioModel, AutomationServer, …) Not touched by this increment n/a Reviewed under #5974

No new public surface: no protocol verb, settings key, capability field, CLI flag or CHANGELOG.md entry. No - line removes a guard, early return, confirmation, or a comment naming a fixed symptom — the only deletions are the two lines the debounce replaces (m_ppmEdited = true and the inline Apply lambda), both preserved in submitPpm()/the new lambda. No build config, CI, or vendored-tree churn.

Security. The required CodeGuard audit ran over the flagged file list. Nothing exploitable in this PR's five files, and every flagged category is a false positive on them: no shell, QProcess, URL, QNetworkAccessManager, SQL, HTML surface, credential literal, or non-ASCII byte exists in the diff (0x52544c0000000000ULL at RtlReceiverSettingsWidget.cpp:36 is a request-ID namespace seed, ASCII "RTL", not a key). The api:buffer_ops memcpy signal is against a stale base — there is no memcpy hunk in this PR, and the four copies it points at in RtlRfExtractor.cpp / RadioModel.cpp are each bounded (verified by security-audit). One genuine, pre-existing and out-of-increment item surfaced, credited to security-audit (CodeGuard): src/core/AutomationServer.cpp:2401-2419 creates /tmp/aethersdr-automation{,.json} at predictable, non-random paths with plain QFile::open(WriteOnly|Truncate), which follows symlinks — on a multi-user Linux host a local user can pre-create that path as a symlink and have the next start truncate a victim-owned file. Not caused by, and not fixable in, this PR; worth its own issue.

3. Blockers

None.

4. Nits (all non-blocking)

  • N1 — a no-change PPM request still fires while the backend is pending (RtlReceiverSettingsWidget.cpp:226, inline). When confirmed["pending"] is true, the settled-value early return is skipped, so returning the entry to the already-applied value submits a redundant ppm.set and costs the operator a real capture restart and reacquisition. The comment explains the reasoning (only the backend owns the latest-pending transaction) and the body lists this as a disclosed carry-over from #6003 — recording it as disclosed, not re-raising it as new.
  • N2 — stale-token spectrum check is negative-only and uses a magic literal (rtl_capture_worker_test.cpp:233, inline). Discriminates today, but has no paired positive control, unlike its sibling at lines 566-571.
  • N3 — the burst assertion is the one real flake surface (rtl_receiver_settings_widget_test.cpp:166, inline). Two 40 ms gaps inside a 200 ms single-shot window; a >160 ms scheduler stall makes it fail. This is the #6003 "debounce margins" note, still open. The pumpFor(260) negative checks elsewhere are not flaky — the timer is stopped on those paths, so they can't race.
  • N4 — the rtl_device_controls_test fixture rework deserves a sentence in the body (rtl_device_controls_test.cpp:23, inline). Also, the "nondefault receiver" assertion at line 133 checks audioGain/audioPan/audioMute but not squelchEnabled/squelchLevel, so it would not notice if those two silently reverted to defaults.
  • N5 — a refused PPM value survives hideEvent as an "edit" (RtlReceiverSettingsWidget.cpp:212, inline). The comment says "discard only unsent input"; a refused value is neither accepted nor discarded, and m_ppmEdited stays true so acceptStatus() will not reseed the entry for the rest of the binding.
  • N6 — the new status line is never announced. renderStatus() sets m_status->setAccessibleDescription(...) but fires no QAccessible::updateAccessibility, so "Frequency correction will apply after this adjustment." reaches a screen-reader user only if focus happens to land on the label. This is the widget's pre-existing pattern and docs/a11y.md:119's throttle rule argues against spamming an announcement per arrow press, so I am not asking for a change — noting it as a known coverage edge.

5. What I tried to break, and it held

  • The Return override is load-bearing, and the test fixture mirrors the real host. I assumed the RtlPpmSpinBox subclass was defensive dead code and went looking for the actual host: RadioSetupDialog.cpp:856 creates the page, and that dialog is a PersistentDialog with QDialogButtonBox(QDialogButtonBox::Close) at line 1039 wired rejected → QDialog::close at 1043 — exactly the fixture the test builds. QAbstractSpinBox::keyPressEvent deliberately calls event->ignore() on Return so the dialog's default button fires, so without this override Enter would close Radio Setup mid-edit. The base call precedes the accept(), so the field still commits. apply->setAutoDefault(false) closes the complementary hole (dialog push-buttons default to autoDefault=true).
  • Self-triggered debounce loops. I checked every programmatic write to m_ppm — bindBackend(), acceptStatus(), hideEvent() — and all three are under QSignalBlocker, so no model-driven or revert-driven setValue can start a timer or spoof an operator edit. The valueChanged guard adds !isVisible() || !m_haveState on top.
  • Synchronous reentrancy. submit() assigns pending = nextRequest() before invokeBackendExtension, so a backend that completes on the same stack (the test's synchronousPpm) is seen with id == m_ppmRequest rather than being dropped as stale. submitPpm()'s guards are a strict superset of submit()'s, so m_submittedPpm can never go stale relative to a non-zero m_ppmRequest.
  • The squelch-enabled fixture. audioMute=true, squelchEnabled=true, squelchLevel=43 looked like it should be refused: RtlReceivePipeline::prepareDetailed rejects squelchEnabled for Wfm (line 63) and for any legacy receiver (line 81). Fmn is neither — it is explicitly excluded from the legacy predicate — and squelchLevel=43 is inside [0,100], so the fixture is admitted and the pre-existing DC/display assertions still exercise a live receiver. I also confirmed the aggregate-initialiser field order against RtlCaptureTransaction.h:26-36; the positional values land on the fields the assertion names.
  • Lifecycle and stale-completion paths. Disconnect, capability loss, capability restore, and backend rebind all route through bindBackend(), which stops the timer, zeroes all three request IDs and clears m_haveState — so a timer cannot outlive its session, and submitPpm() re-validates connection, backend identity and supportsSettings() independently before sending. Old-generation extensionResult/extensionError are gated on m_bindingGeneration and source == m_model.backend().
  • Hostile device text. All three labels are forced to Qt::PlainText (lines 75-78), so a device-reported serial, a saveReason, or a refusal reason cannot inject Qt rich text into the GUI.
  • requestedPpm. The new fixture key is not invented — RtlSdrBackend.cpp:1763 really emits it. The widget not reading it is fine.

Could not check: compile and test execution (no build here, and none in CI on this SHA); the audible reacquisition cost of one auto-applied correction; and whether m_viewport is guaranteed engaged at the first Access::view(backend) call — *backend.m_viewport on an empty std::optional would be UB rather than a clean failure, though the preceding setPanBandwidth/setPanCenter calls make it very likely engaged.

6. Recommendation

Needs maintainer decision, on one axis only. The code is in good shape — I went after the debounce state machine from the directions that usually break this pattern (reentrancy, stale completion, programmatic writes, teardown, the second session) and it survived all of them, with tests that are mostly genuine rather than self-affirming. The open question is the one the author raised themselves: "Apply-on-edit is this PR's specific UX decision." Replacing explicit-Apply with auto-apply-after-200 ms on a control that triggers a real hardware recapture is a behavior change to an existing (if unmerged) control, and per GOVERNANCE.md that is a maintainer's call, not a reviewer's — I am not treating it as a smuggled preference, since it is the PR's stated purpose under RFC #5468, but it does want an explicit ruling.

Concrete next step: a maintainer rules on apply-on-edit; the author optionally picks up N2-N4 (one sentence in the body for the fixture rework, the named kSpectrumBinCount constant plus a positive control for the stale-token check, and a wider margin on the burst assertion). Given that no compile or ctest ran on 5e39790a, I would want the focused suite green at this exact head — not only at the #6083 aggregate — before merge.


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

Comment thread src/gui/RtlReceiverSettingsWidget.cpp
Comment thread src/gui/RtlReceiverSettingsWidget.cpp
Comment thread tests/rtl_capture_worker_test.cpp Outdated
Comment thread tests/rtl_receiver_settings_widget_test.cpp Outdated
Comment thread tests/rtl_device_controls_test.cpp

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

The five-file increment at d4ef62d54a91fffcbfc262fa182a2ee08cfc46a2 matches #5468's live PPM scope: it changes the settings-page submission policy while retaining the existing accepted hardware transaction and per-device persistence. I found no implementation blocker. The remaining decision is the explicitly proposed apply-on-edit UX, not a new request to approve the already-ratified backend seam or Digital FM.

Scope

Files/group Claimed? Verdict
RtlReceiverSettingsWidget.{cpp,h} Yes: 200 ms debounce, Enter, hide/rebind handling In scope; deliberate UX change
rtl_receiver_settings_widget_test.cpp Yes: keyboard, coalescing, lifecycle and stale completion In scope; socket-free model injection
rtl_device_controls_test.cpp Yes: preserve receiver/RF/view and accepted persistence In scope; injected USB boundary, no device opened
rtl_capture_worker_test.cpp Yes: generation and stale-spectrum regression strengthening In scope

No additional USB path, thread, calibration default or Digital dependency is introduced. Older stack commits are prerequisites, not unrelated additions to this five-file increment.

Blockers

None found.

Non-blocking notes

  1. src/gui/RtlReceiverSettingsWidget.cpp:205: hiding cancels unsent input, including a just-committed edit whose debounce has not fired; an already-submitted transaction may still finish and persist. This is consistent with the body and was exercised by the test. Jeremy's UX decision should cover this exact boundary, including window-system hides; it should not be described as “close always applies the visible value.”
  2. tests/tests.cmake:5795: exact-head hosted results currently show Static Checks, not all three application/platform builds. Earliest run creation 2026-10-03 04:56:21 UTC predates current main 2026-10-04 18:26:40 UTC; branch protection's strict: false does not force an updated merge test. The historical Mac/Nobara qualifications remain historical.

Verification and attempted failures

  • Built the head's focused native targets in an isolated worktree (GCC 16.2.1 / Qt 6.12). rtl_receiver_settings_widget_test passes offscreen in 6.34 s, and rtl_device_controls_test passes in 1.57 s. Test profiles and FFTW wisdom are isolated; no actual USB device or network peer is opened.
  • The widget test drives real arrow/Return events and covers burst coalescing, incomplete typed text, immediate Apply, reversal during pending work, older completion/refusal, explicit retry, hide/reopen, destruction, disconnect, backend replacement, capability loss and synchronous completion. The hardware-injection test preserves asymmetric FM-N filters, gain/pan/mute/SQL and an independent viewport across adoption/rollback; rejected and pending values do not become saved calibration.
  • Disconnected the timer-to-submission binding while retaining manual Apply. The widget regression fails its automatic burst/arrow/Enter/newer-edit assertions (CTest exit 8). Restored exact source, rebuilt, and it passes again in 6.20 s. rtl_capture_worker_test also passes in 10.26 s, including old-token rejection, the paired valid-current-token control and subsequent production-worker frame. All three changed test targets were therefore built and passed.
  • Source audit follows generation/request-id guards, signal blockers and accepted/readback separation. Existing RTL typed extension submission is retained. Four boundary checks pass against parent and head with identical findings/counts and current manifests.

Did not drive an application bridge session: SimBackend has no RTL calibration extension or USB capture-reacquisition path, so a demo session cannot establish this PR's live PPM claims. The actual page is exercised by the offscreen widget test. No fresh antenna/calibration-sign/reacquisition-timing or native Windows/macOS qualification is claimed. The separate automated code-review skill was unavailable.

Recommendation: Needs maintainer decision on the disclosed apply-on-edit interaction. The tested implementation supports that interaction; if accepted, land in stack order after current integration checks, without reopening the earlier seam decisions.

— Jeremy KK7GWY + Codex

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

Issue fit: yes, for the five-file increment. Live per-device PPM edits now apply after a 200 ms debounce or immediately on Apply. The requested value stays separate from hardware readback and from accepted persistence, as #5468 requires. I found no code blocker in this increment. The open item is the apply-on-edit UX, which is the maintainer decision, plus the stack rebase this PR inherits. I reviewed e3464c91..d4ef62d5 (this PR's own base..head) at head d4ef62d54a91fffcbfc262fa182a2ee08cfc46a2, as an independent second pass after the earlier ten9876 + Codex review on the same head.

Stack: main ← #5973 ← #5974 ← #6082 ← #6083. The base is #5974's head branch. The codex/rtl-live-ppm branch was previously the head of #6003, which merged into codex/rtl-native-hd-fm, not main. This PR replaces it, as the body says.

Scope

Files Claimed? Verdict
src/gui/RtlReceiverSettingsWidget.{h,cpp} Yes: debounce, Enter handling, hide/rebind In scope. This is a deliberate UX change.
tests/rtl_receiver_settings_widget_test.cpp, rtl_device_controls_test.cpp, rtl_capture_worker_test.cpp Yes In scope. All three are socket-free (model or USB-boundary injection; nothing opens, binds or connects).

Everything in the diff is explained by the body. No CHANGELOG entry.

Blockers

None of its own.

Inherited: this branch must be rebuilt on the rebased #5973 and #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. See #5973's review. The five files here don't touch that code, so the rebase should be mechanical for this increment.

For the maintainer (@ten9876)

  • Apply-on-edit for a hardware calibration value. The earlier review routed this to you. One boundary is worth stating precisely. hideEvent (RtlReceiverSettingsWidget.cpp:205-214) cancels unsent input, and in Qt that includes a page-switch inside Radio Setup and a window-system minimise, not just Close. A typed value committed with Enter and then hidden within 200 ms is therefore discarded. The help text says "Closing this window does not undo an applied correction". That is true, but it doesn't tell the operator that an unapplied one is dropped.

Non-blocking

  • The new help text describes "focus change" as a commit trigger. With keyboardTracking(false), a typed value commits on focus-out, and valueChanged then starts the timer. If focus leaves because the page is being hidden, the hide handler stops the timer first. That matches the cancel semantics above; the help text could say "unless the window is closed first".

What I verified, and what I tried to break

  • Re-checked the prior review. The scope is five files, and no new USB path, thread or calibration default is introduced. Both hold against the diff.
  • Refusal path. A refused request stops the debounce only when the spin box still shows the refused value (m_ppm->value() == m_submittedPpm), so a newer edit keeps its deadline and an old refusal can't swallow it.
  • Re-submitting a pending value. submitPpm returns without a new request when the value equals the in-flight one, and without any request when it equals the confirmed value with nothing pending.
  • Stale bindings. bindBackend stops the timer before rebinding. submitPpm re-checks isVisible(), m_haveState, connection, backend identity and capability at fire time, so a timer that outlives a disconnect or a backend swap does nothing.
  • Enter key. RtlPpmSpinBox::keyPressEvent lets QSpinBox interpret the text first, then accepts Return/Enter so the dialog's default button doesn't fire. apply->setAutoDefault(false) closes the other route.
  • a11y. The spin box is a QFormLayout row with a label, so its accessible name comes from the label buddy. No new unlabeled interactive widget.
  • Not driven in the app. The demo backend has no RTL calibration extension and no USB re-acquisition path, so a demo session can't reach this page's behaviour. I relied on the PR's offscreen widget test (which the earlier review mutation-checked) and on source reading. Nothing was run against hardware.

Recommendation: Needs maintainer decision on apply-on-edit. If accepted, land it in stack order after #5973 and #5974 are rebased onto current main.

— Jeremy KK7GWY + Claude

@Ozy311
Ozy311 force-pushed the codex/rtl-wfm-filters-stereo branch from e3464c9 to b8aba26 Compare October 6, 2026 01:53
@Ozy311
Ozy311 force-pushed the codex/rtl-live-ppm branch 2 times, most recently from 1e0276f to c667b41 Compare October 6, 2026 05:38
@Ozy311
Ozy311 force-pushed the codex/rtl-wfm-filters-stereo branch from b8aba26 to ebb35cf Compare October 6, 2026 05:38
Base automatically changed from codex/rtl-wfm-filters-stereo to codex/rtl-wfm-dsp-foundation October 7, 2026 14:35
@jensenpat
jensenpat requested a review from a team as a code owner October 7, 2026 14:35
Base automatically changed from codex/rtl-wfm-dsp-foundation to main October 7, 2026 15:21
@jensenpat jensenpat self-assigned this Oct 7, 2026

@jensenpat jensenpat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict

Do not merge this head. No code defect in the live-PPM increment. The branch cannot land: it is CONFLICTING / DIRTY against current main (121340c08fb5), and required build, check-macos, and check-windows have no runs on this SHA.

Reviewed c667b41cb8e6f825445954fcaaaf7eaca31229f5. Issue fit: the five-file increment matches the live per-device PPM behavior described for analog RTL RFC #5468 (200 ms debounce or immediate Apply, requested value separate from readback and from accepted persistence, hide/minimize/page-switch cancels unsent edits). No linked closing issue. No CHANGELOG entry.

Parent commits 1398f3b9 and ebb35cfd replay the WFM stack that already squash-merged as #5973 (ca4e7cb0). They are not ancestors of main. The GitHub three-dot diff is still 90 files because the merge-base is 5f09371e, thirteen commits behind main.

Scope

Group What it is Verdict
RtlReceiverSettingsWidget.{h,cpp} Debounce, Enter-does-not-close, hide cancels unsent PPM In scope. Matches the help text and the widget test.
rtl_receiver_settings_widget_test.cpp, rtl_device_controls_test.cpp, rtl_capture_worker_test.cpp Widget lifecycle, preservation across PPM adopt/rollback, stale-spectrum token In scope. No socket, bind, listen, or connectToHost. Device test injects a USB boundary and does not open a device.
Other 85 files in the three-dot diff WFM DSP, applet, typed receive, WDSP patch, manifests Already on main via #5973. Not a second copy to land from this head.
Shared SliceDelta.h WFM fields from this branch, noise-blanker fields from main Only content conflict. Keep both: NoiseBlankerKind.h from main, WFM fields already on main.

A scratch merge of this head onto origin/main (121340c08fb5) changes seven paths: the five PPM files, conflicted SliceDelta.h, and docs/architecture/aetherd-touchpoints.md. That manifest hunk moves core/AppSettings.h from 107 to 108. AppSettings.h itself does not change versus main, and the #5973 squash left the count at 107. Merging this head would publish a stale generated count. Rebase onto current main and regenerate; do not hand-edit the number on this base. Static checks passed for this tree on 2026-10-06, which does not validate the count after merging later main.

RTL is the only family whose behavior changes in the increment. Flex, HL2, Icom, ANAN, and Sim sources are unchanged versus current main after the scratch merge. The conflict is an include on the shared slice delta, not a new cross-family PPM path.

Findings

No demonstrated defect in the PPM widget or its tests.

submitPpm keeps a distinct in-flight value, ignores a duplicate of that value, and skips a no-op only when nothing is pending and the spin box already shows confirmed PPM. A refusal stops the timer only when the spin box still shows the refused value, so a newer edit keeps its deadline. hideEvent stops the timer and restores confirmed PPM unless a request is in flight, in which case it shows the submitted value and still allows that transaction to finish. Enter is accepted on the spin box and Apply is not an auto-default, so Return does not hit the dialog Close button. bindBackend stops the timer before rebinding. Those are the paths the new widget test drives, including burst-without-yield, arrow keys, uncommitted typed digits, reversal during an in-flight request, hide/show, disconnect, capability loss, backend replacement, synchronous completion, and destruction.

Apply-on-edit is the behavior this PR specifies, including discarding an Enter that has not yet debounced when the page hides. That matches the help text. I am not reopening it as a new UX defect.

Gates

  • mergeable: CONFLICTING, mergeStateStatus: DIRTY, reviewDecision: REVIEW_REQUIRED.
  • Head checks: Static checks and Sanitizer option configures succeeded (a duplicate pair was cancelled). No build, check-macos, or check-windows run exists for c667b41c.
  • Five unresolved review threads remain, all non-blocking nits from aethersdr-agent on the October 2 review. Two are outdated. Conversations still have to be resolved before a later merge.
  • Commits are GitHub-verified SSH signatures (verified: true).

Verification limits

Read the five-file increment (ebb35cfd..c667b41c, +409/−20) and reproduced the merge against origin/main. Did not build or run tests: the conflict and missing required checks already block merge, and a green local run would not make this head mergeable. No app launch. No fresh hardware PPM reacquisition.

Non-blocking follow-up, not a merge prerequisite: the body says a refusal that arrives after hide, while a request was still pending, keeps the submitted value for an explicit retry. The widget test covers hide-after-refusal and confirm-while-hidden. It does not drive refuse-after-hide. The error path leaves m_ppmEdited set, which is what produces that result.

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

Rebased onto current main 43e2633e9. The resulting diff is the five-file live PPM increment already reviewed at c667b41c (RtlReceiverSettingsWidget plus the three RTL tests, +409/−20). Cherry-pick applied cleanly; author remains Ozy311. No touchpoint or SliceDelta change remains.

Approving this SHA. Merge still waits on required checks and resolved review threads.

@jensenpat
jensenpat enabled auto-merge (squash) October 7, 2026 17:40
@jensenpat
jensenpat merged commit 2a89515 into main Oct 7, 2026
7 checks passed
@jensenpat
jensenpat deleted the codex/rtl-live-ppm branch October 7, 2026 18:21
@aethersdr aethersdr deleted a comment from aethersdr-agent Bot Oct 8, 2026
ten9876 added a commit that referenced this pull request Oct 12, 2026
… Principle VIII.

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

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

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

## Summary

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

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

## What changed

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

## Validation

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

## Screenshots now out of date (not recaptured)

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

## Found during the audit, not fixed here

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

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants