Problem
On many digital radios (Baofeng DM32, typical commercial DMR handsets) there is no true promiscuous RX and no independent repeater + talk group selection on the radio face. Each digital channel row maps to exactly one talk group; the operator picks a channel, not a site then a TG.
Amateur operators work around this in the field with extra channels beyond the expanded per-TG rows:
- Per-TG channels — already handled by multi-talkgroup expansion (#36,
channelExpansion/, DM32 expandRxGroupLists: true).
- Scratch channel — e.g.
GB7GL Scratch: same repeater/site RF profile as the site's other channels, but programmed with a throwaway TX contact the operator can change via radio menus for ad-hoc talk groups without corrupting the main channel layout.
Today the app can expand an RX group list into flat per-TG rows on export, but it does not offer to emit the companion scratch channel operators routinely add by hand.
Not applicable to formats with native promiscuous RX and independent TG selection (OpenGD77 lean export with TG List + TG_Lists.csv — see opengd77/multi-talkgroup.md).
Intended outcome
Add an optional per–RX-group-list flag that, on export to formats requiring channel×talkgroup expansion, emits one additional scratch channel per flagged RGL.
Internal model
Extend RxGroupList (vendor-neutral):
interface RxGroupList {
// …existing fields…
/** When true, export may emit a companion scratch channel for expansion-only targets. */
includeScratchChannel?: boolean;
}
- Default
false / absent — no behaviour change.
- CRUD on RX group list edit: checkbox Include scratch channel on export (or similar), with short help text explaining DM32/commercial-radio field workflow.
- Hide or disable the control when the active export target does not use channel-TG expansion (OpenGD77, etc.) — export-profile capability, not a radio constant in mutations/validation.
Export semantics (when includeScratchChannel and target expands RGLs)
For each logical digital channel referencing a flagged RGL, emit one scratch row in addition to expanded per-member rows (dedupe if multiple logical channels share the same RGL + RF context — one scratch per RGL per distinct site/RF profile, not per logical channel; finalise grouping rule in design).
Scratch row sketch:
| Aspect |
Proposed rule |
| Wire name |
Deterministic pattern, e.g. {callsign} Scratch (respect name shortening / maxNameLength at boundary) |
| RF profile |
Same RX/TX, colour code, timeslot, location as the lean logical channel |
| TX contact |
Operator-chosen default on RGL or documented sentinel (e.g. first RGL member, or empty/private placeholder) — open design |
| RX group list |
null on wire (single-TG row semantics) |
| Zone membership |
Open design — auto-add scratch to zones that reference expanded channels from this RGL, or leave to operator |
| Scan list |
Out of scope here — see #125 (scan list carrier channels) |
Implementation lives in src/lib/channelExpansion/ (or sibling export helper), invoked from DM32 and future expansion consumers — not in CRUD validation as a radio cap.
Import (best-effort, not a blocker)
Detect scratch channels on import (name suffix / pattern + RF match to an RGL cluster) and set includeScratchChannel on the matched RGL. Ambiguous cases: leave as a normal channel; no regression.
Optional follow-up: Find scratch channel candidates repair UI (mirror #116).
Pattern
| Concept |
Multi-TG expansion (#36) |
This issue |
| Trigger |
Digital channel with rxGroupListId on expansion-only export |
RGL includeScratchChannel on expansion-only export |
| Export output |
N rows (one per RGL member) |
N + 1 scratch row per RGL/site |
| OpenGD77 |
N/A — lean RGL reference |
N/A — flag ignored |
| DM32 |
Shipped (expandRxGroupLists: true) |
Primary consumer |
Affected
src/models/codeplug.ts — RxGroupList.includeScratchChannel; schema version + migration
src/lib/channelExpansion/ — scratch row generation alongside multi-TG expansion
src/lib/export/dm32/ — wire scratch row into Channels.csv serialisation
- CRUD — RX group list edit checkbox + help
docs/reference/multi-talkgroup-expansion.md — scratch channel section
docs/reference/dm32/multi-talkgroup.md — DM32-specific notes
- Unit + system tests (DM32 export fixture with flagged RGL)
Related
Open design questions (resolve in PR)
- TX contact default on scratch row — RGL-level picker vs first member vs operator template.
- Deduping when several logical channels share one RGL + RF profile.
- Zone fan-out — include scratch in zone member expansion or manual only.
- Naming —
{callsign} Scratch vs configurable export template (may share machinery with #153).
Out of scope
- Scan list carrier channels (
GB7GL Scan) — #125
- Per-channel scan lists and 16-member scan caps (DM32) — #125
- OpenGD77 scratch/promiscuous semantics (not needed)
- Guaranteed import recognition of every operator naming variant
Manual verify
- RGL with
includeScratchChannel off → DM32 export unchanged vs today.
- RGL flagged on → DM32 export adds one
{callsign} Scratch row per site/RGL with matching RF; re-imports as separate channel (collapse optional).
- OpenGD77 export → flag has no effect; lean RGL export unchanged.
- Name shortening enabled → scratch name respects
maxNameLength with documented trim rules.
- Zone export: behaviour matches chosen design for scratch inclusion.
Workflow note
Branch from origin/main, atomic conventional commits (model + migration → expansion lib + tests → DM32 wire → CRUD → docs). PR linking Closes #.
Problem
On many digital radios (Baofeng DM32, typical commercial DMR handsets) there is no true promiscuous RX and no independent repeater + talk group selection on the radio face. Each digital channel row maps to exactly one talk group; the operator picks a channel, not a site then a TG.
Amateur operators work around this in the field with extra channels beyond the expanded per-TG rows:
channelExpansion/, DM32expandRxGroupLists: true).GB7GL Scratch: same repeater/site RF profile as the site's other channels, but programmed with a throwaway TX contact the operator can change via radio menus for ad-hoc talk groups without corrupting the main channel layout.Today the app can expand an RX group list into flat per-TG rows on export, but it does not offer to emit the companion scratch channel operators routinely add by hand.
Not applicable to formats with native promiscuous RX and independent TG selection (OpenGD77 lean export with
TG List+TG_Lists.csv— see opengd77/multi-talkgroup.md).Intended outcome
Add an optional per–RX-group-list flag that, on export to formats requiring channel×talkgroup expansion, emits one additional scratch channel per flagged RGL.
Internal model
Extend
RxGroupList(vendor-neutral):false/ absent — no behaviour change.Export semantics (when
includeScratchChanneland target expands RGLs)For each logical digital channel referencing a flagged RGL, emit one scratch row in addition to expanded per-member rows (dedupe if multiple logical channels share the same RGL + RF context — one scratch per RGL per distinct site/RF profile, not per logical channel; finalise grouping rule in design).
Scratch row sketch:
{callsign} Scratch(respect name shortening /maxNameLengthat boundary)nullon wire (single-TG row semantics)Implementation lives in
src/lib/channelExpansion/(or sibling export helper), invoked from DM32 and future expansion consumers — not in CRUD validation as a radio cap.Import (best-effort, not a blocker)
Detect scratch channels on import (name suffix / pattern + RF match to an RGL cluster) and set
includeScratchChannelon the matched RGL. Ambiguous cases: leave as a normal channel; no regression.Optional follow-up: Find scratch channel candidates repair UI (mirror #116).
Pattern
rxGroupListIdon expansion-only exportincludeScratchChannelon expansion-only exportexpandRxGroupLists: true)Affected
src/models/codeplug.ts—RxGroupList.includeScratchChannel; schema version + migrationsrc/lib/channelExpansion/— scratch row generation alongside multi-TG expansionsrc/lib/export/dm32/— wire scratch row intoChannels.csvserialisationdocs/reference/multi-talkgroup-expansion.md— scratch channel sectiondocs/reference/dm32/multi-talkgroup.md— DM32-specific notesRelated
ScanListentity + scan-list carrier channels (separate workflow; discuss separately)Open design questions (resolve in PR)
{callsign} Scratchvs configurable export template (may share machinery with #153).Out of scope
GB7GL Scan) — #125Manual verify
includeScratchChanneloff → DM32 export unchanged vs today.{callsign} Scratchrow per site/RGL with matching RF; re-imports as separate channel (collapse optional).maxNameLengthwith documented trim rules.Workflow note
Branch from
origin/main, atomic conventional commits (model + migration → expansion lib + tests → DM32 wire → CRUD → docs). PR linkingCloses #.