qdmr YAML codeplug

qdmr and its command-line tool dmrconf save a codeplug as a YAML file, the "extensible codeplug". The Codeplug Editor opens and saves this file, so a codeplug can move between qdmr and the editor in both directions. This spec covers the file as qdmr 0.15 writes it, the editor's mapping to the open-firmware records (open-firmware-cps.md 7), and what each direction leaves out.

Sources: qdmr's published manual (the "Extensible Codeplug File Format" chapter), files written by qdmr 0.15.1, and dmrconf encode run on test files, with the binary it writes decoded by the editor's own decoder. qdmr's source is GPL and was not read (CLEANROOM.md).

Code: site/static/js/codeplug/yaml.js (the YAML subset), site/static/js/codeplug/qdmr.js (the mapping), site/static/js/editor/qdmrpart.js (the editor's Open and Save), tests in site/tests/qdmr.test.mjs.

1. Syntax

The file is one YAML 1.2 document. qdmr uses a small part of YAML, and yaml.js reads that part:

Construct Example Notes
Document markers --- first, ... last Optional. A second document is refused
Block map settings: and indented keys Two-space indent
Block list - dmr: qdmr indents a list two spaces under its key
Flow map {id: cont1, name: TG 9, number: 9} Contacts, radio IDs and group lists are written this way
Flow list A: [ch1, ch2], B: [] References and paths
Plain scalar name: Repeater TS1, name: 201 A plain 201 reads as a number; names are converted back to text
Quoted scalar password: "", 'It''s', "a\tb" Double quotes take the usual escapes
Null rxTone: ~
Tag power: !default "" !default: use the radio's global value
Comment # ... After whitespace, outside quotes
Block scalar ` , >with-or+`

Anchors, aliases, complex keys and tabs in indentation are refused with an error. The writer quotes a string when the plain form would read back as something else (a number, true, empty, leading space, : or #).

2. Structure

The top-level keys, in qdmr's order:

Key Holds Editor
version qdmr version that wrote the file Written as 0.15.1; not checked on import
settings Radio-wide settings: power, defaultID, boot, audio, tone, gnss, dmr and others Part, see 4.1
radioIDs The radio's DMR IDs: - dmr: {id, name, number} Yes
contacts - dmr: {...} digital contacts, - dtmf: {...} DTMF contacts Yes
groupLists RX group lists: {id, name, contacts: [refs]} Yes
channels - dmr: {...} and - fm: {...} channels Yes
zones {id, name, A: [refs], B: [refs]} Yes
scanLists Scan lists Dropped
positioning - aprs: {...} APRS systems, - dmr: {...} DMR position reporting APRS only
roaming Roaming channels and zones Dropped
commercial encryptionKeys Dropped
sms Message format and templates Dropped

Older qdmr versions and the manual name the element types digital and analog. The import accepts both spellings; the export writes dmr and fm, as qdmr 0.15 does.

2.1 References

Every element has an id that is unique in the file. Other elements refer to it by that id: a channel's contact, groupList, radioId and aprs, a zone's A and B, a group list's contacts, an APRS system's revert. qdmr writes ids as a prefix and a count (ch12, cont3, grp1, zone2, aprs1, id1), and the export does the same in table order. The import doesn't depend on the form. A reference to an id the file doesn't define is dropped.

2.2 Values

Kind Forms read Form written
Frequency 439.5625 MHz, 145500 kHz, Hz, GHz, a bare number in MHz 439.5625 MHz
Duration 3 min, 45 s, 500 ms, a bare number in seconds, infinite / off (0), !default 3 min, 45 s, infinite
Sub-tone {ctcss: 67}, {ctcss: 67 Hz}, {dcs: 23}, {dcs: -754} (negative = inverted), ~ {ctcss: 67 Hz}, {dcs: -754}, ~
Power Min, Low, Mid, High, Max, !default Same
Location Maidenhead locator, 4, 6 or 8 characters, or "" 6 characters
Boolean true, false Same

3. Elements

3.1 Radio IDs and settings

qdmr Editor Notes
settings.defaultID Names the radio ID that is the radio's own
default radio ID number Radio, DMR ID
default radio ID name Radio, radio name Shortened to 8 characters
other radio IDs Channel DMR ID A channel whose radioId names another ID gets that ID as its own DMR ID
settings.boot.message1, message2 Boot text lines 1 and 2 16 characters
settings.dmr.groupCallHangTime, privateCallHangTime Group and private hang In 500 ms steps (open-firmware-cps.md 7.3.2, the unit our firmware assumes)
settings.gnss.fixedPositionEnabled, fixedPosition APRS: use fixed position, latitude, longitude Applied to every APRS configuration
settings.audio.vox Decides a channel's VOX when the channel says !default

The export writes one radio ID for the radio (id1) and one more for each distinct channel DMR ID. Other settings (power, audio, tone, the boot logo and password, talker alias encoding and so on) are radio settings the codeplug image doesn't hold in the editor's model; the import ignores them and the export leaves them out, so qdmr uses its defaults.

3.2 Contacts

openGD77 is the name qdmr gives its block of per-contact and per-channel extension keys for the open-firmware radios.

qdmr Editor
dmr.name Name (16 characters)
dmr.type: GroupCall, PrivateCall, AllCall Call type: group, private, all
dmr.number ID (all call: 16777215)
dmr.openGD77.timeSlotOverride: None, TS1, TS2 Timeslot: channel's, 1, 2
dmr.ring Dropped (written as false)
dtmf.name, dtmf.number DTMF contact name and code

In the export, DTMF contacts follow the digital contacts and continue their id count.

3.3 RX group lists

name and contacts map directly. A list keeps its first 32 contacts.

3.4 Channels

Both types:

qdmr Editor Notes
name Name 16 characters
rxFrequency, txFrequency RX, TX frequency 10 Hz grid
power Power byte: Min 1, Low 3, Mid 6, High 9, Max 10, !default 0 open-firmware-cps.md 7.3.3. Another stored level exports as !default
timeout TX timeout 15 s steps, rounded up; infinite and !default are off
rxOnly RX only
vox VOX on or off A number above 0 is on, off or 0 is off, !default follows settings.audio.vox. The export writes 1 or off
openGD77.scanZoneSkip, scanAllSkip Zone skip, all-channel skip
openGD77.beep Not "no beep"
openGD77.powerSave Not "no economy"
openGD77.location Use location, latitude, longitude Locator to the center of its square. The export writes a 6-character locator, about 5 km
openGD77.talkerAliasTS1, TS2: None, APRS, Text, Both Talker alias TX on TS1, TS2: 0-3
aprs APRS configuration Only an APRS system the file defines
scanList Dropped
admit Dropped Written as Always

DMR channels:

qdmr Editor Notes
colorCode Color code 0-15
timeSlot: TS1, TS2 Timeslot
contact TX contact
groupList RX group list
radioId Channel DMR ID !default or the default ID: none
roaming Roaming flag Any value sets it; the export writes !default ""
extended.talkaround DMO flag
other extended keys Dropped

FM channels:

qdmr Editor Notes
bandwidth: Narrow, Wide 12.5 or 25 kHz
rxTone, txTone Tones
squelch Squelch level qdmr's 1-10 is doubled onto the record's 1-21; 0 and !default are the radio's default. The export halves it
extended.reverseBurst Dropped

The SFR settings (flag, bytes A and B) have no qdmr counterpart; the export leaves them out and says so.

3.5 Zones

name and A map to a zone and its channels. qdmr's zones have two lists, A and B, while an open-firmware zone has one. A zone with an empty B list imports as one zone. A zone with channels in both imports as two, <name> A and <name> B, which is also what dmrconf encode writes for this radio. The export writes each zone as an A list with an empty B list. A zone keeps its first 80 channels.

3.6 APRS

An aprs element under positioning becomes an APRS configuration (open-firmware-cps.md 7.3.8):

qdmr Editor Notes
name Name 8 characters
source SSID The call sign part is dropped; the radio sends its own
path Path 1 and 2 Entries after the second are dropped
icon Symbol table / and symbol Table below; an unknown name imports as the car />
message Comment 23 characters
revert TX frequency The TX frequency of the FM channel it names. The export names the first FM channel on that frequency
opengd77.baud: 300, 1200 300 baud flag
opengd77.transmitQSY QSY flag
period Dropped The radio sets the beacon interval in its menu
destination Dropped Written as APN000-0

The export builds source from the radio name (letters and digits, up to 6) and the SSID, or N0CALL and the SSID when the radio name has none. The silent and ambiguity settings have no qdmr counterpart.

dmr elements under positioning (position reports over DMR) have no counterpart and are dropped.

qdmr's icon names, each a symbol of the primary table (/), as dmrconf encode writes them:

Symbol Names
! - / PoliceStation, None, Digipeater, Phone, DXCluster, HFGateway, SmallPlane, MobileSatelliteStation, WheelChair, Snowmobile, RedCross, BoyScout, Home, X, RedDot
0 - 9 Circle0 - Circle9
: - @ Fire, Campground, Motorcycle, RailEngine, Car, FileServer, HCFuture
A - Z AidStation, BBS, Canoe, (D has none), Eyeball, Tractor, GridSquare, Hotel, TCPIP, (J has none), School, Logon, MacOS, NTSStation, Balloon, PoliceCar, TBD, RV, Shuttle, SSTV, Bus, ATV, WXService, Helo, Yacht, Windows
[ - ` Jogger, Triangle, PBBS, LargePlane, WXStation, DishAntenna
a - z Ambulance, Bike, ICP, FireStation, Horse, FireTruck, Glider, Hospital, IOTA, Jeep, SmallTruck, Laptop, MicE, Node, EOC, Rover, Grid, Antenna, PowerBoat, TruckStop, TruckLarge, Van, Water, XAPRS, Yagi, Shelter

A symbol from the alternate table, or /D and /J, exports as None with a note.

4. What each direction reports

The import returns the record counts and a list of notes, each a code and a count, for what it changed or left out: names shortened, scan lists, roaming, encryption keys, DMR position reporting, APRS periods, extra path entries, unknown icons, lists and zones over their limits, records over the table limits, unknown element types, channels without a valid frequency, sub-tones that aren't CTCSS or DCS. The editor lists them after the import.

The export returns notes for SFR settings, power levels qdmr has no name for, channel locations rounded to a locator, APRS frequencies with no FM channel to name, APRS symbols with no icon, and the silent and ambiguity flags. The VFOs and the DMR ID database are never part of a qdmr file.

An import replaces the channel, zone, contact, DTMF contact, RX group list and APRS tables and the settings in 3.1, as one undoable change. Everything else in the open codeplug (the VFO frequencies, the radio's other settings, the DMR ID database) stays. The VFOs lose their contact, list and APRS references, since those pointed into the old tables. Records are numbered in file order.

5. qdmr's own binary encoding

dmrconf encode -R openuv380 on the same file writes the same channels, contacts, lists, zones, DTMF contacts and boot settings as the editor, with these differences (qdmr 0.15.1):

Field qdmr writes Editor writes
TX contact of a channel with an RX group list 0 The contact
Squelch (channel byte 0x37) 0 The level (3.4)
Group and private hang 0 (the bytes are left zero) The settings (3.1)
APRS symbol (bytes 0x1D, 0x1E) Table index (0 primary) and symbol index (character - 0x21) The characters, as core/codeplug/codeplug.c reads them
APRS TX frequency 0 The revert channel's TX frequency
APRS record magic 0x4152 (older layout) 0x4153
A channel without an openGD77 block Beep and power save off Beep and power save on
A contact without an openGD77 block Timeslot override TS1 No override
Channel without location, settings.gnss.fixedPosition set The fixed position No location
radioId: !default Flag 1 bit 7 set with ID 0 Bit 7 clear, 00 16 00
extended.talkaround, roaming Not written DMO and roaming flags

The APRS symbol difference means the firmware reads no symbol from a codeplug qdmr wrote (it takes a byte below ! as none). A codeplug written by the editor carries the symbol characters.

6. Tests

site/tests/qdmr.test.mjs covers the YAML subset, the value formats, an import of a file written in qdmr's form, the round trips YAML to model to YAML and cpsSim's codeplug to YAML to model, and the Tools page converters. When dmrconf is installed, a test exports a codeplug, runs dmrconf encode -R openuv380 on it and compares the decoded binary with the editor's encoding, field by field, apart from the differences in section 5. Without dmrconf that test is skipped.

View the source of this page