Repository navigation
qdmr: dual-bank zones, fleet contact dedup, tytExtension passthrough - #45
Merged
Merged
Conversation
added 5 commits
October 8, 2026 12:43
Two entries, mirroring the RT3S pattern: - baofeng_dm1701: OEM firmware, dmrconf radio key dm1701 (Retevis RT84 is the same hardware, alias key rt84). Limits read from qdmr v0.15.1 lib/dm1701_limits.cc: 3000 channels, 250 zones (64 ch/zone per A/B bank), 250 scan lists of 31 channels, 250 RX group lists of 32 TGs, 10000 contacts, single DMR ID, 16-char names, 136-174 / 400-480 MHz. - baofeng_dm1701_opengd77: OpenGD77 firmware, dmrconf key openuv380. Shares the MDUV380/DM1701/RT3S/RT84 target, so limits match retevis_rt3s_opengd77 (1024 ch, 68 zones, 76 group lists, 1024 contacts, UHF capped at 470 MHz, no scan-list entity). Verified: gen_radio_table.py --check clean, 280 passed / 1 skipped, and a generated qdmr YAML passes dmrconf verify on both dm1701 and rt84. The same YAML with a 479 MHz channel is correctly rejected on the OpenGD77 key, which confirms the deliberate 480 vs 470 UHF split between the two files. Not bench-verified: no RADIO_NAMES entry for dm1701 yet, because those strings are captured from real detect runs. Device write stays fail-closed until the radio is on USB. Site rows carry no hw flag.
Captured from a real 'dmrconf detect' run against Eric's radio, which is the standing rule for this dict -- the value is never guessed from source. Detected name is 'Baofeng DM-1701', the qdmr display name (_name in lib/dm1701.cc), not the short --list-radios label 'DM-1701'. This unblocks the device write path, which was fail-closed on the missing key. Detect talks to the TyT DFU interface (USB 0483:df11, descriptor 'Digital Radio in USB mode'), not a serial port. It prints Found: and exits 0, then emits three benign teardown errors from lib/dfu_libusb.cc. Reproduced twice; comment added so nobody reads those as a detect failure. No RT84 entry: same hardware, but its detected string is unverified here. 280 passed, 1 skipped.
Full round trip done on Eric's radio (eam_dm1701_01) 2026-10-08: detect, binary read, profile write, read back, decode, diff. Read-back matches the written codeplug exactly -- 219 channels, 22 zones, 15 contacts, 5 group lists, channel-name lists identical -- and only 22610 of 844093 bytes differ from the factory image, so the write was targeted rather than a wholesale reinit. Confirms on DM-1701 the DM-32 behaviour already in AGENTS.md: the write progress bar lies. It sat at [ ] 0% and the wrapper never printed an exit code, yet the write had fully committed. Judge by read-back diff only. baofeng_dm1701_opengd77 deliberately NOT badged -- that firmware has never been flashed on this radio.
qdmr models a zone as two channel banks, A and B, each bound to one VFO. The exporter hardcoded "B": [], which is right for OpenGD77 but wrong for TyT OEM firmware: that firmware renders the second display line from bank B and prints "Unprogram." when it is empty. Hardware-confirmed on a Baofeng DM-1701 2026-10-08. All 22 zones of a 219-channel codeplug wrote and read back correctly while every channel showed "Unprogram." on line 2, purely because codeplugger models a zone as one list and only filled A. Mirroring the list into both banks clears it and gives the second VFO the same channels. Driven by a new zone_banks capability rather than applied blanket, so single-bank radios (every OpenGD77 target) keep B empty and are byte-identical to before. Declared in the 0.2 schema, which is additionalProperties:false at both levels, and set to 2 only for baofeng_dm1701. Also allow tytExtension as a qdmr root extension. The OEM profile has no settings block (extensions.qdmr.settings models OpenGD77 fields this firmware lacks), so side buttons could only come out as codeplugger defaults, i.e. all Disabled. Profiles can now carry the real mapping instead of it being hardcoded or lost.
Multiple radio instances owned by the same operator share the same dmr_id and dmr_contact_name. The fleet loop emitted one identical PrivateCall entry per instance, triplicating KC9MHE in a three-radio registry. Deduplicate by (name, number) before appending.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two fixes for the Baofeng DM-1701 / Retevis RT84 qdmr export path, both
hardware-verified on a Baofeng DM-1701 running OEM firmware (2026-10-08).
1 — Dual-bank zone export (
zone_bankscapability)Root cause. qdmr models a zone as two channel banks, A and B, each
bound to one VFO. The exporter hardcoded
"B": []. On OpenGD77 targetsthat is correct. On TyT OEM firmware it is not: the firmware renders the
second display line from bank B and shows "Unprogram." when it is
empty. This appeared on every channel of a 219-channel codeplug across
all 22 zones.
Fix. A new
zone_banksfield in the capabilities schema (top level,additionalProperties: falseat both levels so it had to be declared).Default is
1; set to2forbaofeng_dm1701. Whenzone_banks >= 2the exporter mirrors bank A into bank B, giving both VFOs the same
channels. Every OpenGD77 target remains byte-identical to before.
Verification. Written and read back: 219 ch · 22 zones · no empty B.
2 — Deduplicate fleet contacts across radio instances
Root cause. The fleet contact loop emitted one
PrivateCallentryper radio instance, so an operator with three radios registered to the
same DMR ID got that contact triplicated in the output (e.g.
KC9MHE3207125 appearing three times).
Fix. Deduplicate by
(name, number)before appending.3 —
tytExtensionroot extension passthroughAdds
tytExtensiontoQDMR_ROOT_EXTENSION_KEYSso profiles can carryTyT OEM-specific settings (button mappings, menu settings) through the
exporter. Without this the exporter had no way to set side buttons on OEM
firmware radios; they always came out Disabled with
longPressDuration 0.Files changed
src/codeplugger/exporters/qdmr_yaml.pyzone_banksparam, mirror logic, fleet dedup,tytExtensionpassthroughsrc/codeplugger/profile.pyzone_banksfrom capabilities to exporterschemas/capabilities-0.2.schema.jsonzone_banks(integer, 1–2)radios/baofeng_dm1701/capabilities.json"zone_banks": 2tests/test_qdmr_yaml.pytest_zone_banks_mirrors_channels_into_bank_bTest results