Skip to content

feat(radio): accept conventional band-name aliases (70cm -> 440) in declared bands#4259

Draft
nigelfenton wants to merge 2 commits into
aethersdr:mainfrom
nigelfenton:feat/band-name-aliases
Draft

feat(radio): accept conventional band-name aliases (70cm -> 440) in declared bands#4259
nigelfenton wants to merge 2 commits into
aethersdr:mainfrom
nigelfenton:feat/band-name-aliases

Conversation

@nigelfenton

Copy link
Copy Markdown
Contributor

Summary

AE's band vocabulary is internally inconsistent — the 70 cm band is named 440, not 70cm. But a gateway (or a ham typing a radio profile) naturally spells it 70cm, and parseDeclaredBands() matches names exactly against kBands, so the conventional spelling is silently dropped and the band never appears.

That mismatch has so far been patched adapter-by-adapter on the Aether-gate side — one rename per radio (its PRs #14 _70CM440, #15, #16 6cm5cm). This fixes it once, in AE, so no adapter has to know AE's naming quirks: teach parseDeclaredBands a small alias table that resolves a conventional spelling to the canonical kBands name before matching.

Follows #4027 (radio-declared bands=) and its review follow-ups in #4194. Standalone off main (not stacked).

Change

  • A constexpr alias table + resolveAlias() helper in DeclaredBands.cpp. Each incoming token is alias-resolved (case-insensitive) before the existing kBands allow-list loop.
  • Kept deliberately minimal — one entry, 70cm → 440, the only mismatch actually observed from a gateway. Easy to extend later with concrete evidence (e.g. 6cm → 5cm), but no speculative aliases.

Principle VII is unchanged (the bit worth checking)

An alias may only map to a name that already exists in kBands. The resolved name still runs through isDeclarable() + the kBands allow-list, so:

This is enforced by construction: resolveAlias() returns a plain string that is then validated exactly as before. A non-alias token passes through unchanged.

Tests

declared_bands_test gains 6 cases (all green — 17/17):

  • 70cm → [440], case-fold 70CM → [440]
  • alias among reals 2m,70cm,23cm → [2m,440,23cm]
  • dedup when both spellings arrive: 440,70cm → [440] and 70cm,440 → [440]
  • Principle VII: 70cm,junk → [440] — alias kept, junk dropped

Built + ran locally on Windows (Qt 6.10.3, current main = b9735c79).

AE's band vocabulary is internally inconsistent — the 70cm band is named
"440" — but a gateway (or a ham typing a radio profile) naturally spells
it "70cm", and parseDeclaredBands() would silently drop the unrecognized
spelling. That mismatch has been patched adapter-by-adapter on the
Aether-gate side (PRs aethersdr#14/aethersdr#15/aethersdr#16, each renaming one radio's band); this
fixes it once, in AE, so no adapter has to know AE's naming quirks.

Add a small alias table resolving conventional spellings to the canonical
kBands name before matching (currently just 70cm -> 440, the only mismatch
actually observed from a gateway). An alias only ever maps to a name that
already exists in kBands, and the resolved name still runs the existing
isDeclarable + allow-list, so Principle VII is unchanged: an alias is a
second spelling of a known band, never a new band, and junk still drops.

Tests (declared_bands_test): 70cm -> [440], case-fold, alias among reals,
dedup when both spellings are sent (either order), and the Principle-VII
case (70cm,junk -> [440] — alias kept, junk dropped). 17/17 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nigelfenton
nigelfenton requested review from a team as code owners July 15, 2026 18:54

@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.

Clean, tightly-scoped fix. I verified against BandDefs.h: 440 is the canonical kBands entry (there is no 70cm entry), and it's declarable (highMhz 450 ≥ 1.0), so the alias resolves and still passes the isDeclarable + allow-list gate exactly as the description claims. The dedup path is correct in both orders (440,70cm and 70cm,440 both collapse to [440]), and the Principle VII guarantee is genuinely preserved — an alias is only ever a second spelling of an existing band. The comments and test coverage are excellent. Nice work, and thanks for fixing this once in AE rather than per-adapter.

No blocking issues.

Non-blocking notes

  • kBandAliases: the "alias canonical must already exist in kBands" invariant could be enforced at compile time instead of by review — see inline. Optional but it makes Principle VII hold by construction.
  • Minor doc nit (no change requested): the .cpp comment and tests cite Aether-gate PRs #14/#15 while the PR description also mentions #16 for 6cm → 5cm; harmless, just noting for future-you when that alias lands.

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

Comment on lines +39 to +41
constexpr BandAlias kBandAliases[] = {
{"70cm", "440"}, // UHF: every ham + gateway spells it 70cm; AE names it 440
};

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.

Optional hardening: since kBands and kBandAliases are both constexpr, the security invariant in the comment above ("an alias may ONLY map to a name that already exists in kBands") can be checked by the compiler rather than trusted to reviewers. Something like:

constexpr bool aliasCanonicalsExist() {
    for (const auto& a : kBandAliases) {
        bool found = false;
        for (const auto& def : kBands)
            if (std::string_view(def.name) == a.canonical) { found = true; break; }
        if (!found) return false;
    }
    return true;
}
static_assert(aliasCanonicalsExist(),
              "every kBandAliases canonical must name an existing kBands entry");

That way a future typo'd or renamed canonical fails the build instead of silently dropping the band. Non-blocking — the current one-entry table is easy to eyeball.

Address the reviewer's optional-hardening suggestion: a constexpr check +
static_assert that every kBandAliases canonical names an existing kBands
entry, so Principle VII holds by construction rather than by review. A
future typo'd or renamed canonical now fails the build instead of silently
dropping the band. Also note aethersdr#16 alongside aethersdr#14/aethersdr#15 in the table comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nigelfenton

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! Implemented the compile-time hardening (84a03908): a constexpr aliasCanonicalsExist() + static_assert now enforces that every kBandAliases canonical names a real kBands entry, so Principle VII holds by construction — a future typo'd or renamed canonical fails the build instead of silently dropping the band. Also updated the table comment to note #16 alongside #14/#15. Build + all 17 declared_bands tests green.

nigelfenton added a commit to nigelfenton/AetherSDR that referenced this pull request Jul 16, 2026
Accept conventional band spellings (70cm->440) with compile-time invariant.
Favorably reviewed by aethersdr-agent, all 7 CI green. Upstream PR aethersdr#4259
still open for maintainer merge.
@jensenpat
jensenpat marked this pull request as draft July 17, 2026 16:23
@jensenpat

Copy link
Copy Markdown
Collaborator

Holding for next week's release.

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