[feature] WAVE Phase 2: Enhance applet visualization controls#2124
[feature] WAVE Phase 2: Enhance applet visualization controls#2124ten9876 merged 2 commits intoten9876:mainfrom
Conversation
There was a problem hiding this comment.
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_pausedTransmittingactiveBuffer() 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(notQSettings) with.save()— consistent with the rest of the codebase QSignalBlockerRAII for blocking signals during init — cleanGuardedComboBox/GuardedSliderfor the drawer controls — respects the scroll-lock pattern- Sanitization on all user-facing inputs (
sanitizeWindowMs,sanitizeRefreshRateHz,sanitizeAmplitudeZoom) scheduleRepaintcorrectly derives the throttle interval fromm_refreshRateHz- Settings persistence key naming (
WaveApplet_ViewModeetc.) is clear and scoped - Backward compat: Scope saves as
Graphto 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]>
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]>
Summary
This expands the WAVE waveform applet into a more useful live signal monitor while keeping the compact applet footprint by default.
Viewdropdown with four visualizations:Scope: the original waveform trace, still persisted as the deployedGraphsetting 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.ZoomandFPScontrols in the drawer.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_ViewModeWaveApplet_ZoomPercentWaveApplet_RefreshRateHzFor compatibility with the only previously deployed value, Scope continues to save as
Graph. The new modes save asEnvelope,History, andBands.Validation
git diff --checkcmake --build build -j10 --target AetherSDR👨🏼💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat