Skip to content

feat(spectrum): add "Purple" waterfall color scheme#3583

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
sblanchard:feat/purple-waterfall-colormap
Jun 14, 2026
Merged

feat(spectrum): add "Purple" waterfall color scheme#3583
ten9876 merged 1 commit into
aethersdr:mainfrom
sblanchard:feat/purple-waterfall-colormap

Conversation

@sblanchard

@sblanchard sblanchard commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

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 positions 0.00 / 0.15 / 0.30 / 0.45 / 0.60 / 0.75 / 1.00.

It follows the existing theme-token architecture:

  • One enum value, appended after Plasma so the persisted DisplayWfColorScheme index is unchanged for existing users.
  • Two switch cases mapping it to the color.waterfall.colormap.purple token and the "Purple" display label.
  • Gradient stops added to both bundled themes (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 via wfSchemeName().

Test plan

  • Local build passes (cmake --build build)
  • Scheme selectable in the overlay menu; renders black → … → white floor-to-peak
  • Existing tests pass (CI)

Checklist

  • No new flat-key AppSettings calls (reuses the existing DisplayWfColorScheme)
  • All meter UI uses MeterSmoother — N/A (no meters touched)
  • Commits are signed (SSH)
  • Security-sensitive changes reference a GHSA — N/A

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.
@sblanchard sblanchard requested a review from a team as a code owner June 14, 2026 13:42

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

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() and wfSchemeName() are the only two functions that switch over WfColorScheme, and both now have the Purple case (SpectrumWidget.cpp:423, :474). Both retain a default: arm, so no -Wswitch fallout from the new enumerator.
  • Persisted index preserved. Purple is appended after Plasma and before the Count sentinel, so the existing DisplayWfColorScheme indices for current users are unchanged. ✔
  • Auto-population is genuine. rebuildWfStopsCacheFromTheme() iterates 0..Count and resolves each token from the theme (SpectrumWidget.cpp:431), and SpectrumOverlayMenu.cpp:1412 builds 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. purple is nested as a sibling under color.waterfall.colormap in both default-dark.json and default-light.json, so the color.waterfall.colormap.purple token 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

@ten9876 ten9876 merged commit 4e0b3b9 into aethersdr:main Jun 14, 2026
6 checks passed
@ten9876

ten9876 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

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 Plasma so existing users' persisted DisplayWfColorScheme indices don't shift, driving everything from the theme tokens with no hardcoded stop table, and updating both bundled themes. The write-up made review fast, too.

It'll ship in v26.6.3. Hope to see more from you — and 73!

ten9876 added a commit that referenced this pull request Jun 14, 2026
#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>
@sblanchard sblanchard changed the title feat(spectrum): add SmartSDR "Purple" waterfall color scheme feat(spectrum): add "Purple" waterfall color scheme Jun 14, 2026
jensenpat pushed a commit that referenced this pull request Jun 19, 2026
…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>
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