codeplugger builds radio codeplugs from shared RF data and user-owned
configuration. Its initial target is a neonplug-compatible import for the
Baofeng DM-32, with room to support other radios and CPS tools later.
flowchart LR
public["ssrf-lite<br/>Authoritative public RF data"]
private["SSRF overlay<br/>chioff fixture or private user data"]
profiles["Codeplugger profile<br/>chioff fixture or user preferences"]
merge["Resolve and validate inputs"]
codeplugger["codeplugger<br/>Generate CPS import"]
cps["neonplug / DM32 CPS"]
radio["DM32 radio"]
public --> merge
private --> merge
profiles --> codeplugger
merge --> codeplugger
codeplugger --> cps
cps --> radio
The diagram source is also available in docs/workflow.mmd.
ssrf-liteis the authoritative source for shareable RF facts: channel plans, repeaters, stations, talkgroups, and their schema.chioff-ssrf-testis a public reference overlay. It models the structure and merge behavior of a private SSRF repository using only public or synthetic fixture data.chioff-ssrf-sharedholds real Chicago Offline community-net RF facts (shared GMRS and MURS channels) that members merge on top ofssrf-lite.username-ssrf-privatecontains private RF records and explicit overrides ofssrf-lite, such as local names, unpublished channels, or user-specific grouping metadata.chioff-codeplugger-profiles-testcontains public reference profiles for Chicago Offline radios and provides realistic inputs for end-to-end integration tests.chioff-codeplugger-profiles-sharedcontains profiles selecting the shared community net for supported radios.username-codeplugger-profilescontains radio and output preferences: channel selection, zones, scan lists, button assignments, display settings, and per-radio variants.codepluggerresolves those inputs, validates them against radio limits, and generates files importable by a supported CPS tool.dm32-inforemains a research and reference repository. Stable findings needed for generation should become radio definitions or validation rules incodeplugger, rather than making generation depend directly on research notes.
Inputs should be merged deterministically:
- Load RF records from
ssrf-lite. - Add or override RF records from
username-ssrf-privateusing stable record identifiers, not display names. - Apply a selected codeplug profile to choose and arrange records and set radio-specific preferences.
- Validate the result against the target radio and exporter constraints.
- Generate the CPS import as a disposable build artifact.
Generated codeplugs should not become an authoritative data source. Changes should flow back into SSRF data or the selected profile and then be regenerated.
RF data describes what exists; a profile describes what a person wants programmed into a particular radio. Keeping those concerns separate makes profiles reusable across SSRF dataset updates and avoids mixing device settings into an RF schema.
The two chioff-* repositories form a public reference configuration for
integration testing:
ssrf-lite + chioff-ssrf-test + chioff-codeplugger-profiles-test -> codeplugger
The -test repositories hold only synthetic fixture data and exist to model
the overlay merge behavior. Real user repositories may follow the same layout
while remaining private. Public fixtures must not contain personal identities,
unpublished frequencies, credentials, location-sensitive records, or exports
from real radios.
The -shared repositories are the exception: they carry real Chicago Offline
community-net channels that are intended to be published and shared. They
still must not contain personal identities, credentials, or radio exports.
Small synthetic fixtures should remain in this repository for fast,
deterministic unit tests. The chioff-* repositories are intended for broader
cross-repository contract and end-to-end tests.
The first end-to-end milestone is intentionally narrow:
- Read
ssrf-liteplus optional private overrides. - Read one DM-32 profile.
- Validate channel and radio constraints.
- Produce an import accepted by neonplug.
The directories in this repository are intended to evolve along these lines:
radios/: radio capabilities, constraints, and exporter-specific mappings.profiles/: public examples and fixtures, not real user secrets.
Profile version 0.1 selects ordered SSRF assignments by stable ID and groups
them into ordered zones for one target radio:
$schema: "https://raw.githubusercontent.com/Chicago-Offline/codeplugger/main/schemas/profile-0.1.schema.json"
version: "0.1"
id: "dm32_reference"
name: "DM-32 reference"
radio: "baofeng_dm32"
radio_instance: "dm32_reference_01"
zones:
- id: "reference"
name: "Reference"
assignments:
- "asg_fixture_reference_simplex"
- "asg_wx1"radio identifies the radio model and radio_instance identifies the specific
physical radio receiving the generated codeplug. The instance ID is arbitrary
but stable, such as dm32_green_01 or d01. Profiles that omit it retain
backward compatibility by using their profile id as the instance ID.
Profiles contain selection and ordering policy, not RF facts or per-user DMR
identities. Display-name or RF changes belong in an SSRF overlay. Version 0.1
intentionally excludes selectors, scan lists, contacts, button mappings, and
exporter settings until the explicit-ID workflow is proven end to end.
Validate a profile against authoritative data, overlays, and radio limits:
uv run codeplugger-profile path/to/profile.yml \
--ssrf-root ../ssrf-lite/ssrf \
--ssrf-root ../chioff-ssrf-test/ssrfInspect the normalized, exporter-neutral codeplug as deterministic YAML or JSON:
uv run codeplugger-profile path/to/profile.yml \
--ssrf-root ../ssrf-lite/ssrf \
--ssrf-root ../chioff-ssrf-test/ssrf \
--output-format yamlThe normalized model contains ordered channels and zones, radio-facing RX/TX frequencies, mode, service, analog tones, overlay-resolved notes, and explicit TX permission. It intentionally excludes scan lists, contacts, DMR identities, button settings, and NeonPlug fields; exporters translate this stable model without participating in input resolution.
For analog radios such as the UV-5R Mini, codeplugger can emit CHIRP-compatible CSV and CHIRP remains the upload interface to the radio.
Generate CHIRP CSV from a validated profile:
uv run codeplugger-profile path/to/profile.yml \
--ssrf-root ../ssrf-lite/ssrf \
--ssrf-root ../chioff-ssrf-test/ssrf \
--output-format chirp-csv > output.csvThen import output.csv in CHIRP and upload from CHIRP to the radio.
Notes:
- CHIRP CSV export currently supports FM channels only.
- Any non-FM channel selected by the profile fails export with an explicit error.
- Channel order in the CSV matches resolved profile order.
Profiles may declare radio_instance, and codeplugger can optionally validate
that identifier against a separate instance registry document:
version: "0.1"
instances:
dm32_green_01:
radio: baofeng_dm32
label: "Green DM-32"
firmware: "DM32.01.L01.048"Use --instance-registry to enable this join check:
uv run codeplugger-profile path/to/profile.yml \
--ssrf-root ../ssrf-lite/ssrf \
--instance-registry path/to/instances.ymlWhen enabled, codeplugger verifies:
- the profile instance ID exists in the registry
- the registry entry's
radiomatches the profile'sradio
If no registry is provided, behavior stays backward compatible with existing profile-only workflows.