Reassert desired panadapter and waterfall display rates#2465
Conversation
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
|
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) |
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:
DisplayFftFps/SpectrumWidget::fftFps(), often 30 FPS.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=25display waterfall ... line_duration=80Before 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:, notline_duration.Current mapping:
Rate:DisplayWfLineDurationSpectrumWidget::wfLineDuration()/setWfLineDuration(int)line_duration1..30line_duration = slider + 70Rate: 30maps toline_duration=100With desired
Rate: 30/DisplayWfLineDuration=100, if the radio restoresline_duration=80after a band/display-state transition, Aether now reasserts: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:
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
PanadapterModelnow tracks the latest radio-reportedfps.fpsChanged(int)only when the reported value changes.fpsReported(int)for every validfps=status, including repeated restore bursts.MainWindow::wirePanadapter()wires each owned panadapter toschedulePanFpsReconcile().sw->fftFps(), Aether debounces for 300 ms, re-checks state, then sends:Waterfall Rate / Line Duration Reconciliation
PanadapterModel::applyWaterfallStatus()now tracks the latest radio-reportedline_duration.waterfallLineDurationChanged(int)only when the reported value changes.waterfallLineDurationReported(int)for every validline_duration=status, including repeated restore bursts.MainWindow::wirePanadapter()wires each owned panadapter toscheduleWaterfallLineDurationReconcile().sw->wfLineDuration(), Aether debounces for 300 ms, re-checks state, validates the waterfall ID, then sends:Debounce, Cooldown, And Cleanup
Removed Non-Reset Hardcoded FPS Sends
This PR also removes the remaining non-reset hardcoded
fps=25display setup sends:RadioModel::createPanadapter()no longer sendsfps=25while configuring dimensions/dBm.RadioModel::configurePan()no longer sendsfps=25while configuring dimensions/dBm.fps=25afterdisplay 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
fps=25is not adopted intoDisplayFftFps.line_duration=80is not adopted intoDisplayWfLineDuration.Rate:slider so desired line duration becomes 80, desired line duration becomes 80 and no 100 ms reassert occurs.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.308go-to 28 MHz / 10m20:44:38.469radio reportsfps=25andline_duration=8020:44:38.796Aether reasserts pan FPS to 30display panafall set ... line_duration=100followswfFps=12-13 wfLineMs=80Restart evidence showed the expected recovery behavior:
line_duration=80display panafall set ... line_duration=100line_duration=100This 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.csPanadapter.FPSsendsdisplay pan set 0x<stream> fps=<value>.fps=<value>updates the internal FPS property and raisesFPSchanged.Waterfall.csWaterfall.FallLineDurationMssendsdisplay panafall set 0x<stream> line_duration=<value>.line_duration=<value>updates the internal line-duration property and raisesFallLineDurationMschanged.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:
Also ran:
No whitespace errors were reported.
Expected Runtime Verification
With desired FFT FPS of 30 and desired waterfall
Rate: 30/DisplayWfLineDuration=100:display pan ... fps=25for an owned pan, Aether should senddisplay pan set <pan_id> fps=30shortly after status settles.display waterfall ... line_duration=80for an owned waterfall, Aether should senddisplay panafall set <waterfall_id> line_duration=100shortly after status settles.Rate:, reconciliation should use the new desired values.👨🏼💻 Generated with OpenAI Codex (GPT-5.5 Pro 4/23) and tested by @jensenpat