Created 2020-06-17
Modified 2020-08-03
- Amateur Radio
- DMR
- Codeplug
A note from the future that goes at the start
I started working on this nearly two month ago, and I've not had time to get any further with it. So I'm going to post this now, incomplete as it is, in the hopes it might be useful to someone else, or my future self if I get back to it at some point.
Some background
Recently I was too tempted by the seemingly very good price of the Baofeng dm1702 DMR radio.
I paid £65 for the radio, which included delivery, which seemed reasonable considering the features
- 2m + 70cm
- Tier II DMR
- Analogue FM
- GPS
- 5w output
- Programming over standard USB!
I didn't do enough research though, I just assumed that there would be tools available to help me code it up in Linux, as there are for so many DMR radios, but sadly all I can find right now is some stuff to write data to and from the radio, which works, but doesn't help me change any settings like dmrconfig does for my Anytone.
So I resorted to the official software, which only works in windows, which means I had to kick my offline VM into gear. But oh my, is that software bad, like, really, really bad.
When editing a table of channels, you can't tab between them, it sorts by name so adding a new row goes wherever the default text fits in the sorting order, etc. etc. etc.
It is so bad I decided to break out a hex editor and try to work out what is going on in the codeplug itself.
An initial look
Now, I'm not an expert at reverse engineering anything, so I don't expect to be able to actually do this task…
A first look wasn't promising, there is lots of empty space, lots of FFFF
, lots of repeating data that doesn't appear to store anything meaningful.
Next I made one small change to the codeplug, and tried to diff it, here I have changed my hotspot frequency from 438.8MHz
to 136.12345MHz
on RX and 444.67899
on TX:
$ diff <(xxd codeplug-justfreq.data) <(xxd codeplug.data)
833c833
< 00003400: 4523 6113 9978 4644 4000 0000 0000 1100 E#a..xFD@.......
> 00003400: 4523 6113 9978 4644 4200 0000 0000 1100 E#a..xFDB.......
Thanks to a great tip from SP6MR who identified the values as being stored as unsigned 32bit
I started to get somewhere. Maybe.
A change of tense
You might notice a tense change now, as I'm writing this post as I work, mostly to keep notes for myself!
We can also see that changing the TX power level from low to high changes the value after the frequency from 40
to 42
< 00003400: 4523 6113 9978 4644 4000 0000 0000 1100 E#a..xFD@.......
> 00003400: 4523 6113 9978 4644 4200 0000 0000 1100 E#a..xFDB.......
Changing the timeslot from 2
to 1
and the last chunk changes from 11
to 10
< 00003400: 4523 6113 9978 4644 4000 0000 0000 1100 E#a..xFD@.......
> 00003400: 4523 6113 9978 4644 4200 0000 0000 0100 E#a..xFDB.......
But wait! changing the colour code from 1
to 2
makes that 11
or 10
into 12
or 02
, so we must be storing the timeslot in one bit, counting from zero
< 00003400: 4523 6113 9978 4644 4000 0000 0000 1100 E#a..xFD@.......
> 00003400: 4523 6113 9978 4644 4200 0000 0000 1200 E#a..xFDB.......
Which makes me wonder about our conclusion for the TX power…
< 00003400: 4523 6113 9978 4644 4000 0000 0000 1100 E#a..xFD@.......
> 00003400: 4523 6113 9978 4644 0000 0000 0000 1200 E#a..xFD........
Based on that, the 40
is actually 4
for DMR/Digital and 0
for analogue, with the other bit storing the TX power level as 0
or 2
.
We also see another line change much further through the file:
< 0001e020: 000e 0009 000b 0100 000d 0100 0000 ffff ................
> 0001e020: 000e 0009 000b 0100 000d 0000 0000 ffff ................
Only one bit seems to change though, so I don't know what it's for (yet?)
Next I changed it back to a digital channel, and set the "TX Contact Name"
< 0001e020: 000e 0009 000b 0100 000d 0100 0000 ffff ................
> 0001e020: 000e 0009 000b 0100 000d 0005 0000 ffff ................
It seems that the 6th chunk has some bits that changes to a number referring to the contact (somehow).
Around 0001F000
we see what looks like the name of contacts
0001f000: ffff 5357 204f 6869 6f20 4b34 5553 4400 ..SW Ohio K4USD.
0001f010: 0000 ffa2 7a00 04ff ffff 3331 3035 3537 ....z.....310557
0001f020: 204d 6169 6e00 0000 0000 ff1d bd04 04ff Main...........
0001f030: ffff 3331 3639 204d 6964 5765 7374 0000 ..3169 MidWest..
0001f040: 0000 ff61 0c00 04ff ffff 4172 6561 2038 ...a......Area 8
0001f050: 2033 3130 3938 0000 0000 ff7a 7900 04ff 31098.....zy...
0001f060: ffff 3331 3020 5441 4300 0000 0000 0000 ..310 TAC.......
0001f070: 0000 ff36 0100 04ff ffff 3331 3120 5441 ...6......311 TA
0001f080: 4300 0000 0000 0000 0000 ff37 0100 04ff C..........7....
0001f090: ffff 3331 3220 5441 4300 0000 0000 0000 ..312 TAC.......
0001f0a0: 0000 ff38 0100 04ff ffff 3331 3030 2055 ...8......3100 U
0001f0b0: 5341 0000 0000 0000 0000 ff1c 0c00 04ff SA..............
0001f0c0: ffff 3933 204e 2041 6d65 7269 6361 0000 ..93 N America..
0001f0d0: 0000 ff5d 0000 04ff ffff 3331 3339 204f ...]......3139 O
0001f0e0: 6869 6f20 5769 6465 0000 ff43 0c00 04ff hio Wide...C....
0001f0f0: ffff 576f 726c 6445 6e67 6c69 7368 2039 ..WorldEnglish 9
Each Name seems to be 16 characters long, and have something after the text. This repeats for 800 calls, ending at 00023B40
, where we have FF
until 00024000
where we get 00
until 00025020
.
Changing the call ID causes a change, but it's not clear how the data is stored, it doesn't appear to be unsigned 32bit
. Here I've changed the ID from 310
to 111
< 0001f070: 0000 ff36 0100 04ff ffff 3331 3120 5441 ...6......311 TA
> 0001f070: 0000 ff6f 0000 04ff ffff 3331 3120 5441 ...o......311 TA
Based on Andy Valencia's keen eyes the ID is being stored in "endian shuffled order", as can be seen here with the ID 11111111
as c7, then 8a, and a9
, read each two together, but backwards and we get 0xa98ac7
0001f070: 5354 ffc7 8aa9 04ff ffff 3331 3120 5441 ST........311 TA
If we change the ID to 11111113
we have even more confidence we're doing it right
0001f070: 5354 ffc9 8aa9 04ff ffff 3331 3120 5441 ST........311 TA
Structure of a contact
At this point, I think the structure of a contact is:
- 16 bytes text (Name)
- 1 byte padding/space (
FF
) - 3 bytes in "endian shuffled order" (call ID)
- 1 byte either
03
(Private Call)04
(Group Call) or05
(All Call) - 3 bytes padding/space (
FF FF FF
)
Making 24 bytes in total per contact.
Structure of a memory
- 4 bytes unsigned 32bit (RX Frequency)
- 4 bytes unsigned 32bit (TX Frequency)
0
for analogue,4
for digital.- Add
2
if "RX Only" (2
or6
) - Add
1
if Squelch is "Stringent"
- Add
-
A bit for power, Channel Spacing, and "Lone Worker"
0
for low power,4
for high power.- If "Lone Worker"
8
for low anda
for high power. 0
Low + Narrow spacing (Analogue) OR Low Digital Power1
Low + Wide spacing (Analogue)2
High + Narrow spacing (Analogue)3
High + Wide spacing (Analogue)4
High Digital Power8
Low + Narrow spacing (Analogue) + Lone Worker OR Low Digital Power + Lone Worker9
Low + Wide spacing (Analogue) + Lone Workera
High + Narrow spacing (Analogue) + Lone Worker OR High Digital Power + Lone Workerb
High + Wide spacing (Analogue) + Lone Worker
-
8
for "Auto Scan Start", or0
for not (only used if scan list set) 1
for the "Scan List" reference, or0
if none00
Is for "TX Admit"00
"Always" (Analogue or Digital)01
"Analogue Channel Free"02
"Analogue CTCSS/CDCSS Correct"03
"Analogue CTCSS/CDCSS Incorrect"08
"Digital Channel Free"10
"Digital Color Code Free"
8
for "Emergency Alarm Indication",c
if also "Emergency Alarm ACK",e
if also "Emergency Call Indication",2
if not "Alarm" but "Call"1
for the "Emergency System" reference, or0
if none0
- Doesn't appear to change0
- Doesn't appear to change0
for "Private Call Confirmed" off,2
for on.3
for "Short Data Significance Bit" in "Confirming Mode",1
if "Private Call Confirmed" is off0
- Doesn't appear to change0
for Slot 0,1
for Slot 2,3
for "Double Capacity Mode"0
-f
for the colour code0
for no encryption,8
for encryption on0
-f
for the Encryption Key reference
3410 offset
00
-ff
"GPS System" reference00
-ff
"RX Group List" Reference1
- Doesn't appear to change0
or8
??ff ff
"CTCSS/CDCSS Decode" in endian shuffled order (e.g.70 06
for67.0
,41 25
for254.1
)ffff
if none.ff ff
Same again for Encode.1
for VOX,0
if not0
- Doesn't appear to change8
for "PTT ID Display",0
for off000
- Doesn't appear to change0
if "Talk Around Status" is OFF,1
if OFF. Add4
if ENABLED (4
and5
)000
- Doesn't appear to change0000 0000
- Doesn't appear to change
0001e020 offset
- 3 bytes
0
0100
0000
0
0
00
-ff
"TX Contact Name" reference
Make a memory by hand
In theory we know enough to try making a memory by hand now, I'm going to try GB3CG, a local analogue repeater on 2m.
- 145.725MHz TX
- 145.125MHz RX
- βJβ 118.8 Hz CTCSS
- Analogue
- High Power + Narrow spacing
- TX admit always
- everything else off
I think it will look like this:
0025 5714 0025 5114 0200 0000 0000 0000
0000 1881 1188 1000 0000 0000 0000 0000
I manually edited the codeplug in Okteta and wrote it to the radio, and it half worked!
CTCSS was wrong, it was enabled but had the wrong value, so I changed it on the radio and read back the changes
< 00003410: 0000 1881 1188 1000 0000 4000 0000 0000 ..........@.....
> 00003410: 0000 1888 1188 1100 0000 4000 0000 0000 ..........@.....
When I remove just the RX CTCSS we get
< 00003410: 0000 1881 1188 1000 0000 4000 0000 0000 ..........@.....
> 00003410: 0000 18ff ff88 1100 0000 4000 0000 0000 ..........@.....
When I remove just the TX CTCSS we get
> 00003410: 0000 18ff ffff ff00 0000 4000 0000 0000 ..........@.....
This suggests that I've not got the CTCSS structure quite right, but what's really happened is I've messed up understanding the structure. The one above is actually the revised version, not what I originally had. Byte 3 on this line needs to be something else, and I'd missed it completely so everything was offset.