Skip to content

Support radios with sub-devices (dual-band and zoned models load with an empty channel list) #30

Description

@jasiek

Summary

Radios that expose get_sub_devices() — dual-band mobiles and zoned commercial sets — are unsupported end to end. Their images load without error and present an empty channel list, because their channels live in the sub-devices rather than on the parent radio object.

17 of the 357 upstream test images are affected; 15 of those contain real channel data that CHIRP displays and webchirp does not.

Image Sub-devices Channels Parent get_memory() errors
Yaesu_FT-8800 Left [1-512], Right [1-512] 70 512
Retevis_RA87 Left [0-99], Right [0-99] 68 100
Kenwood_TK-3180K2 Zone FRS/GMRS, Zone Channels, Zone GMRS Wide 65 512
Baojie_BJ-9900 Left [1-128], Right [1-128] 50 128
Yaesu_FTM-350 Left [0-500], Right [0-500] 34 501
Kenwood_TK-880 1 [1-250], 2 [1-250] 31 250
LUITON_LT-725UV Upper [1-128], Lower [1-128] 8 128
Yaesu_VX-1 CG1 [1-52], CG2 [1-142], BC [1-10] 5 260
Kenwood_TK-481 1, 2 3 250
Kenwood_TK-981 1, System 2, System 3 3 250
Kenwood_TK-7160K Zone 1 [1-128] 3 128
Yaesu_FT-8100 VHF [1-99], UHF [1-99] 3 99
Kenwood_TK-3140K Group 1, Group 2 2 250
Kenwood_TK-280 1 [1-250] 1 250
Kenwood_TK-8180 Zone 1 [1-250] 1 512
BTECH_UV-50X3 Left [0-499], Right [0-499] 0 (image is blank) 500
Jetstream_JT270MH A Band [1-99], B Band [1-99] 0 (image is blank) 0

Root cause

_radio_rows_from_instance (web/python/runtime_bridge.py:572) iterates only the parent radio's memory_bounds and calls radio.get_memory(n) on the parent. For these drivers the parent holds no memories, so every call raises — NotImplementedError, or bitwise-struct errors like AttributeError: No attribute system-1 in struct (anonymous).

The failure is invisible because the loop swallows it:

try:
    mem = radio.get_memory(number)
except Exception:
    continue

512 consecutive exceptions become "no channels", with nothing logged. That also runs against the "preserve debug visibility" rule in CLAUDE.md — full errors should reach the debug panel.

The write path has the same gap: _apply_rows_to_radio_instance (web/python/runtime_bridge.py:591) also walks only the parent, so these radios cannot be written either. get_radio_column_metadata likewise derives editability and bounds from the parent's features.

For reference, upstream handles this by indexing sub-devices explicitly — see SUB_DEVICE in chirp/tests/base.py and the per-sub-device test generation in chirp/tests/__init__.py.

What full support needs

  1. Read — walk get_sub_devices() when rf.has_sub_devices, tagging each row with the sub-device it came from.
  2. Write — route rows back to the right sub-device in _apply_rows_to_radio_instance.
  3. Column metadata / settings — resolve features per sub-device rather than from the parent.
  4. UI — sub-devices need to be selectable or otherwise distinguished. Note their memory numbering overlaps: FT-8800 exposes Left [1-512] and Right [1-512], so Location alone is no longer a unique row key. CHIRP's desktop UI presents each sub-device as its own tab, which is probably the model to follow.
  5. Stop swallowing per-memory exceptions — surface them in the debug panel rather than returning an empty list, so a regression like this is visible rather than silent.

Open questions

  • One tab per sub-device, or a single table with a sub-device column? The overlapping Location ranges push toward tabs.
  • Does the export/import .img round trip need to preserve sub-device identity, or is reconstructing it from the driver enough?

Verification

chirp/tests/images/ gives direct coverage: each affected image should load with the channel count in the table above, and a load → export round trip should be byte-identical to the source where the driver allows.

Found while measuring image-load coverage for #28.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions