Skip to content

Reassert desired panadapter and waterfall display rates#2465

Merged
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:aether/pan-fps-reconcile
May 8, 2026
Merged

Reassert desired panadapter and waterfall display rates#2465
ten9876 merged 2 commits into
aethersdr:mainfrom
jensenpat:aether/pan-fps-reconcile

Conversation

@jensenpat

@jensenpat jensenpat commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes radio-restored display-rate drift for both panadapter FFT FPS and waterfall Rate: / line_duration.

Aether has app/user desired display settings, for example:

  • Panadapter FFT FPS: DisplayFftFps / SpectrumWidget::fftFps(), often 30 FPS.
  • Waterfall Rate: slider: DisplayWfLineDuration / SpectrumWidget::wfLineDuration(), defaulting to 100 ms.

The Flex radio can later restore persisted band/display state and report values such as:

  • display pan ... fps=25
  • display waterfall ... line_duration=80

Before this PR, those radio-restored values could silently override the radio-side display behavior while Aether's UI still showed the desired values. This PR treats Aether's persisted/widget settings as the desired truth and reconciles owned radio display status back to those settings after a short debounce.

User-Facing Behavior

The waterfall control in Aether's panadapter overlay is labeled Rate:, not line_duration.

Current mapping:

  • UI label: Rate:
  • persisted setting: DisplayWfLineDuration
  • widget API: SpectrumWidget::wfLineDuration() / setWfLineDuration(int)
  • radio status/command field: line_duration
  • slider range: 1..30
  • command value: line_duration = slider + 70
  • default/max Rate: 30 maps to line_duration=100

With desired Rate: 30 / DisplayWfLineDuration=100, if the radio restores line_duration=80 after a band/display-state transition, Aether now reasserts:

display panafall set <waterfall_id> line_duration=100

Root Cause

Aether's UI and settings kept the desired values, but the radio can restore persisted display state after common operating actions. Those restores arrive through normal radio status messages, not necessarily through Aether's centralized tuning policy.

This means drift can happen on paths such as:

  • band jumps
  • go-to frequency
  • spots
  • memories
  • shortcuts
  • pan scrolling/dragging across band/display-state boundaries
  • radio-side persisted band/display state restores

The old code did not track the radio-reported display FPS or waterfall line duration separately enough to reconcile those radio-side restores back to Aether's desired settings.

What Changed

Panadapter FPS Reconciliation

  • PanadapterModel now tracks the latest radio-reported fps.
  • It emits:
    • fpsChanged(int) only when the reported value changes.
    • fpsReported(int) for every valid fps= status, including repeated restore bursts.
  • MainWindow::wirePanadapter() wires each owned panadapter to schedulePanFpsReconcile().
  • If the radio-reported FPS differs from sw->fftFps(), Aether debounces for 300 ms, re-checks state, then sends:
display pan set <pan_id> fps=<desired>

Waterfall Rate / Line Duration Reconciliation

  • PanadapterModel::applyWaterfallStatus() now tracks the latest radio-reported line_duration.
  • It emits:
    • waterfallLineDurationChanged(int) only when the reported value changes.
    • waterfallLineDurationReported(int) for every valid line_duration= status, including repeated restore bursts.
  • MainWindow::wirePanadapter() wires each owned panadapter to scheduleWaterfallLineDurationReconcile().
  • If the radio-reported waterfall line duration differs from sw->wfLineDuration(), Aether debounces for 300 ms, re-checks state, validates the waterfall ID, then sends:
display panafall set <waterfall_id> line_duration=<desired>

Debounce, Cooldown, And Cleanup

  • Both reconcile paths use per-pan state.
  • Both paths debounce briefly to absorb startup and drag/status bursts.
  • Both paths use a 5 s same-desired cooldown to avoid command spam if the radio keeps refusing or repeatedly restoring a value.
  • Both paths clear their cooldown guard when the radio reports the desired matching value, so a later fresh restore is corrected promptly.
  • Timers and signal connections are cleaned up when a panadapter is removed.
  • Reconcile state is cleared on disconnect.

Removed Non-Reset Hardcoded FPS Sends

This PR also removes the remaining non-reset hardcoded fps=25 display setup sends:

  • RadioModel::createPanadapter() no longer sends fps=25 while configuring dimensions/dBm.
  • RadioModel::configurePan() no longer sends fps=25 while configuring dimensions/dBm.
  • Multi-pan layout creation no longer sends fps=25 after display panafall create.

The explicit display-settings reset path still sends and persists fps=25, because in that case 25 is the app/user desired reset value.

Important Behavioral Guarantees

  • Aether's persisted/widget values are the desired truth.
  • Radio-restored fps=25 is not adopted into DisplayFftFps.
  • Radio-restored line_duration=80 is not adopted into DisplayWfLineDuration.
  • Explicit user changes still win.
    • If the user sets FPS to 25, desired FPS becomes 25 and no 30 FPS reassert occurs.
    • If the user moves the Rate: slider so desired line duration becomes 80, desired line duration becomes 80 and no 100 ms reassert occurs.
  • Multiple panadapters are reconciled independently.
  • Slice/tune/pan command ordering is otherwise unchanged.
  • Logging is low-noise and only occurs when Aether actually reasserts:
    • MainWindow: reasserting panadapter FPS ...
    • MainWindow: reasserting waterfall rate ...

Evidence From Logs

Fresh long-run evidence showed the waterfall sibling of the original FPS bug:

  • 20:44:38.308 go-to 28 MHz / 10m
  • 20:44:38.469 radio reports fps=25 and line_duration=80
  • 20:44:38.796 Aether reasserts pan FPS to 30
  • no display panafall set ... line_duration=100 follows
  • perf stays degraded at approximately wfFps=12-13 wfLineMs=80

Restart evidence showed the expected recovery behavior:

  • radio briefly reports 10m line_duration=80
  • startup display setup sends display panafall set ... line_duration=100
  • radio echoes line_duration=100
  • waterfall returns to approximately 25 FPS

This PR brings that restart-only recovery behavior into normal runtime reconciliation.

Flex API Check

Checked the downloaded FlexLib v4.2.18.41174 API files:

  • Panadapter.cs

    • Panadapter.FPS sends display pan set 0x<stream> fps=<value>.
    • panadapter status fps=<value> updates the internal FPS property and raises FPS changed.
  • Waterfall.cs

    • Waterfall.FallLineDurationMs sends display panafall set 0x<stream> line_duration=<value>.
    • waterfall status line_duration=<value> updates the internal line-duration property and raises FallLineDurationMs changed.

That matches this PR's approach: reconcile display-rate commands independently from slice/tune/pan movement behavior.

Validation

Built successfully with the required AetherSDR build command:

env -u CPLUS_INCLUDE_PATH cmake --build build-ninja --parallel 22

Also ran:

git diff --check

No whitespace errors were reported.

Expected Runtime Verification

With desired FFT FPS of 30 and desired waterfall Rate: 30 / DisplayWfLineDuration=100:

  1. Startup should still send desired display settings through the existing display setup paths.
  2. If the radio later reports display pan ... fps=25 for an owned pan, Aether should send display pan set <pan_id> fps=30 shortly after status settles.
  3. If the radio later reports display waterfall ... line_duration=80 for an owned waterfall, Aether should send display panafall set <waterfall_id> line_duration=100 shortly after status settles.
  4. If the radio already reports the desired values, Aether should not send repeated commands.
  5. If the user explicitly changes FPS or Rate:, reconciliation should use the new desired values.

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

Co-authored-by: Codex <noreply@openai.com>
@jensenpat jensenpat marked this pull request as ready for review May 8, 2026 02:59
@jensenpat jensenpat requested a review from ten9876 as a code owner May 8, 2026 02:59
Co-authored-by: Codex <noreply@openai.com>
@jensenpat jensenpat changed the title Reassert desired panadapter FPS after radio restores Reassert desired panadapter and waterfall display rates May 8, 2026
@jensenpat jensenpat marked this pull request as draft May 8, 2026 03:55
@jensenpat jensenpat marked this pull request as ready for review May 8, 2026 04:06
@ten9876 ten9876 merged commit d50ac4e into aethersdr:main May 8, 2026
4 checks passed
@ten9876

ten9876 commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Claude here — really sound architectural fix, jensenpat. The Changed-vs-Reported signal split is exactly the right primitive: silent drift via repeated same-value restores is precisely the class of bug that gets missed when reconcile only fires on value changes. Per-pan state with QPointer, connection-handle bookkeeping for rewire, debounce + same-desired cooldown but bypass for changed-desired — every detail considered. The complementary removal of hardcoded fps=25 sends from non-reset paths is the right pairing; otherwise reconcile would race with the legacy commands on fresh pan creation. PR description's enumeration of trigger paths (band jumps, spots, memories, drags) is itself useful documentation for future readers. This pattern is reusable too — any other 'Aether desired vs radio reported' divergence can follow the same shape. Merged.

73, Jeremy KK7GWY & Claude (AI dev partner)

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