Skip to content

[feature] WAVE Phase 2: Enhance applet visualization controls#2124

Merged
ten9876 merged 2 commits intoten9876:mainfrom
jensenpat:aether/wave-applet-phase2
Apr 28, 2026
Merged

[feature] WAVE Phase 2: Enhance applet visualization controls#2124
ten9876 merged 2 commits intoten9876:mainfrom
jensenpat:aether/wave-applet-phase2

Conversation

@jensenpat
Copy link
Copy Markdown
Collaborator

@jensenpat jensenpat commented Apr 28, 2026

Screenshot 2026-04-27 at 9 51 39 PM

Summary

This expands the WAVE waveform applet into a more useful live signal monitor while keeping the compact applet footprint by default.

  • Double-clicking the waveform now opens or closes a settings drawer instead of clearing samples.
  • Adds a compact View dropdown with four visualizations:
    • Scope: the original waveform trace, still persisted as the deployed Graph setting value.
    • Envelope: a filled RMS envelope with peak outlines for easier level shape reading.
    • History: a side-scrolling bar history view for recent signal activity.
    • Bands: vertical EQ-style frequency bands for a more useful live spectral readout.
  • Adds persisted Zoom and FPS controls in the drawer.
  • Updates the settings controls to use the smaller, lighter applet typography used by the existing power labels.

Details

The drawer is hidden by default and participates in the applet size hint when expanded, so the WAVE applet stays compact until the user asks for controls. The view selector uses the existing guarded combo styling instead of a row of mode buttons, which keeps the drawer from feeling crowded as the visualization list grows.

The zoom control is wired as amplitude gain across all views, not just as a time-window preference, so users can visibly change signal scale in Scope, Envelope, History, and Bands. The FPS control maps directly to the waveform repaint schedule, defaults to 24 fps, and is clamped from 5 to 30 fps to calm down the more active meters without making the display feel stale.

Settings persisted by this PR:

  • WaveApplet_ViewMode
  • WaveApplet_ZoomPercent
  • WaveApplet_RefreshRateHz

For compatibility with the only previously deployed value, Scope continues to save as Graph. The new modes save as Envelope, History, and Bands.

Validation

  • git diff --check
  • cmake --build build -j10 --target AetherSDR

👨🏼‍💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat

@jensenpat jensenpat changed the title Enhance WAVE applet visualization controls [feature] WAVE Phase 2: Enhance applet visualization controls Apr 28, 2026
@jensenpat jensenpat marked this pull request as ready for review April 28, 2026 04:50
@jensenpat jensenpat requested a review from ten9876 as a code owner April 28, 2026 04:50
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

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

Nice work on this, @jensenpat — the four visualizations are a solid addition and the drawer keeps the default footprint compact. The refactoring of the paint path into separate drawGraph/drawEnvelope/drawBars/drawVerticalBars methods is clean, and the Goertzel-based frequency band analysis is a nice approach for the Bands view.

A few things worth addressing before merge:

1. AppSettings::save() on every slider tick

The zoom and FPS sliders connect QSlider::valueChanged directly to a lambda that calls settings.save(). Since GuardedSlider doesn't debounce, dragging either slider writes the full XML settings file to disk on every single tick. At 500 steps across the zoom range that's a lot of atomic write-rename cycles during a drag.

Suggested fix: Either connect to QSlider::sliderReleased for the save (keeping valueChanged for the live preview), or use a short QTimer::singleShot debounce so the save only fires ~200ms after the last change. The setValue + live update on the waveform is fine on every tick — it's just the disk write that should be deferred.

2. setZoomWindowMs reads wrong buffer when paused

void WaveformWidget::setZoomWindowMs(int windowMs)
{
    ...
    if (m_paused) {
        const RingBuffer& buffer = activeBuffer(); // uses m_transmitting, not m_pausedTransmitting

activeBuffer() returns based on current m_transmitting, but when paused the samples should come from the buffer that was active at pause time (m_pausedTransmitting). If the user pauses during TX then TX ends, changing the zoom window would re-snapshot from the (empty/different) RX buffer. This method isn't wired to the UI yet so it's not user-facing today, but worth fixing now to avoid a subtle bug when it gets connected.

3. kPi vs std::numbers::pi

Minor: since the project targets C++20, you can use std::numbers::pi from <numbers> instead of defining kPi. Not a blocker.

4. clearActiveRing removed — no way to clear samples

Double-click now toggles the settings drawer, which is good UX, but the ability to clear the waveform buffer was removed entirely (the clearActiveRing method is deleted and clear() is now only called internally). Consider adding a "Clear" action to the waveform's context menu, similar to what was just done for the CW decode window in #2115/#2116.

What looks good

  • Correctly uses AppSettings (not QSettings) with .save() — consistent with the rest of the codebase
  • QSignalBlocker RAII for blocking signals during init — clean
  • GuardedComboBox/GuardedSlider for the drawer controls — respects the scroll-lock pattern
  • Sanitization on all user-facing inputs (sanitizeWindowMs, sanitizeRefreshRateHz, sanitizeAmplitudeZoom)
  • scheduleRepaint correctly derives the throttle interval from m_refreshRateHz
  • Settings persistence key naming (WaveApplet_ViewMode etc.) is clear and scoped
  • Backward compat: Scope saves as Graph to match the existing deployed value
  • Files changed are all within scope

Thanks for the contribution!

The View / Zoom / FPS controls are essential for discovery — hiding
them behind a double-click on first launch left users unable to
find the new visualizations.  Now expanded by default; double-click
still toggles to the compact form for users who want it.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ten9876 ten9876 merged commit bbd5c5d into ten9876:main Apr 28, 2026
5 checks passed
ten9876 added a commit that referenced this pull request Apr 29, 2026
Community-driven release.  WAVE Phase 2 visualization (#2124),
DAX-aware TCI multi-stream routing for FlexRadio firmware 4.2.18
(#2140), TCXO frequency-offset calibration (#2119), VFO marker
tri-state UX (#2141), v4.2.18 discovery beacon parsing (#2138).
Bug fixes from the community: r8b heap corruption (#2114, NF0T),
serial PTT triple-fix (#2125, chibondking), slice-audio mute on
band change (#2128, jensenpat), CWX Live toggle (#2122, jensenpat),
connect-radio dialog polish (#2121, jensenpat).

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
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.

2 participants