Skip to content

[codex] Fix Kiwi waterfall pan and zoom stalls#3825

Merged
ten9876 merged 3 commits into
aethersdr:mainfrom
rfoust:codex/kiwi-waterfall-pan-zoom-stalls
Jun 26, 2026
Merged

[codex] Fix Kiwi waterfall pan and zoom stalls#3825
ten9876 merged 3 commits into
aethersdr:mainfrom
rfoust:codex/kiwi-waterfall-pan-zoom-stalls

Conversation

@rfoust

@rfoust rfoust commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #3816. Moves KiwiSDR receiver/network work off the GUI thread, coalesces Kiwi waterfall row delivery, and changes pan/zoom handling so AetherSDR keeps the local panadapter responsive while avoiding repeated remote Kiwi waterfall window resets during gestures. This also stabilizes the Kiwi FFT trace math so partial/stale waterfall rows during pan or zoom no longer re-anchor the FFT floor and make the trace jump upward.

Constitution principle honored

Principle XI — Fixes Are Demonstrated. The change is backed by a focused KiwiSDR trace-math regression test, local builds, focused Kiwi test runs, and live receiver testing against the reported pan/zoom behavior.

Test plan

  • Local build passes (cmake --build build --target AetherSDR kiwi_sdr_trace_math_test)
  • Behavior verified on a real receiver: Kiwi pan left/right no longer freezes, and fast zoom behavior was iterated against live logs/user testing
  • Focused Kiwi tests pass (ctest --test-dir build -R 'kiwi_sdr_trace_math_test|kiwi' --output-on-failure)
  • git diff --check passes
  • A11y checker passes with existing warning-only findings (python3 tools/check_a11y.py)
  • Reproduction steps documented: drag or zoom a Kiwi-backed panadapter and watch for waterfall/FFT freezes or FFT trace vertical jumps

Checklist

  • Commits are signed (docs/COMMIT-SIGNING.md)
  • No new flat-key AppSettings calls — use nested-JSON-under-one-key (Principle V)
  • Code is clean-room — not decompiled, disassembled, or reverse-engineered from a proprietary binary (Principle IV)
  • All meter UI uses MeterSmoother (AGENTS.md convention)
  • Documentation updated if user-visible behavior changed
  • Security-sensitive changes reference a GHSA if applicable

