Skip to content

Add spectrum position lock#2643

Closed
rfoust wants to merge 1 commit into
aethersdr:mainfrom
rfoust:codex/lock-spectrum-location
Closed

Add spectrum position lock#2643
rfoust wants to merge 1 commit into
aethersdr:mainfrom
rfoust:codex/lock-spectrum-location

Conversation

@rfoust

@rfoust rfoust commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a right-click Lock Spectrum Position option to the panadapter spectrum display.

When enabled, the spectrum trace/noise floor keeps the same vertical screen position the user selected. Users can still drag the dBm scale normally; the lock records that new position and uses it as the target. As the measured spectrum baseline changes, the widget adjusts the dBm display range to bring the trace back to that target position.

The implementation also handles:

  • Zoom/range changes by reacquiring from fresh FFT frames while preserving the locked position
  • Manual dBm drag followed by zoom, including stale pending echo cleanup
  • Smooth display motion instead of visible stepping
  • Short upward noise-floor spikes, such as lightning crashes, by requiring them to persist before the lock follows them
  • Existing noise-floor auto-adjust being disabled while this lock is active

Test Plan

  • Built successfully with:

    cmake --build build -j4
  • Manual checks to verify:

    • Enable Lock Spectrum Position from the spectrum context menu
    • Drag the dBm scale up/down and confirm the new position becomes the locked target
    • Zoom in/out and confirm the spectrum returns to the locked vertical position
    • Change antenna/noise floor and confirm persistent level changes are followed
    • Confirm short lightning-crash spikes do not immediately pull the spectrum position

Copilot AI review requested due to automatic review settings May 14, 2026 01:10
@rfoust rfoust requested review from jensenpat and ten9876 as code owners May 14, 2026 01:10

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

Adds a spectrum position lock feature to the panadapter so the displayed spectrum/noise baseline can remain at a selected vertical position while range changes are automatically adjusted.

Changes:

  • Adds lock state and baseline/target tracking fields to SpectrumWidget.
  • Implements baseline estimation, lock target refresh/reset, smoothing, transient filtering, and dBm range command throttling.
  • Adds a right-click context-menu action for toggling Lock Spectrum Position and disables existing noise-floor auto-adjust while active.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/gui/SpectrumWidget.h Declares spectrum lock helpers and state fields.
src/gui/SpectrumWidget.cpp Implements lock behavior, integrates it with FFT updates, dBm dragging, zoom/range changes, and the context menu.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/gui/SpectrumWidget.cpp Outdated
Comment on lines +803 to +813
if (on && m_measuredNoiseFloorDbm > -500.0f) {
m_spectrumLockBaselineDbm = m_measuredNoiseFloorDbm;
m_spectrumLockBaselineValid = true;
m_spectrumLockLastSampleMs = QDateTime::currentMSecsSinceEpoch();
refreshSpectrumLocationLockTarget();
}
}

