feat(spectrum): add "Purple" waterfall color scheme#3583
Conversation
Reproduces SmartSDR's "Add Purple" gradient exactly: black → blue → green → yellow → red → purple (#800080) → white at 0.00/0.15/0.30/0.45/0.60/0.75/1.00, taken verbatim from the SmartSDR 4.2 reference (PanafallViewModel.waterfallGradientPurple). Additive: Purple is appended to WfColorScheme after Plasma, so the persisted DisplayWfColorScheme index is unchanged for existing users. Two switch cases map it to the color.waterfall.colormap.purple token and the "Purple" combo label; gradient stops are added to both bundled themes (default-dark.json, default-light.json). Colormaps load only from the qrc JSON, so the overlay combo and the gradient editor pick the scheme up automatically — no hardcoded stop table.
There was a problem hiding this comment.
Reviewed the diff against the surrounding code — this is a clean, correctly-scoped additive change. Thanks for the careful PR write-up, @sblanchard; it made verification fast. 🟣
What I verified:
- Both switches updated.
wfSchemeToken()andwfSchemeName()are the only two functions that switch overWfColorScheme, and both now have thePurplecase (SpectrumWidget.cpp:423,:474). Both retain adefault:arm, so no-Wswitchfallout from the new enumerator. - Persisted index preserved.
Purpleis appended afterPlasmaand before theCountsentinel, so the existingDisplayWfColorSchemeindices for current users are unchanged. ✔ - Auto-population is genuine.
rebuildWfStopsCacheFromTheme()iterates0..Countand resolves each token from the theme (SpectrumWidget.cpp:431), andSpectrumOverlayMenu.cpp:1412builds the combo the same way — so the new scheme picks up in both the stops cache and the combo box with no further wiring. No hardcoded stop table, as claimed. - Theme entries land correctly.
purpleis nested as a sibling undercolor.waterfall.colormapin bothdefault-dark.jsonanddefault-light.json, so thecolor.waterfall.colormap.purpletoken resolves. The two theme files carry identical stops, which is consistent with how the existing colormaps (grayscale/fire/plasma) are defined identically across both files — a waterfall colormap is an intensity ramp, not a theme-relative palette, so that's the right call.
No AppSettings/QSettings, RAII, null-pointer, or resource concerns — the change is data + two enum cases, with the existing < 2 stops fallback in the cache already guarding a malformed token.
Code LGTM. The remaining unchecked test-plan boxes (on-radio visual check, CI) are the only things outstanding, and those are runtime confirmations rather than code issues.
🤖 aethersdr-agent · cost: $2.8462 · model: claude-opus-4-8
|
Merged — welcome aboard, @sblanchard, and thank you for your first contribution to AetherSDR! 🟣 This is exactly the shape we love to see from a first PR: tightly scoped, SmartSDR-faithful (value-exact gradient stops), and architecturally clean — appending the enum after It'll ship in v26.6.3. Hope to see more from you — and 73! |
#3583 landed on main after the release branch was cut; it ships in v26.6.3, so record it in the release notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d waterfall floor (#3586) ## Summary Makes the waterfall noise floor render as a flat, evenly-levelled band by using the **radio's own per-tile auto-black level** instead of a client-side estimate. AetherSDR was computing its own noise-floor estimate and sending the radio `auto_black=0`, which produced a textured, black-speckled floor. The radio already computes a noise-floor black level and embeds it in every waterfall tile (FlexLib's `WaterfallTile.AutoBlackLevel`) — AetherSDR even parsed it (`PanadapterStream.cpp`) and emitted `waterfallAutoBlackLevel()`, but the value was discarded (the handler clamped the raw uint16 to 0–125). This wires it up: 1. `RadioModel` sends `auto_black=1` so the radio populates the tile field. 2. `MainWindow_Session` feeds the per-tile value into the renderer via the new `SpectrumWidget::setRadioAutoBlackLevel()`. 3. `intensityToRgb` maps `low` = radio auto-black, `high` = `wfHighThresholdRaw()` — a cubic colour-gain curve: ``` num = (100 − colorGain)/100 · cbrt(65535 − low) high = low + num³ (floored at low + 100) ``` then `t = (rawValue − low) / (high − low)` → gradient. The client-side estimate stays as a fallback (FFT fallback / auto-black off). Both display paths are covered — the native waterfall is coloured on the CPU via `intensityToRgb`, then uploaded as a texture (no GPU-side mapping). ## Reviewer note Try it with **Waterfall (Color) Gain ≈ 30** and **Auto Black Level ≈ 30** — it works very well, particularly with the new **Purple** colour scheme (#3583). At default Color Gain and Black Level offset = 50 (no bias), the floor follows the radio's computed level directly. ## Constitution principle honored **Principle I — FlexLib Is The Protocol Authority.** Replaces a client-side approximation with the radio's own auto-black value (FlexLib's per-tile `AutoBlackLevel`), the authoritative noise-floor figure. It also **reverses a deliberate client-side decision** and changes a rendering default for all waterfall schemes, so it is flagged for **maintainer sign-off** on the look (Principle XIII — the operator/maintainer is the authority on visual design). ## Test plan - [x] Local build passes (`cmake --build build`) — links clean - [x] Verified on a live FLEX-8600 (fw 4.2.20.41343) — floor now evenly levelled - [ ] Existing tests pass (CI) - [ ] Maintainer review of the architecture reversal + visual default ## Open questions for review - The auto-black **offset** slider now biases the radio black point (`(50 − offset)·0.5·128` raw); scaling is open to tuning. - Initial waterfall config sends `auto_black=1` unconditionally; could instead reflect the persisted client auto-black state. ## Checklist - [x] No new flat-key `AppSettings` calls (none added) - [x] All meter UI uses `MeterSmoother` — N/A (no meters touched) - [x] Commits are signed (SSH) - [x] Security-sensitive changes reference a GHSA — N/A --------- Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Adds a "Purple" waterfall colour scheme — an additive preset that joins the existing ones (Default, Grayscale, Blue-Green, Fire, Plasma) in the panadapter overlay colour-scheme combo. The gradient runs black → blue → green → yellow → red → purple (
#800080) → white at positions0.00 / 0.15 / 0.30 / 0.45 / 0.60 / 0.75 / 1.00.It follows the existing theme-token architecture:
Plasmaso the persistedDisplayWfColorSchemeindex is unchanged for existing users.color.waterfall.colormap.purpletoken and the"Purple"display label.default-dark.json,default-light.json).No hardcoded stop table — waterfall colourmaps load only from the qrc JSON, so the combo box and the gradient editor pick the new scheme up automatically.
Constitution principle honored
Principle III — User-Facing Names Match The Visible UI Labels.
"Purple"is the single source for the combo-box entry viawfSchemeName().Test plan
cmake --build build)Checklist
AppSettingscalls (reuses the existingDisplayWfColorScheme)MeterSmoother— N/A (no meters touched)