You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary
Closes#5537.
Right-click the panadapter or waterfall and choose **Waterfall Time
Markers** to display thin horizontal lines with small UTC timestamps at
the left edge. Intervals are Off, 15/30 seconds, and 1/5/10/15 minutes.
Off remains the default, and the selection persists independently per
panadapter slot in the existing Display document.
Markers align to clock boundaries (for example, HH:mm:00 for one-minute
markers) and follow their captured signal rows through live scrolling,
paused history, and resize. Changing the waterfall rate affects future
row cadence; existing history retains its captured row spacing. GPU and
software rendering share timestamp geometry and a cached label atlas.
Dedicated theme tokens provide subdued pale gray-blue lines/text with a
dark label backing.
This is a client display feature with no radio protocol, TX, dependency,
or unrelated behavior changes. Final visual/UX acceptance is requested
from the maintainer. The minimum interval is intentionally 15 seconds to
limit clutter.
## Review follow-up (maintainer-requested)
Four review nits from #5538 are addressed in `Address review nits on
waterfall time markers`:
- **30-minute and 1-hour intervals retired.** Issue #5537 proposed nine
intervals, but `kWaterfallHistoryMs` caps retained history at 20 minutes
and
a screenful of waterfall is only ~11-19 s at typical rates — measured on
the
demo simulator at 230-410 rows and 40-47 ms/row. A 1-hour marker would
be on
screen roughly 0.4% of the time and a 30-minute one ~0.9%. The menu now
tops
out at 15 minutes. This is a deliberate deviation from the issue text,
agreed
with the maintainer. A value written by an earlier build fails closed to
Off
through `validWaterfallMarkerInterval()`; the settings test pins that.
- **`waterfallTimeMarkerSeconds` Q_PROPERTY is now read-only.** Nothing
needs
to set the interval by reflection, and the `dss snapshot` field already
carries it for reads.
- **The new `dss snapshot` fields are documented** in
`docs/automation-bridge.md` next to the sibling DSS fields, with the two
assertions that make markers checkable from the bridge.
- **The `<=` clock-advance guard's overlap with the bucket comparison is
now
a deliberate, commented choice**, and the zero-advance row is pinned
directly in the geometry test.
Rebased onto current `main` (the only conflict was both sides appending
to
`AETHER_SETTINGS_CONSUMERS` in `tests/tests.cmake`) and the touchpoint
manifest
regenerated for current main.
Independently verified on **Linux** (`AETHER_GPU_SPECTRUM=ON`), the
platform the
original test plan did not cover, driving the demo simulator over the
automation
bridge: markers land exactly on clock boundaries, scroll at
`1000/msPerRow` px/s (measured 20.82 vs 21.09 expected), survive resize
(410→350→230 rows), history scrub, `sim
stallscope`/`dropslice`/`malformed`, and
disconnect/reconnect, and the interval persists across a process
restart.
Mutation testing killed 5 of 6 deliberate reversions of the geometry.
## Constitution principle honored
- Principle V: preference lives in the existing nested Display settings
document, without a new flat settings key.
- Principle IV: original Qt implementation; WSJT-X is a behavioral
reference only, with no proprietary binary-derived code.
- Principle XI: demonstrated via focused tests and native Demo
automation-bridge checks.
## Test plan
- [x] Native macOS ARM64 RelWithDebInfo build with the local toolchain
and RADE enabled; ARM64 executable verified and RNNoise x86 sources
absent from the build graph.
- [x] Four focused CTests passed: `waterfall_time_markers_test`,
`waterfall_time_marker_settings_test`, `waterfall_history_buffer_test`,
and `spectrum_preview_logic_test`.
- [x] Geometry tests cover all intervals, wrap, fractional scrolling,
paused rows, resize mapping, duplicate timestamps, missing timestamps,
gaps, backward clock movement, Off, and invalid values. Settings test
checks independent slots, sibling preservation, and reload.
- [x] Mutation checks: reversing fractional-scroll direction fails the
geometry checks; reverting clock-boundary labeling fails delayed-row
assertions for every interval.
- [x] Native Cocoa/Metal automation bridge using isolated settings,
DEMO-0001, and TX disabled: menu selections, live motion, paused
history, resize, themes, Off, and restart persistence. A retained marker
stayed at row 226 when the paused waterfall grew from 410 to 470 rows.
Corrected timestamps were asserted divisible by the selected 15000 ms
interval and rendered pixels inspected.
- [x] Theme seed, touchpoint manifest, test registration,
engine-boundary strict check, and whitespace checks passed. No increase
in the hardcoded-color ratchet.
- [ ] Windows/Linux GPU runtime verification and actual Flex/Kiwi
source-switching validation have not been performed. Software renderer
was syntax-checked but not exercised as a full runtime build.
- [ ] CI results pending the PR run.
New CTests are socket-free. Bridge proof is a separate manual Demo
session. No live-radio validation is claimed; reopening the earlier
testing profile briefly auto-connected to a saved FLEX, so that instance
was closed without transmit and the final handoff used the clean Demo
profile.
## Checklist
- [x] Commit is SSH-signed and locally verified.
- [x] No new flat-key AppSettings calls.
- [x] Clean-room code.
- [x] Theme token documentation and generated inventories updated;
CHANGELOG unchanged.
- [x] Local code review completed; identified packet-arrival timestamp
labeling corrected before this PR.
Prepared with Codex assistance. Meter smoothing and GHSA references are
not applicable to this display-only feature.
---------
Co-authored-by: Jeremy [KK7GWY] <kk7gwy@aethersdr.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/architecture/aetherd-touchpoints.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,7 +142,7 @@ Burndown manifest for the engine/UI decoupling ([RFC](../aetherd-headless-engine
142
142
|`core/SystemInventory.h`| 1 | ui-support — Startup and support-bundle inventory of host CPU, SIMD and RAM capabilities. Process diagnostics, not radio state. | unconverted |
143
143
|`core/TciServer.h`| 3 | mixed(flex) — TCI WebSocket server for WSJT-X et al: protocol surface is canonical radio state, but audio/IQ rides Flex DAX | unconverted |
144
144
|`core/TgxlConnection.h`| 2 | peripheral(4o3a) — Direct TCP client for the 4O3A Tuner Genius XL (port 9010, relay/autotune), reverse-engineered from the 4O3A management app — a standalone accessory transport, not SmartSDR. Not radio-family wire; a peripheral accessory, NOT behind the IRadioBackend radio seam (reclassified from vendor(flex), #4087 follow-up). | unconverted |
145
-
|`core/ThemeManager.h`|146| ui-support — Qt token-based theming singleton (colors/fonts/QSS, theme files, editor hooks) — pure client GUI plumbing, no radio state. | unconverted |
145
+
|`core/ThemeManager.h`|147| ui-support — Qt token-based theming singleton (colors/fonts/QSS, theme files, editor hooks) — pure client GUI plumbing, no radio state. | unconverted |
146
146
|`core/ThreadCpuRing.h`| 2 | ui-support — Short host-thread CPU history used by Runtime Monitor peak and sparkline presentation. Diagnostic UI support, not radio state. | unconverted |
147
147
|`core/TimeFrameVoter.h`| 1 | universal — Shared AetherClock time-frame types plus confidence-weighted cross-frame bit voting over a sliding window. Map-agnostic pure DSP/logic — no Qt, no GUI, no vendor ties. | unconverted |
148
148
|`core/TxKeyingMarker.h`| 6 | ui-support — QWidget property marker guarding TX-keying controls from the automation bridge; GUI-shell plumbing, no radio state. | unconverted |
Copy file name to clipboardExpand all lines: docs/automation-bridge.md
+27-1Lines changed: 27 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3315,7 +3315,7 @@ Actions:
3315
3315
3316
3316
| action | value | effect |
3317
3317
|---|---|---|
3318
-
|`snapshot`| optional pan target | Read `live`, current center/bandwidth MHz, waterfall/DSS history row counts, visible DSS row count, the current front-row peak bin/min/max/span, localized plateau metrics (`dssVisibleFrontMinValueBins`, `dssVisibleFrontLongestFlatRunBins`, and visible maxima), and flat/non-flat visible-row counts. |
3318
+
|`snapshot`| optional pan target | Read `live`, current center/bandwidth MHz, waterfall/DSS history row counts, visible DSS row count, the current front-row peak bin/min/max/span, localized plateau metrics (`dssVisibleFrontMinValueBins`, `dssVisibleFrontLongestFlatRunBins`, and visible maxima), flat/non-flat visible-row counts, and the waterfall time-marker state (`waterfallTimeMarkerSeconds`, `waterfallTimeMarkers`). |
3319
3319
|`reset`|`native` or `kiwi`| Clear the selected stream's current/history rows and make that stream active for subsequent injection. |
3320
3320
|`inject`|`<count> <firstPeakBin> <stepBin> [native\|kiwi [rowLowMhz rowHighMhz]]`| Add synthetic rows with one strong peak per row. `count` is rejected if it exceeds the retained waterfall history capacity. Native injection adds one fallback-style waterfall/DSS row per input row; Kiwi injection drives `updateKiwiSdrWaterfallRow()`. Kiwi frame arguments override the source row's frequency span, so tests can cover partial-overlap rows. |
3321
3321
|`scrollback`|`<offsetRows>`| Enter waterfall history mode and rebuild the 3D surface using the same offset. |
@@ -3329,6 +3329,32 @@ injected peak becomes visible. The total row counts are still returned, but the
3329
3329
`*RowsAdded` fields are the deterministic assertion surface if live data is also
3330
3330
arriving between bridge requests.
3331
3331
3332
+
### Waterfall time markers
3333
+
3334
+
`dss snapshot` reports the clock-aligned waterfall time markers (#5537):
3335
+
3336
+
| field | meaning |
3337
+
|---|---|
3338
+
|`waterfallTimeMarkerSeconds`| Selected interval for this pan slot, in seconds. `0` means Off (the default). Only `0`, `15`, `30`, `60`, `300`, `600` and `900` are valid; anything else fails closed to `0`. |
3339
+
|`waterfallTimeMarkers`| Markers currently inside the waterfall viewport, newest first. Each entry is `{"timestampMs", "y"}`: `timestampMs` is the **clock boundary** the marker labels (always an exact multiple of the interval, never the packet arrival time), and `y` is its offset in pixels from the top of the waterfall rect. |
3340
+
3341
+
The interval is set from the panadapter context menu (**Waterfall Time
3342
+
Markers**) and persists per pan slot in the `Display` settings document. It is
3343
+
not settable over the bridge; seed `DisplaySettings` or use the menu.
3344
+
3345
+
Markers are attached to the signal row that was captured when the boundary was
3346
+
crossed, so they scroll with the waterfall rather than with wall-clock time.
3347
+
Two useful assertions:
3348
+
3349
+
- Every `timestampMs` is divisible by `waterfallTimeMarkerSeconds * 1000`.
3350
+
- A marker's `y` advances at `1000 / waterfallTimeScaleMsPerRow` pixels per
3351
+
second while live, and holds still under `dss scrollback`.
3352
+
3353
+
An empty array is normal: a screenful of waterfall is only
3354
+
`waterfallRows * waterfallTimeScaleMsPerRow` milliseconds deep (typically
3355
+
11-19 s), so intervals longer than that window have no marker on screen most
3356
+
of the time.
3357
+
3332
3358
To reproduce a low-coverage Kiwi row, read `centerMhz` and `bandwidthMhz` from
3333
3359
`dss snapshot`, then inject a Kiwi source row whose span overlaps less than 5%
3334
3360
of the current view. For example, with `viewHigh = centerMhz + bandwidthMhz/2`,
0 commit comments