aethersdr / aethersdr/AetherSDR
IC-9700: maxPanadapters advertises 2 but createPanadapter() is never implemented — "none left" when none were ever created
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 221
- Forks
- 117
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 302
Description
Preflight
- I have searched existing issues
- Running a current build — v26.9.1,
6ca3dc74, Windows 11 / MSVC 2022 / Qt 6.10.3
Summary
IcomCivBackend advertises maxPanadapters = 2 for the IC-9700, but never implements createPanadapter(). Requesting a second panadapter fails with a message naming a maximum of 2 and none available — which reads as "two are in use" when zero were allocated and only one can ever exist.
Two defects, and the second is the one that costs time:
- The capability is unbacked.
maxPanadaptersclaims 2; the code path returnsfalseunconditionally. - The failure is reported as capacity exhausted rather than unsupported, sending the operator hunting for a second panadapter that was never created.
Reproduce
- Connect to an IC-9700 over RS-BA1 LAN.
- One panadapter comes up and works normally.
- Add a second panadapter (layout change, or Add Panadapter).
- The limit message names a maximum of 2, with none available.
Expected: a second panadapter, or a message saying this radio supports one.
Actual: a message implying two are already allocated.
Root cause
IcomCivBackend does not override createPanadapter(), so it inherits the base:
// src/core/backends/IRadioBackend.h:319
virtual bool createPanadapter() { return false; }
Only Hl2Backend (Hl2Backend.h:90) and SimBackend (SimBackend.h:70) override it — grep -rn "createPanadapter" src/core/backends/ returns no Icom hit.
The capability meanwhile derives from the receiver count:
// src/core/backends/icom/IcomCivBackend.cpp:225-226
c.maxSlices = m.receivers;
c.maxPanadapters = m.hasScope ? m.receivers : 0;
and the IC-9700 profile declares two receivers (IcomModels.cpp:66 — 0xA2, "IC-9700", 2, 2).
| Step | Location | Result |
|---|---|---|
| Declare capability | IcomCivBackend.cpp:226 |
maxPanadapters = 2 |
| Request second pan | RadioModel.cpp:4805 |
createPanadapter() → always false |
| Report failure | RadioModel.cpp:4807 |
panadapterLimitReached(maxPanadapters, …) → "max 2" |
The message is emitted from the backend-declined branch but carries the declared maximum, so a refusal to implement is indistinguishable from genuine exhaustion.
The backend is structurally single-panadapter, not merely missing a function — both identities are compile-time constants with no index:
// src/core/backends/icom/IcomCivBackend.h:313-314
[[nodiscard]] int sliceId() const noexcept { return 0; }
[[nodiscard]] QString panId() const { return QStringLiteral("0"); }
Every panRangeChanged / panCenterBandwidthChanged / panPreampChanged emit passes panId(), so there is one pan identity per session. There is no sub-receiver scope path — grepping for subReceiver|receiver2|dualwatch returns nothing, and 0x27 0x00 frames decode as a single stream.
The hardware agrees. The IC-9700 has one scope. Even with createPanadapter() implemented, a second panadapter would have no stream to render — the "black panadapter" symptom IcomCivBackend.cpp:1292 already warns about in a different context.
Verified by execution
Not read from source alone. A local test selects the real IC-9700 profile through the existing IcomCivBackendTestAccess shim and asserts both facts in one process:
[ OK ] IC-9700 profile declares 2 receivers
[ OK ] IC-9700 profile declares a scope
[ OK ] capabilities() advertises maxPanadapters == 2 (derived from receivers)
[ OK ] createPanadapter() returns FALSE — the second panadapter cannot be created
[ OK ] MISMATCH CONFIRMED: capability advertises >1 pan, backend refuses to create one
[ OK ] capabilities() also advertises maxSlices == 2 (same derivation)
ALL PASS
Mutation-checked rather than trusted green: changing maxPanadapters to 1 turns assertions 3 and 5 red (2 failures, exit 1), so the test detects the condition rather than passing regardless.
The test is pushed at nigelfenton/AetherSDR:test/icom-maxpan-capacity-5347 (tests/icom_panadapter_capacity_test.cpp, +121). Happy to PR it alongside whichever fix you prefer — it is written to assert the CURRENT behaviour, so if maxPanadapters becomes 1 the third and fifth assertions are the ones that flip and the test wants updating with the fix rather than before it.
Suggested fix
Stop advertising what cannot be delivered:
// The scope is a single 0x27 0x00 stream and panId() is a fixed "0", so this
// backend drives exactly one panadapter regardless of receiver count.
c.maxPanadapters = m.hasScope ? 1 : 0;
That makes the capability true and turns the error into an accurate "this radio supports one panadapter".
⚠ maxSlices may have the same problem — sliceId() is likewise a hardcoded 0. I have not traced the slice path, so I am flagging it rather than claiming it.
Separately, RadioModel::createPanadapter()'s backend-declined branch (RadioModel.cpp:4805-4808) reports the declared maximum on a path where the backend refused outright. Even with the capability corrected, any backend declining for another reason produces a misleading capacity message. A distinct signal for unsupported versus exhausted would stop this recurring.
Scope note
This issue asks only that the capability stop claiming a feature that does not exist. It is not a request for dual-receiver panadapters — that is #4840, and if it lands, maxPanadapters = 2 becomes true and this can be reverted.
Showing the second receiver in the UI is a separate proposal (a per-model VFO flag), filed separately so this stays a one-line correction.
Relationship to existing issues
- #4840 — IC-9700 dual VFO / slices for satellite use. The feature request for real dual-receiver support.
- #5267 (Flex-only verbs reporting
okon their drop) and #5266 (Flex-only controls silently dead off-Flex), both fixed in v26.9.1: the same pattern — a capability or verb claiming support the active backend does not provide. This is that pattern in the capability table.
Not verified
Confirmed by the test above and by the app's own limit message on a live IC-9700 over RS-BA1 LAN. I have not stepped RadioModel::createPanadapter() in a debugger to observe the branch taken, and have not traced whether maxSlices misbehaves the same way.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/core/backends/icom/IcomCivBackend.cpp:225-226 and the IC-9700 profile in IcomModels.cpp:66, then inspect the panadapter identity in IcomCivBackend.h. Run tests/icom_panadapter_capacity_test.cpp from the referenced test branch. Done means the advertised panadapter capacity matches the backend's single panadapter and the capacity test reflects the corrected behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100