Skip to content

feat(swr): optional manual sweep range (#2241)#3885

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
M7HNF-Ian:feat/swr-sweep-custom-range
Jun 30, 2026
Merged

feat(swr): optional manual sweep range (#2241)#3885
ten9876 merged 1 commit into
aethersdr:mainfrom
M7HNF-Ian:feat/swr-sweep-custom-range

Conversation

@M7HNF-Ian

Copy link
Copy Markdown
Contributor

Adds the user-bounded sweep range from #2241 (rnash2's request to "manually set the start and end sweep frequency to respect his operating privileges").

A Limit range checkbox with From/To fields in the SWR sweep controls lets an operator confine a sweep to a licence sub-band or a slice of interest instead of always sweeping the whole band.

  • Off by default — full-band sweep behaviour is unchanged.
  • Ticking the box enables the fields and seeds them with the current band's edges (via BandSettings) as a starting point to narrow from.
  • The bounds pass through swrSweepStartRequested() to startSwrSweep(), where they are intersected with the existing in-region safe range — so they can only ever narrow the sweep, never widen it past what the regional band plan already permits (Sweep doesn't respected band limits #2800). The out-of-region transmit guard is preserved.
  • A range that doesn't overlap the band by at least one step is rejected with a warning.

Tested live on a FLEX-6600: ticked Limit range (auto-seeded 14.000–14.350 on 20 m), narrowed To to 14.150, and confirmed the sweep trace covered only 14.000–14.150 instead of the full band.

Note: the resonance/bandwidth-marker portion of #2241 already landed in #2320, so this completes the remaining actionable scope.

@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 this, @M7HNF-Ian — nicely scoped and the safety reasoning is sound. The clamp happens receiver-side in startSwrSweep where the custom bounds are intersected with the already-computed safeLow/safeHigh (which themselves fold in the regional band-plan narrowing from #2800), so a manual range can only ever narrow the sweep and the out-of-region transmit guard is preserved. The signal/slot wiring lines up (the function-pointer connect in MainWindow_Wiring.cpp resolves cleanly to the new 4-arg startSwrSweep), the spin boxes and checkbox are parented into the layout so there's no leak, and the null guards (if (!m_slice), m_slice ? … : -1) are all in place. No AppSettings/QSettings concern since nothing here persists.

One substantive thing worth tightening:

Inverted range silently falls back to a full-band sweep. The guard is:

if (customLowMhz > 0.0 && customHighMhz > customLowMhz) {
    ...
}

If the operator enters From above To (e.g. From 14.200, To 14.100), the condition is false and the code drops through to the full-band safeLow/safeHigh range with no warning. For the exact use case this feature targets — rnash2 wanting to confine the sweep to respect operating privileges — that's the wrong-direction surprise: they think they've constrained the sweep but actually get a full-band transmit. The non-overlapping case already warns; an inverted entry should too rather than silently widening. Suggest splitting the validation so a positive-but-inverted range is rejected with the same kind of QMessageBox::warning instead of falling through.

Minor (optional): the From/To fields are seeded once on first tick (value() <= 0.0), so if the user changes band afterward the seeded edges go stale. Not a correctness issue — the receiver-side clamp still keeps it safe — but re-seeding on band change (or whenever the range is empty) would match the "sensible in-band starting point" intent better.

Nothing here blocks; the safety-critical path is correct. Nice work, and thanks for the clear write-up and the live FLEX-6600 test notes.


🤖 aethersdr-agent · cost: $3.7778 · model: claude-opus-4-8

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Ian — nicely scoped, and the safety reasoning checks out.

TX-safety (Principle VI) — clean. The SWR sweep keys the radio, so this is a transmit path, but the custom bounds are clamped inside the already-computed safe range:

sweepLow  = std::max(safeLow,  customLowMhz);
sweepHigh = std::min(safeHigh, customHighMhz);

safeLow/safeHigh already fold in the #2800 regional band-plan narrowing and the out-of-region transmit guard (both untouched), so a manual range can only ever narrow the sweep — never widen it past what the band plan permits. The sub-step-overlap rejection also prevents a zero-width keying. The invariant holds.

🔴 Needs a rebase — merge conflict with #3882. The SWR CSV export (#3882) landed in this same window and added a Save CSV button + swrSweepSaveCsvRequested() signal + m_swrSaveBtn in the exact regions this PR edits. There are real content conflicts in SpectrumOverlayMenu.cpp and SpectrumOverlayMenu.h (the other two files auto-merge clean). It's purely mechanical "keep both" — the CSV button row and the Limit-range row are logically independent, so the resolution keeps the new 4-arg swrSweepStartRequested and the swrSweepSaveCsvRequested signal, plus all members. Please rebase on main and let CI re-run (the current green is from before #3882 merged).

🟡 Worth folding into the same rebase: inverted range silently sweeps full-band. With From > To (both > 0), the guard customHighMhz > customLowMhz is false, so it drops through to the full safeLow/safeHigh. It's not a safety violation — the sweep is still clamped within the safe band — but it's a wrong-direction surprise for the exact use case this targets: an operator confining a sweep to their privileges thinks they've narrowed it but actually gets a full-band sweep. Suggest rejecting a positive-but-inverted range with the same QMessageBox::warning you already use for the non-overlap case.

⚪ Minor (optional): the From/To fields seed once on first tick (value() <= 0.0), so they go stale if the operator changes band afterward. The receiver-side clamp keeps it safe regardless; re-seeding on band change (or whenever the range is empty) would just match the "sensible in-band starting point" intent better.

Clean work and thanks for the live FLEX-6600 test notes. Once it's rebased past #3882 — ideally with the inverted-range warning added — this is a merge.

Add a "Limit range" checkbox and From/To frequency fields to the SWR sweep
controls so operators can confine a sweep to a licence sub-band or a slice
of interest instead of always sweeping the whole band.

Off by default — the full-band sweep behaviour is unchanged. Ticking the box
enables the fields and seeds them with the current band's edges (via
BandSettings). The seed is refreshed whenever the fields are empty or no
longer lie within the active band, so changing band and re-opening the range
always starts from a sensible in-band point while a range narrowed within the
current band is preserved.

The chosen bounds are passed through swrSweepStartRequested() to
startSwrSweep(), where they are intersected with the existing in-region safe
range. They can therefore only ever narrow the sweep — never widen it past
what the regional band plan already permits (aethersdr#2800) — preserving the
out-of-region transmit guard. A positive-but-inverted range (From > To) is
rejected up front with a warning rather than silently falling through to a
full-band sweep, and a range that does not overlap the band by at least one
step is likewise rejected.

Part of aethersdr#2241 (the resonance/bandwidth display landed earlier in aethersdr#2320).
@M7HNF-Ian M7HNF-Ian force-pushed the feat/swr-sweep-custom-range branch from 1ff059b to 74fac62 Compare June 29, 2026 09:51
@M7HNF-Ian

Copy link
Copy Markdown
Contributor Author

Thanks Jeremy — all three addressed in the rebased branch:

  • 🔴 Rebased past feat(swr): export SWR sweep data to CSV (#2241) #3882. Resolved the SpectrumOverlayMenu.cpp/.h conflicts as you predicted — purely "keep both": the Save CSV row and the Limit-range row are independent, so the resolution keeps the 4-arg swrSweepStartRequested, the swrSweepSaveCsvRequested signal, and all members. CI should re-run green against current main.
  • 🟡 Inverted range now rejected. A positive-but-inverted range (From > To) hits a QMessageBox::warning ("Enter a sweep start frequency below the stop frequency.") and returns, instead of dropping through to a full-band sweep. Verified live on the FLEX-6600 — the warning fires with no key-up. Switched the guard from customLowMhz > 0 && customHighMhz > customLowMhz to entering the block on any positive bound, then validating.
  • ⚪ Stale seeding fixed too. The From/To fields now re-seed when they're empty or no longer within the active band (not just on first tick), so changing band and re-opening the range starts from in-band edges, while a range narrowed within the current band is preserved.

Builds clean; the TX-safety invariant is untouched (bounds still intersect the in-region safe range).

@ten9876 ten9876 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified against the current head (74fac623) — all three prior points resolved and CI green.

TX-safety (Principle VI) holds: the custom bounds are clamped inside the already-computed safe range (std::max(safeLow, customLow) / std::min(safeHigh, customHigh)), which folds in the #2800 regional band-plan narrowing, the edge guard, and the out-of-region TX guard — so a manual range can only ever narrow, never widen. Sub-step overlap check prevents zero-width keying.

  • 🔴 Rebase past #3882 — clean 'keep both' (4-arg swrSweepStartRequested + swrSweepSaveCsvRequested + Save CSV button all present and wired).
  • 🟡 Inverted range — now rejected with a warning before any keying instead of falling through to full-band.
  • ⚪ Stale seeding — re-seeds when empty or out-of-band, preserves a within-band narrowing.

Clean, well-scoped, and nicely tested on a FLEX-6600. Thanks @M7HNF-Ian — merging.

@ten9876 ten9876 merged commit 3d7ff9b into aethersdr:main Jun 30, 2026
6 checks passed
ten9876 added a commit that referenced this pull request Jul 2, 2026
#3961)

## Release documentation prep for **v26.7.1** (2026-07-02)

29 commits since v26.6.5. Docs-only + version bump — no code behavior
change.

### Version bump 26.6.5 → 26.7.1
The three canonical locations (per `AGENTS.md`):
- `CMakeLists.txt:2` — `project(AetherSDR VERSION 26.7.1)` →
`AETHERSDR_VERSION` (the app's version string)
- `README.md` — Current version line
- `AGENTS.md` — Current version line

### `CHANGELOG.md`
Promoted `[Unreleased]` → **`## [v26.7.1] — 2026-07-02`** in house style
(v-prefix, em-dash, "N commits since…" opener + headline), authored from
all merged PRs and categorized:
- **Added:** 3D stacked-trace spectrum (#3899), 3D dBm scale (#3937),
in-process NVIDIA BNR + AFX download-on-demand (#3902), TX meter
mouse-over readouts (#3936), SWR manual sweep range (#3885), CW sidetone
capture (#3895), KiwiSDR metadata scaffolding (#3898), bridge verbs
(#3920)
- **Changed:** BNR container/NIM backend removed, 60 fps + per-pixel GPU
FFT trace (#3958), FlexLib-sourced model capabilities (#3954/#2177), RX
speaker-latency cut (#3897), Display pane sections (#3935)
- **Fixed:** #3922, #3926, #3941, #3940, #3942, #3921, #3903, #3892,
#3924, #3891, #3890, #3900, #3947
- Fresh empty `[Unreleased]` restored above the release block.
Internal/CI/docs/self-fix PRs (#3931/#3916/#3934/#3929) omitted per
house style.

### `ROADMAP.md`
- Cycle header → "post-v26.7.1"
- NVIDIA BNR removed from **In flight** (shipped this cycle)
- Five v26.7.1 highlights prepended to **Recently shipped**

### `README.md`
- Highlights: GPU spectrum bullet now notes the **per-pixel FFT trace at
up to 60 fps** and the **3D stacked-trace** spectrum mode

### Reviewer notes
- Touches protected paths (`*.md` + `AGENTS.md` Tier-1 +
`CMakeLists.txt`) — needs `@aethersdr/infrastructure` sign-off.
- Tagging `v26.7.1` and cutting the release build are **not** part of
this PR (docs prep only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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