fix(waterfall): TX scroll rate now matches RX#3031
Merged
Conversation
Symptom: after #3019, the TX waterfall scrolled visibly slower than RX on the same pan (~3x slower in practice). Root cause: pushWaterfallRow (the FFT-derived row path used during TX) gated emission to one row per m_wfLineDuration (100 ms by default) on the premise that RX native tiles also scroll at line_duration. They don't — native tiles arrive at the radio's FFT rate (~30 Hz on this fleet) and updateWaterfallRow pushes one row per tile with no rate gate. So RX was ~30 rows/sec and TX was ~10 rows/sec. Pre-#3019 this was masked: with show-tx-in-waterfall on, native tiles *also* ran during TX, so the cadence was filled regardless of whether the FFT gate let a row through. #3019 correctly stopped the double- advance by suppressing native tiles for TX-affected pans — and that exposed the underlying pacing-assumption bug. Fix: emit one row per FFT frame from pushWaterfallRow, matching the native-tile behaviour exactly. The intermediate-frame linear-power accumulation becomes unnecessary (no intermediates to combine when each frame is its own row); intra-frame bin interpolation is now in dBm space, consistent with updateWaterfallRow. The state members (m_fftRowDebtMs / m_lastFftFrameMs / m_fftAccumPower / m_fftAccumCount) and resetFftWaterfallAccumulator() are left in place to keep this diff focused on the behavioural fix; they're now unused and can be removed in a follow-up cleanup. Verified locally with diagnostic probe: TX pushWaterfallRow calls and RX native-tile arrivals both occur at ~30 Hz; before the fix only ~10 of the 30 TX FFT calls per second emitted a row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Thanks @ten9876 — the diagnosis is convincing and the fix lines up cleanly with the native-tile path.
I verified the supporting structure:
updateWaterfallRow(src/gui/SpectrumWidget.cpp:2837) pushes one row per tile with no rate gate, so removing the FFT-side gate is the right way to achieve parity rather than introducing a matching gate on the native path.- The intra-frame bin interpolation now lives in dBm space, matching the intensity-space interpolation in
updateWaterfallRow(src/gui/SpectrumWidget.cpp:2856-2859) — consistent across both paths. m_wfMsPerRowis measured from native-tile timecodes (src/gui/SpectrumWidget.cpp:2782), so time-axis labelling stays correct independent of which path produces the row.- Skipping the linear-power accumulator when each frame is its own row is correct (averaging-in-power only matters when you're combining frames).
One small note on the deferred cleanup: the PR body lists m_fftRowDebtMs, m_lastFftFrameMs, m_fftAccumPower, m_fftAccumCount as now-unused. After this diff, m_lastFftRowMs is also write-only — the only remaining reference is the = 0 reset in resetFftWaterfallAccumulator (src/gui/SpectrumWidget.cpp:2226), with no readers. Worth adding to the cleanup-PR scope.
No AppSettings/QSettings or MeterSmoother concerns; no new null/leak surfaces. LGTM.
🤖 aethersdr-agent · cost: $2.8850 · model: claude-opus-4-7
aethersdr-agent Bot
added a commit
that referenced
this pull request
May 24, 2026
…). Principle IX. PR #3031 (TX waterfall scroll-rate parity) removed the debt-gated FFT row emission and per-frame linear-power accumulator from SpectrumWidget::pushWaterfallRow but intentionally left the supporting state in place to keep the behavioural-fix diff focused. This commit removes that now-dead state: - m_lastFftRowMs, m_lastFftFrameMs, m_fftRowDebtMs (pacing timestamps for the removed debt gate) - m_fftAccumPower, m_fftAccumCount (per-bin linear-power accumulator for the removed inter-frame averaging) - SpectrumWidget::resetFftWaterfallAccumulator() and its 4 call sites in setWfLineDuration(), setTransmitting(true), setTxWaterfallSlice(), and clearTxWaterfallSlice() — each was a bare statement with no surrounding logic to untangle Grep across the codebase confirmed the only writes to these members were inside the reset function itself, and no read sites remained anywhere. Pure dead-code removal — 26 LOC net, zero behavioural change. The TX waterfall scroll-rate parity verified by #3031 covers the path. Blast radius: removed symbols are private to SpectrumWidget; the PreToolUse hook flagged risk_score=0.291 against SpectrumWidget.h/.cpp as whole TUs (high-bw MainWindow callers exist) but those callers do not touch the removed private members — confirmed by grep before edit.
ten9876
pushed a commit
that referenced
this pull request
May 24, 2026
…). Principle IX. (#3040) ## Summary Fixes #3038 ### What was changed cleanup(spectrum): remove dead FFT accumulator state after #3031 (#3038). Principle IX. ### Files modified - `src/gui/SpectrumWidget.cpp` - `src/gui/SpectrumWidget.h` ``` src/gui/SpectrumWidget.cpp | 14 -------------- src/gui/SpectrumWidget.h | 12 ------------ 2 files changed, 26 deletions(-) ``` --- Generated by AetherClaude (automated agent for AetherSDR) --- <sub>🤖 aethersdr-agent · cost: $10.0359 · model: claude-opus-4-7</sub> Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com>
This was referenced May 24, 2026
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
## Summary Fix TX waterfall scrolling visibly slower than RX (~3x slower in practice) after PR aethersdr#3019 landed. ## Root cause `pushWaterfallRow` (FFT-derived row path, sole TX waterfall source post-aethersdr#3019) was gating row emission to one row per `m_wfLineDuration` (100 ms by default) on the premise that RX native tiles also arrive at line_duration cadence. They don't. Native tiles arrive at the radio's FFT rate (~30 Hz on this fleet) and `updateWaterfallRow` pushes one row per tile with no rate gate. So RX = ~30 rows/sec and TX = ~10 rows/sec → 3x perceptual slowdown. Pre-aethersdr#3019 this was masked: with show-tx-in-waterfall on, native tiles *also* ran during TX, filling the cadence regardless of whether the FFT gate let a row through. aethersdr#3019 correctly stopped the double-advance — and that exposed the underlying pacing-assumption bug from aethersdr#2666. ## Fix Emit one row per FFT frame from `pushWaterfallRow`, matching the native-tile behaviour exactly. The intermediate-frame linear-power accumulator becomes unnecessary (no intermediates to combine when each frame is its own row); intra-frame bin interpolation is now in dBm space, consistent with `updateWaterfallRow`. ## What I left alone State members (`m_fftRowDebtMs`, `m_lastFftFrameMs`, `m_fftAccumPower`, `m_fftAccumCount`) and `resetFftWaterfallAccumulator()` are now unused but left in place to keep this diff focused on the behavioural fix. Cleanup PR can remove them. ## Diagnostic evidence Probe instrumentation in `pushWaterfallRow` and `updateSpectrum` confirmed before the fix: - FFT frames arrive at ~30 Hz during both RX and TX (~33 ms intervals, no radio-side throttle) - `pushWaterfallRow` was called ~270 times in a 9-second TX window, but only emitted 92 rows (~10 rows/sec, matching the gated cadence) After the fix, every FFT frame produces a row → ~30 rows/sec, parity with RX. ## Stats - 1 file, +22 / -55 - No new flat-key AppSettings (Principle V N/A) - MeterSmoother N/A (no meter code) ## Test plan - [x] Local build clean - [x] Visual confirmation by Jeremy on FLEX-8600 USB at 14.265 MHz: TX waterfall scroll now matches RX speed - [ ] CI: build + check-paths green; check-windows skipped (no build paths changed) — this PR doesn't touch MainWindow.cpp so aethersdr#3028's new filter doesn't fire ## Checklist - [x] No new flat-key `AppSettings` - [x] All meter UI uses `MeterSmoother` (N/A) - [x] Documentation updated if user-visible behavior changed (release notes will mention TX waterfall rate fix) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris
pushed a commit
to G6PWY-Chris/AetherSDR
that referenced
this pull request
Jun 22, 2026
…3031 (aethersdr#3038). Principle IX. (aethersdr#3040) ## Summary Fixes aethersdr#3038 ### What was changed cleanup(spectrum): remove dead FFT accumulator state after aethersdr#3031 (aethersdr#3038). Principle IX. ### Files modified - `src/gui/SpectrumWidget.cpp` - `src/gui/SpectrumWidget.h` ``` src/gui/SpectrumWidget.cpp | 14 -------------- src/gui/SpectrumWidget.h | 12 ------------ 2 files changed, 26 deletions(-) ``` --- Generated by AetherClaude (automated agent for AetherSDR) --- <sub>🤖 aethersdr-agent · cost: $10.0359 · model: claude-opus-4-7</sub> Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix TX waterfall scrolling visibly slower than RX (~3x slower in practice) after PR #3019 landed.
Root cause
pushWaterfallRow(FFT-derived row path, sole TX waterfall source post-#3019) was gating row emission to one row perm_wfLineDuration(100 ms by default) on the premise that RX native tiles also arrive at line_duration cadence.They don't. Native tiles arrive at the radio's FFT rate (~30 Hz on this fleet) and
updateWaterfallRowpushes one row per tile with no rate gate. So RX = ~30 rows/sec and TX = ~10 rows/sec → 3x perceptual slowdown.Pre-#3019 this was masked: with show-tx-in-waterfall on, native tiles also ran during TX, filling the cadence regardless of whether the FFT gate let a row through. #3019 correctly stopped the double-advance — and that exposed the underlying pacing-assumption bug from #2666.
Fix
Emit one row per FFT frame from
pushWaterfallRow, matching the native-tile behaviour exactly. The intermediate-frame linear-power accumulator becomes unnecessary (no intermediates to combine when each frame is its own row); intra-frame bin interpolation is now in dBm space, consistent withupdateWaterfallRow.What I left alone
State members (
m_fftRowDebtMs,m_lastFftFrameMs,m_fftAccumPower,m_fftAccumCount) andresetFftWaterfallAccumulator()are now unused but left in place to keep this diff focused on the behavioural fix. Cleanup PR can remove them.Diagnostic evidence
Probe instrumentation in
pushWaterfallRowandupdateSpectrumconfirmed before the fix:pushWaterfallRowwas called ~270 times in a 9-second TX window, but only emitted 92 rows (~10 rows/sec, matching the gated cadence)After the fix, every FFT frame produces a row → ~30 rows/sec, parity with RX.
Stats
Test plan
Checklist
AppSettingsMeterSmoother(N/A)🤖 Generated with Claude Code