@rfoust rfoust force-pushed the codex/kiwi-waterfall-pan-zoom-stalls branch from 2ae22c3 to a5a2dde Compare June 26, 2026 02:29
@rfoust rfoust force-pushed the codex/kiwi-waterfall-pan-zoom-stalls branch from a5a2dde to 10364a5 Compare June 26, 2026 03:29
@rfoust rfoust marked this pull request as ready for review June 26, 2026 03:48
@rfoust rfoust requested a review from a team as a code owner June 26, 2026 03:48
Copilot AI review requested due to automatic review settings June 26, 2026 03:48
@rfoust rfoust requested review from a team as code owners June 26, 2026 03:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses KiwiSDR-induced UI stalls (issue #3816) by moving KiwiSDR client work off the GUI thread, reducing UI-thread workload from Kiwi waterfall delivery, and stabilizing Kiwi FFT trace behavior during pan/zoom gestures to avoid floor “re-anchoring” and trace jumps.

Changes:

  • Run KiwiSdrClient instances on a dedicated QThread, routing control calls via invokeMethod() and delivering signals back to the manager/UI via queued connections.
  • Coalesce Kiwi waterfall row emissions (min interval) and add gesture-settle signals so remote waterfall view updates aren’t reset on every drag/zoom step.
  • Introduce KiwiSdrTraceMath + a focused regression test to keep FFT trace-floor behavior stable across partial/stale row coverage during gestures.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/kiwi_sdr_trace_math_test.cpp Adds a regression test covering Kiwi trace-floor estimation/mapping/stabilization.
src/gui/KiwiSdrTraceMath.h New shared trace-math helpers for mapping Kiwi rows to the FFT trace and stabilizing the trace floor.
src/gui/SpectrumWidget.h Adds gesture/settle signals and new Kiwi trace-floor state to support stable pan/zoom + trace rendering.
src/gui/SpectrumWidget.cpp Implements deferred/settled pan+range behavior, Kiwi trace stabilization, and safer stream-state save/restore to avoid COW copies.
src/gui/MainWindow.h Adds Kiwi UI sync coalescing flags/state.
src/gui/MainWindow.cpp Avoids routing pan-range requests to Flex when the pan is Kiwi-backed (local-only path).
src/gui/MainWindow_Wiring.cpp Defers Kiwi waterfall view resets during gestures; updates view on settle signals.
src/gui/MainWindow_KiwiSdr.cpp Coalesces Kiwi UI sync work and adjusts bandwidth limits for Kiwi full-bandwidth waterfall behavior.
src/core/KiwiSdrManager.h Adds client-thread + invoke/destroy helpers and caches client state/flags for cross-thread operation.
src/core/KiwiSdrManager.cpp Moves Kiwi client work to a dedicated thread, updates signal wiring for queued delivery, and adds safe teardown.
src/core/KiwiSdrClient.h Extends telemetry signal to carry the value; adds pending-row coalescing state.
src/core/KiwiSdrClient.cpp Implements waterfall-row coalescing and relaxes frame acceptance to avoid stalls on request churn.
CMakeLists.txt Adds the new kiwi_sdr_trace_math_test executable and ctest entry.

Comment thread src/gui/MainWindow.cpp Outdated
Comment on lines +5844 to +5848
qDebug() << "Pan range request:" << source
<< "center" << centerMhz
<< "bandwidth" << bandwidthMhz
<< "local=kiwi";
return;

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

Thanks for this, @rfoust — this is a careful, well-structured fix. I checked out the branch and reviewed the threading move, the row coalescing, the pan/zoom gesture handling, and the new trace-math. All 6 CI checks (build, check-macos, check-windows, CodeQL, a11y) are green, and the overall design holds up well.

What's good

  • Threading move is done correctly. Clients are moved to a dedicated QThread; all cross-thread calls go through invokeClient() (queued), and all client→manager signals are Qt::QueuedConnection with a client(id) != c staleness guard. The getters (telemetry/state/waterfallAvailable) were correctly switched from direct cross-thread reads to cached m_* maps, which is exactly what makes the move safe. The destructor teardown is also sound: destroyClient(id, /*blocking=*/true) uses BlockingQueuedConnection (guarded by c->thread() != currentThread()) to tear each client down on its own thread before quit()/wait(), and the redundant QThread::finished → deleteLater is harmless.
  • saveCurrentWaterfallStreamState move-semantics. Switching the large waterfall QImage/vectors to std::move to avoid COW-detach-and-copy on the GUI thread is a nice, real win.
  • Trace-math extraction is clean and properly tested. KiwiSdrTraceMath.h is pure/header-only, and kiwi_sdr_trace_math_test.cpp directly covers the reported symptom — floor stays anchored when a partial/stale row arrives mid-gesture (stabilizeTraceFloor with allowFloorAdapt=false), plus DC-edge mapping and gradual upward adaptation.
  • UI-sync coalescing (scheduleKiwiSdrUiSync flag batching) and gesture-gated waterfall view updates (settle signals instead of per-step resets) are the right shape for the stall fix.

One confirmed issue (Copilot's finding is valid)

src/gui/MainWindow.cpp:5844 — the unguarded qDebug() in applyPanRangeRequest():

if (!kiwiSdrProfileForPan(panId).isEmpty()) {
    qDebug() << "Pan range request:" << source
             << "center" << centerMhz ...;
    return;
}

This sits on the early-return path taken for every Kiwi pan/zoom range request, and frequencyRangeChangeRequested fires repeatedly during zoom-drag gestures — so this logs at gesture frequency and does string formatting on the GUI thread, in precisely the hot path this PR is trying to keep responsive. Suggest removing it, or gating behind a qCDebug(<category>) so it's off unless the category is enabled.

Minor (non-blocking)

  • On pan-drag release, m_draggingPan is set false before applyPanDragCenter(..., force=true) and the subsequent emit panDragSettled(...), so the centerChangeRequested handler and panDragSettled can both drive updateKiwiWaterfallView once. It's idempotent so no harm — just noting it's a double-commit on release.

Nothing here blocks; the one actionable item is the hot-path qDebug. Nice work on the threading discipline and the regression test.


🤖 aethersdr-agent · cost: $7.8410 · model: claude-opus-4-8

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

Reviewed the substantive pieces in depth — strong, expert-level work.

Threading (highest-risk): single worker QThread; clients created parentless + moveToThread'd with deleteLater-on-finished; all client→manager signals QueuedConnection WITH stale-guards (if (client(id) != c) return) against in-flight queued signals after a client is replaced; GUI→worker via invokeClient (queued); destroyClient's BlockingQueuedConnection correctly guarded (c->thread() != currentThread()) and safe since the worker never waits back on the GUI; shutdown does blocking-destroy each client then quit()+wait(3000) with a setParent(nullptr)+self-delete-on-finish fallback to avoid destroy-while-running; qRegisterMetaType<QVector> for the cross-thread args. The subtle bits most refactors miss are all handled.

Trace math (KiwiSdrTraceMath.h): isolated pure functions with std::isfinite guards throughout (stops a stale/partial bin re-anchoring the floor → the trace jump), area-weighted averaging for zoom-out, graceful fallback, 100-line regression test.

Pan/zoom: settle-timer debounce so a gesture doesn't fire repeated remote Kiwi window resets, and the local drag path owns the visual during an active drag (stale remote rows ignored).

Platform-agnostic, no TX surface, live-verified. The one field watch-point is heavy multi-Kiwi-profile churn, which the stale-guards + blocking-destroy are built for. Thanks @rfoust — this closes the KiwiSDR GUI-thread-lag problem.

@ten9876 ten9876 merged commit 69789d4 into aethersdr:main Jun 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants