Skip to content

fix(waterfall): TX scroll rate now matches RX#3031

Merged
ten9876 merged 1 commit into
mainfrom
auto/fix-tx-waterfall-scroll-rate
May 24, 2026
Merged

fix(waterfall): TX scroll rate now matches RX#3031
ten9876 merged 1 commit into
mainfrom
auto/fix-tx-waterfall-scroll-rate

Conversation

@ten9876

@ten9876 ten9876 commented May 24, 2026

Copy link
Copy Markdown
Collaborator

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 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-#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 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

Checklist

  • No new flat-key AppSettings
  • All meter UI uses MeterSmoother (N/A)
  • Documentation updated if user-visible behavior changed (release notes will mention TX waterfall rate fix)

🤖 Generated with Claude Code

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>
@ten9876 ten9876 requested a review from jensenpat as a code owner May 24, 2026 03:54
@ten9876 ten9876 enabled auto-merge (squash) May 24, 2026 03:55

@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 @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_wfMsPerRow is 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

@ten9876 ten9876 merged commit 36afe85 into main May 24, 2026
5 checks passed
@ten9876 ten9876 deleted the auto/fix-tx-waterfall-scroll-rate branch May 24, 2026 04:39
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>
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>
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.

1 participant