codeplugger GitHub ↗

Open source · Chicago Offline

Programming a radio should be a build, not a CPS session.

codeplugger compiles shared RF facts and your own version-controlled profile into a validated, radio-neutral codeplug, then exports it or writes it straight to the radio. Vendor CPS tools become replaceable backends instead of the workflow.

codeplugger logo: a handheld radio with a code glyph on its screen and plug prongs at its base

How it works

Facts and preferences stay separate. RF data describes what exists; a profile describes what you want in a particular radio. Everything in between is deterministic and reviewable.

SSRF data

Public channel plans, repeaters, and talkgroups from SSRF-Lite, merged with shared or private overlays by stable record ID.

Profile

Your policy in YAML: which assignments, in what order, in which zones, with scan lists, RX groups, contacts, and radio settings. Profiles can inherit from one another.

Resolve & validate

codeplugger joins the inputs and checks every channel against the target radio's capabilities: bands, modes, name lengths, zone and list limits, firmware-dependent capacity.

Export or program

Emit CHIRP CSV, qdmr YAML, or P4 TOML; or let a gated backend write the radio directly, verify by read-back, and log the operation.

Supported radios

Each radio is a capabilities definition plus an exporter or programming backend. Limits below come straight from the checked-in radios/*/capabilities.json.

Baofeng DM-32

DM-32UV
Headless write Hardware verified DMR FM AM airband RX
Bands
136–174 · 400–480 MHz; 108–136 MHz receive
Channels
4000 · 250 zones × 64
Lists
32 scan lists · 32 RX group lists

qdmr YAML → dmrconf. Read-only detect/read/verify, explicit confirmation, pre-write verify gate, detected-radio identity gate, read-back archive, operation log. Display colors and button functions pass through extensions.qdmr.

Retevis MateTalk P4

UHF DMR
Headless write DMR FM
Bands
400–470 MHz
Channels
256 · 16 zones × 16
Contacts
Fleet-registry private contacts

P4 TOML → p64tool. Merges into a p64tool decode baseline; write requires confirm=True, a known-firmware gate, and read-back verification.

Baofeng UV-5R Mini

UV-17Pro family
CHIRP import FM
Bands
136–174 · 400–480 MHz; airband and 350–390 / 480–520 receive
Channels
999 · 12-char names

CHIRP CSV. Analog FM channels in profile order; import in CHIRP and upload from there. Limits are sourced from the CHIRP driver, not the classic UV-5R.

Ailunce HA2

Dual-band analog
CHIRP import FM AM airband RX
Bands
136–174 · 400–480 MHz; FM broadcast, airband, 480–520 receive
Channels
1024 · 16 zones × 64 · 12-char names

CHIRP CSV. Generic analog path; GMRS and MURS require the extended TX unlock available through app programming.

Retevis C64

Dual-band analog
CHIRP import FM
Bands
136–174 · 400–480 MHz
Channels
64 · 12-char names · no zones

CHIRP CSV. Generic analog path. Limits are measured from a physical radio (ident block, NAK boundary, RF calibration table) and match the CHIRP C64 driver, a Retevis C2 protocol variant.

Next up: more qdmr-supported radios through capabilities definitions alone (AnyTone AT-D868UV / 878UV / 578UV, TYT MD-UV380 / UV390, Retevis RT3S, Radioddity GD-77, Baofeng DM-1701), then OpenGD77 via dmrconf. Adding a radio never touches input resolution.

Features

Built for people who keep more than one radio consistent, and want to know exactly what went into each one.

Deterministic builds

The same inputs always produce the same codeplug, down to a stable SHA-256. Generated files are disposable artifacts, never a data source.

Capability validation

Bands, TX-vs-RX-only ranges, modes, bandwidth, name lengths, zone and list limits, and firmware-dependent contact capacity, graded hint / warning / critical with every critical reported in one run.

Gated radio writes

Explicit confirmation, known-firmware and detected-radio identity checks, pre-write verify, read-back verification, and an append-only operation log for every write.

Zones, scan lists, RX groups, contacts

Talkgroups are SSRF facts; membership and per-assignment TX contact, RX group, and scan list are profile policy, all enforced against radio limits.

Profile inheritance

Share a base profile and derive variants with extends, zones_only, and omit_zones. Review the merged result with --print-merged-profile.

Multi-identity DMR IDs

A radio instance can carry several DMR IDs; profiles bind one per zone or per assignment and exporters emit only the identities actually used.

Fleet registry & dry run

An instance registry maps physical radios to profiles. codeplugger-fleet resolves and validates every radio and reports readiness before anything is written.

Derived channel facts

Bandwidth from the ITU emission designator, AM mode for airband receive-only assignments, display-name overrides from overlays.

Reference artifacts

HTML and Markdown channel references plus operation logs per radio instance, so what is in the radio is always documented.

Namespaced extensions

Exporter-native settings live under extensions.<backend> at profile or assignment level, keeping the core schema radio-neutral.

Quick start

Clone codeplugger next to ssrf-lite and a profile repository, then use uv.

Validate a profile

uv run codeplugger-profile path/to/profile.yml \
  --ssrf-root ../ssrf-lite/ssrf \
  --ssrf-root ../chioff-ssrf-shared/ssrf

Export for a radio

# analog radios: import in CHIRP
uv run codeplugger-profile profile.yml \
  --ssrf-root ../ssrf-lite/ssrf \
  --output-format chirp-csv > output.csv

# DM-32: qdmr YAML, then dmrconf write
uv run codeplugger-profile profile.yml \
  --ssrf-root ../ssrf-lite/ssrf \
  --output-format qdmr-yaml > codeplug.yaml

Check the whole fleet

uv run codeplugger-fleet \
  --instance-registry ../my-profiles/instances.yml \
  --profiles-root ../my-profiles/profiles \
  --ssrf-root ../ssrf-lite/ssrf

A minimal profile

version: "0.1"
id: "dm32_reference"
name: "DM-32 reference"
radio: "baofeng_dm32"
radio_instance: "dm32_green_01"
zones:
  - id: "community"
    name: "Community"
    assignments:
      - "asg_chio_all"
      - "asg_chio_road"
      - "asg_chio_repeater"