void SpectrumWidget::refreshSpectrumLocationLockTarget()
{
if (m_spectrumLocationLockEnabled && !m_spectrumLockBaselineValid

@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 for the well-documented PR, @rfoust — the extensive handling of zoom, manual drag, lightning-spike rejection, and smooth motion is nice work, and the refactor of the noise-floor estimator into estimateSpectrumBaselineDbm() is a clean side benefit.

A couple of suggestions:

1. Stale m_measuredNoiseFloorDbm on first enable (Copilot finding confirmed)

In setSpectrumLocationLockEnabled(true) (SpectrumWidget.cpp:813), the lock target is seeded from m_measuredNoiseFloorDbm. That EWMA (α=0.05, ~20 frames / ~0.8 s convergence) is updated continuously and is not reset on setFrequencyRange() or in the zoom paths — resetSpectrumLocationLockMeasurement() only runs when the lock is already enabled. If a user enables the lock shortly after changing bands or zoom, the seed baseline reflects the previous band and the target fraction will lock the spectrum to the wrong vertical position until the EWMA converges.

Suggested fix: on first enable, don't seed from m_measuredNoiseFloorDbm. Instead, set m_spectrumLockFreshFrameCount = 5 (the same mechanism resetSpectrumLocationLockMeasurement() already uses) and let updateSpectrumLocationLock(..., forceBaseline=true) establish baseline + target from the next fresh FFT frame.

2. Setting not persisted across sessions

Nearby panadapter context-menu toggles are persisted via AppSettings — see ExtendedFrequencyLine (SpectrumWidget.cpp:457), SingleClickTune (:455), ShowTuneGuides (:456). The new Lock Spectrum Position toggle resets every session. Consider adding a LockSpectrumPosition key to the same load/save block so it behaves like its menu neighbors.

Neither is a blocker; the rest of the implementation looks solid. 73

@rfoust rfoust force-pushed the codex/lock-spectrum-location branch from bc33793 to 63ea1c0 Compare May 14, 2026 01:40
@rfoust

rfoust commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Updated the branch to address the stale first-enable baseline concern.

setSpectrumLocationLockEnabled(true) no longer seeds the lock from m_measuredNoiseFloorDbm; it now waits for fresh FFT frames via the existing fresh-frame path before capturing the target position. refreshSpectrumLocationLockTarget() also falls back to current FFT bins if it needs to seed a baseline before recording a manual target.

I intentionally left Lock Spectrum Position non-persistent for this PR.

@ten9876

ten9876 commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Hi @rfoust — thanks for the careful algorithm work on this PR. The smoothing + transient-rejection design is exactly what the existing Display → Floor: noise-floor auto-adjust needed.

Closing this PR in favour of #2653, which cherry-picks your algorithm and applies it to the existing Floor: feature path instead of adding a second parallel right-click toggle. Two implementations of the same feature would have been confusing for users ("why are there two ways to lock the noise floor?") and a maintenance burden.

Concretely, #2653 keeps:

  • ✅ Your per-frame asymmetric smoothing (τ = 0.08–0.12 s on drops, τ = 1.2–8 s on rises)
  • ✅ Your candidate-state transient filter (16 frames / 1.2 s for upward 4 dB+ shifts)
  • ✅ Your pan-instead-of-zoom math (signals no longer change visual height when the floor drifts)
  • ✅ Your smooth motion logic (τ = 0.10–0.24 s instead of 10-frame stepping)
  • ✅ Your command throttling (150 ms / 0.75 dB threshold)
  • ✅ Your shared estimateNoiseFloorDbm() helper (refactored both call sites onto it)
  • ✅ Your reset triggers in the zoom/band-change/dBm-drag paths

And consolidates onto:

  • The existing m_noiseFloorEnable toggle (Display → Floor: button)
  • The existing m_noiseFloorPosition slider as the target fraction

Your authorship is preserved via Co-Authored-By: rfoust on the squash commit. Thanks for the algorithm — really nice work on the lightning-crash rejection.

73, Jeremy KK7GWY & Claude (AI dev partner)

@ten9876 ten9876 closed this May 14, 2026
ten9876 added a commit that referenced this pull request May 14, 2026
…ansient rejection + pan instead of zoom (#2653)

Cherry-picks rfoust's algorithm work from PR #2643 (Lock Spectrum Position) and consolidates it into the existing Display → Floor: auto-adjust feature instead of adding a parallel right-click toggle.

The old implementation used 20th-percentile / every-10-frames / zoom-the-span and had three visible problems:
- Visible stepping as the range changed in 10-frame jumps
- Span varied with the floor, making signal visual heights jump
- Lightning crashes and key-up edge clicks pulled the lock immediately

New algorithm (rfoust):
- Per-frame baseline update with asymmetric smoothing — τ ≈ 0.08–0.12 s on drops (track noise reductions fast), τ ≈ 1.2–8 s on rises (don't chase upward transients).
- Candidate-state transient filter — ≥4 dB upward shifts must persist 16 frames / 1.2 s before being adopted; downward 2 frames / 70 ms.
- Pan instead of zoom: m_refLevel slides to keep the smoothed floor at the slider position; m_dynamicRange (span) stays fixed.  Signals no longer change visual height when the floor drifts.
- Smooth motion (τ ≈ 0.10–0.24 s) instead of 10-frame stepping.
- Command throttling — ≥150 ms / ≥0.75 dB between dbmRangeChangeRequested emits.
- Two-pass trimmed-mean noise-floor estimator extracted into a shared estimateNoiseFloorDbm() helper (was duplicated between the floor overlay line and the auto-adjust path).
- Reset triggers in 5 zoom/band-change/dBm-drag paths so the baseline re-acquires after disruptions.

Live-tested on FLEX-8600.  Closes #2643 (rfoust's PR was closed in favour of this consolidation).

Co-Authored-By: rfoust <rfoust@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <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.

3 participants