Skip to content

[codex] Smooth FFT trace and clamp display scale#3847

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
rfoust:codex/fft-display-scale-clamp
Jun 27, 2026
Merged

[codex] Smooth FFT trace and clamp display scale#3847
ten9876 merged 2 commits into
aethersdr:mainfrom
rfoust:codex/fft-display-scale-clamp

Conversation

@rfoust

@rfoust rfoust commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Improves the zoomed FFT trace rendering and tightens dBm scale handling in SpectrumWidget. The FFT display trace now uses reusable scratch buffers for the display-only smoothing path, avoiding per-frame vector copies, and skips FFT-line VBO updates when the line width is disabled. The dBm scale interaction now clamps generated ranges before applying or emitting them, preventing invalid spectrum scale requests such as overly high max dBm values during manual scale drags.

Constitution principle honored

Principle II - radio status remains authoritative; this change only validates local UI-generated dBm ranges before sending operator-requested scale changes.

Test plan

  • Local build passes (cmake --build build --target AetherSDR -j8)
  • Existing focused tests pass:
    • ctest --test-dir build --output-on-failure -R "vita_tile_frequency_test|profile_load_command_test|kiwi_sdr_trace_math_test"
  • git diff --check HEAD~1 HEAD
  • Behavior verified on a real radio if applicable

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): not applicable
  • Documentation updated if user-visible behavior changed: not applicable
  • Security-sensitive changes reference a GHSA if applicable: not applicable

@rfoust rfoust marked this pull request as ready for review June 26, 2026 23:39
@rfoust rfoust requested a review from a team as a code owner June 26, 2026 23:39
Copilot AI review requested due to automatic review settings June 26, 2026 23:39

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 improves SpectrumWidget’s spectrum rendering and dBm scale interaction by reducing per-frame allocations/copies in the FFT display trace path and clamping UI-generated dBm ranges before they’re applied/emitted, preventing invalid scale requests during drag interactions.

Changes:

  • Reworks buildFftDisplayTrace() to return a const QVector<float>& backed by reusable scratch buffers, avoiding per-frame QVector copies in the display-only smoothing/resampling path.
  • Adds centralized dBm-range clamping helpers and applies them during dBm scale press/drag/release and immediate range application to prevent out-of-bounds/invalid ranges.
  • Skips GPU FFT-line VBO updates (and line drawing) when the FFT line width is disabled.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/gui/SpectrumWidget.h Updates buildFftDisplayTrace() signature and adds scratch buffers for display smoothing/resampling.
src/gui/SpectrumWidget.cpp Implements scratch-backed FFT trace generation, clamps dBm scale interactions, and avoids unnecessary GPU VBO updates when line width is off.

@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 @rfoust — this is a tidy, well-scoped change to SpectrumWidget. I traced both halves against the surrounding code and it holds up. CI is green across all six checks (build, macOS, Windows, CodeQL, accessibility).

What I verified

Scratch-buffer reuse (buildFftDisplayTrace) — Correct. The two scratch buffers don't alias: m_fftDisplaySmoothScratch (smoothing) is read and m_fftDisplayTraceScratch (resample) is written in the trace path, and the input bins (displaySpectrumBins()m_smoothed/m_kiwiSdrFftTrace) is never one of the scratch vectors. Both call sites (renderGpuFrame, drawSpectrum) are compile-gated by AETHER_GPU_SPECTRUM so only one is live, and each invokes the function once per frame and consumes the result before anything else touches the buffers — so the reference return is safe today.

Line-VBO skip — Safe. The new if (m_fftLineWidth > 0.0f) guard on updateDynamicBuffer(m_fftLineVbo, …) mirrors the existing draw-side guard at the line pass (if (m_fftLineWidth > 0.0f) before cb->draw(...) on m_fftLineVbo), so the buffer is never drawn stale when the update is skipped.

dBm clamp helpers — The clamp math is robust: clampDbmRange handles non-finite inputs, inverted min/max ordering, and degenerate equal-value cases, always converging to a valid [10, 180] dB span within [-180, 80] dBm. The arrow-click / drag / release paths now route through these consistently, and the release path correctly bails (resets drag flags, no emit) on non-finite state rather than emitting a garbage range. This matches the stated goal of not sending invalid operator scale requests.

One minor note (non-blocking)

buildFftDisplayTrace changed from returning by value to returning const QVector<float>& into shared mutable scratch. It's safe for all current callers, but it's now a footgun if a future caller ever holds two results live (const auto& a = build(...); const auto& b = build(...);a would be clobbered). A one-line comment on the declaration noting "result is valid only until the next call" would save a future maintainer the surprise. Your call — not a blocker.

Nice cleanup overall. 👍


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

The trace builder now returns a reference into shared mutable scratch buffers
(m_fftDisplaySmoothScratch / m_fftDisplayTraceScratch), so the result is only
valid until the next call. Document that on the declaration so a future caller
doesn't hold two results live and silently clobber the first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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

Approved. Verified both halves independently against the diff and call sites:

  • Scratch-buffer reuse — the two scratch buffers don't alias each other or the input (displaySpectrumBins()), and both call sites (renderGpuFrame, drawSpectrum) consume the result in place with no two-results-live, so the transient-reference return is safe. (Documented that contract on the declaration in a follow-up commit.)
  • dBm clamp — robust across the edge cases (inverted, equal, span >180 / <10, non-finite); all interaction paths (arrow, control-drag, pan-drag, move, release) route through the helpers consistently, and the release path correctly bails without emitting a garbage range.

Display-only; Principle II honored (clamps UI-generated requests before sending, never overrides radio-authoritative state). CI green. Thanks @rfoust — clean, well-scoped work.

@ten9876 ten9876 enabled auto-merge (squash) June 27, 2026 02:47
@ten9876 ten9876 merged commit bb33bb4 into aethersdr:main Jun 27, 2026
5 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

Development

Successfully merging this pull request may close these issues.

3 participants