Skip to content

refactor(band-plan): share contiguous-regions walker for ATU + SWR sweep#3018

Merged
ten9876 merged 1 commit into
mainfrom
auto/fix-2822
May 23, 2026
Merged

refactor(band-plan): share contiguous-regions walker for ATU + SWR sweep#3018
ten9876 merged 1 commit into
mainfrom
auto/fix-2822

Conversation

@ten9876

@ten9876 ten9876 commented May 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #2822. Extracts the segment-collect + sort-and-merge contiguous-region computation from AtuPreTuneDialog's private computeCentersForBand() into a public BandPlanManager::contiguousRegionsForBand(low, high) so the same discrete-channel awareness is available to other band-plan-aware features.

What the helper does

For a [searchLow, searchHigh] window, returns the active plan's matching segments merged into contiguous [low, high] regions (1 Hz adjacency tolerance), sorted by ascending low edge.

  • Continuous bands (40 m, 20 m, etc.) → one region spanning the band's regulatory edges.
  • Discrete-channel bands (US 60 m: 5 USB channels at 2.8 kHz each separated by tens of kHz of gap) → one region per legal channel.

Call sites

  • AtuPreTuneDialog::recomputeBands now delegates region math to the helper. Per-region center walk + narrow-region midpoint fallback (US 60 m's 2.8 kHz channel vs 9 kHz segment) preserved verbatim.
  • MainWindow::startSwrSweep replaces its ad-hoc min(lowMhz) / max(highMhz) walk with contiguousRegionsForBand() + first.lowMhz / last.highMhz. Envelope is identical because regions are sorted by low edge after merge — the first region's low IS the global min, the last region's high IS the global max.

What this does NOT change

The 60 m channelized hard-block in startSwrSweep stays intact. The issue's stretch goal (walk each region individually for true discrete-channel SWR sweeping) needs on-air safety review and was explicitly out of scope for this mechanical refactor.

Stats

  • 4 files, +79 / -52
  • Drops a private 50-line helper, adds a 20-line public method.
  • 18/18 tests pass (no test changes — the math hasn't changed, only its location).

Test plan

  • Clean build, 0 warnings
  • atu_pretune_centers_test still green
  • Manual: ATU Pre-Tune sweep on US 60 m — still emits 5 tune centers, one per channel
  • Manual: ATU Pre-Tune sweep on IARU R1 40 m — still narrows to 7.000–7.200 MHz envelope
  • Manual: SWR sweep on IARU R1 40 m — still narrows to plan's edges, completes successfully
  • Manual: SWR sweep on US 60 m — still blocked with the channelized-band warning

🤖 Generated with Claude Code

Extract the segment-collect + sort-and-merge logic from
AtuPreTuneDialog's private computeCentersForBand() into a public
BandPlanManager::contiguousRegionsForBand(low, high) so the same
discrete-channel awareness can serve MainWindow::startSwrSweep.

The new helper returns the band's segments merged into contiguous
[low, high] regions (1 Hz adjacency tolerance), sorted by low edge.
Continuous bands collapse to one region; discrete-channel bands like
US 60 m return one region per legal channel.

Call sites:
- AtuPreTuneDialog::recomputeBands: now takes BandPlanManager& and
  delegates the region math to the helper, preserving the per-region
  center walk and narrow-region midpoint fallback for sub-segment
  channels (US 60 m: 2.8 kHz channel vs 9 kHz segment).
- MainWindow::startSwrSweep: replaces the ad-hoc min/max walk with
  contiguousRegionsForBand() + first.low/last.high. Behavior identical
  (sorted-by-low regions give the same envelope as min/max across
  segments). The 60 m channelized hard-block stays intact — a future
  enhancement can walk each region individually, but that needs the
  on-air safety review the issue called out.

No behavior change on either call site. 18/18 tests pass.

Closes #2822.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 requested a review from jensenpat as a code owner May 23, 2026 19:29
@ten9876 ten9876 enabled auto-merge (squash) May 23, 2026 19:29

@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 @ten9876 — clean mechanical refactor. Behavior preservation reads correct:

  • AtuPreTuneDialog::computeCentersForBand keeps the narrow-region midpoint fallback verbatim and just delegates the collect+sort+merge to the new helper. Null check on m_bandPlan still happens at line 347 before dereference, so *m_bandPlan is safe.
  • MainWindow::startSwrSweep's regions.first().lowMhz / regions.last().highMhz is equivalent to the old min/max walk because the helper's docstring guarantees ascending-by-low-edge order after the merge. The 60 m hard-block above still short-circuits before this code runs, so the discrete-channel case stays out of scope.
  • The 1 Hz adjacency epsilon, midpoint-in-window matching, and segment ordering are preserved verbatim across the move.

Two small suggestions, both optional:

  1. Unit test for BandPlanManager::contiguousRegionsForBand. This is now public API with two callers and no direct coverage (no tests/band_plan* exists today). A handful of cases — single continuous segment, two overlapping segments, two adjacent-within-1Hz segments, US 60 m discrete channels, empty window — would lock down the merge invariant and make future region-aware features (the SWR sweep stretch goal you mention) safer to build on.
  2. Doc comment nit on the Region struct (BandPlanManager.h:30-37). "Discrete-channel bands ... collapse to one Region per legal channel" is true only because the band-plan JSON encodes each US 60 m channel as a separate segment with regulatory gaps between them — i.e. it's a property of the input data, not the merge algorithm. Maybe reword as "non-adjacent segments produce one Region each (e.g. US 60 m's 5 channels)" so the contract is about adjacency, not band semantics.

Also noticed BandPlanManager.h comment now correctly says "5 channels" for US 60 m where the old AtuPreTuneDialog comment said "6" — nice incidental fix.

Not blocking on either.


🤖 aethersdr-agent · cost: $5.6483 · model: claude-opus-4-7

@ten9876 ten9876 merged commit e97b675 into main May 23, 2026
5 checks passed
@ten9876 ten9876 deleted the auto/fix-2822 branch May 23, 2026 21:07
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…eep (aethersdr#3018)

## Summary

Closes aethersdr#2822. Extracts the segment-collect + sort-and-merge
contiguous-region computation from `AtuPreTuneDialog`'s private
`computeCentersForBand()` into a public
`BandPlanManager::contiguousRegionsForBand(low, high)` so the same
discrete-channel awareness is available to other band-plan-aware
features.

## What the helper does

For a `[searchLow, searchHigh]` window, returns the active plan's
matching segments merged into contiguous `[low, high]` regions (1 Hz
adjacency tolerance), sorted by ascending low edge.

- **Continuous bands** (40 m, 20 m, etc.) → one region spanning the
band's regulatory edges.
- **Discrete-channel bands** (US 60 m: 5 USB channels at 2.8 kHz each
separated by tens of kHz of gap) → one region per legal channel.

## Call sites

- `AtuPreTuneDialog::recomputeBands` now delegates region math to the
helper. Per-region center walk + narrow-region midpoint fallback (US 60
m's 2.8 kHz channel vs 9 kHz segment) preserved verbatim.
- `MainWindow::startSwrSweep` replaces its ad-hoc `min(lowMhz)` /
`max(highMhz)` walk with `contiguousRegionsForBand() + first.lowMhz /
last.highMhz`. Envelope is identical because regions are sorted by low
edge after merge — the first region's low IS the global min, the last
region's high IS the global max.

## What this does NOT change

The 60 m channelized hard-block in `startSwrSweep` stays intact. The
issue's stretch goal (walk each region individually for true
discrete-channel SWR sweeping) needs on-air safety review and was
explicitly out of scope for this mechanical refactor.

## Stats

- 4 files, **+79 / -52**
- Drops a private 50-line helper, adds a 20-line public method.
- 18/18 tests pass (no test changes — the math hasn't changed, only its
location).

## Test plan

- [x] Clean build, 0 warnings
- [x] `atu_pretune_centers_test` still green
- [ ] Manual: ATU Pre-Tune sweep on US 60 m — still emits 5 tune
centers, one per channel
- [ ] Manual: ATU Pre-Tune sweep on IARU R1 40 m — still narrows to
7.000–7.200 MHz envelope
- [ ] Manual: SWR sweep on IARU R1 40 m — still narrows to plan's edges,
completes successfully
- [ ] Manual: SWR sweep on US 60 m — still blocked with the
channelized-band warning

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

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.

Share computeContiguousRegions walker between ATU pre-tune and SWR sweep (discrete-channel band handling)

1 participant