aethersdr / aethersdr/AetherSDR

IC-9700: maxPanadapters advertises 2 but createPanadapter() is never implemented — "none left" when none were ever created

Open Beginner friendly
#5,347 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug maintainer-review multi-pan protocol
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:

  1. The capability is unbacked. maxPanadapters claims 2; the code path returns false unconditionally.
  2. The failure is reported as capacity exhausted rather than unsupported, sending the operator hunting for a second panadapter that was never created.

Reproduce

  1. Connect to an IC-9700 over RS-BA1 LAN.
  2. One panadapter comes up and works normally.
  3. Add a second panadapter (layout change, or Add Panadapter).
  4. 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:660xA2, "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 problemsliceId() 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 ok on 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.