DMR IDs

From W9CR
Revision as of 22:01, 27 June 2021 by Bryan (talk | contribs)
Jump to navigation Jump to search

DMR ID's

I've been getting more into amateur DMR and of course you need an ID. Radioid.net is the DB for this of course, but the scheme in use was developed by DMR-MARC. They came up with some convoluted scheme back in the day using Mobile Country Codes as a prefix to segregate the ID's so uses could load only the countries they wish in their contact lists. There is no hierarchical authority (ala DNS) where Radioid.net passes off lookups to country (or lower) levels, and the entire DB stands at under 1mb as text, so this segregation is simply for the users.


DMR ID

First some words on the DMR ID from the specs. It's simple, a 24 bit number, however certain types of DMR systems only use the first 16 bits (Cap Plus trunking for one). Hams tend to use the entire 24 bit ID, and it's typically represented at decimal.

24 bits = 16,777,215 possible ID's

16 bits = 65,536 possible ID's for cap plus

DMR-MARC scheme

DMR MARC's scheme uses the Mobile Country Code ("MCC") as the first part of the ID in decimal, then one digit for sub-country (State), and 3 decimal digits for the user.

First doing this as decimal means we cannot use any ID over 9,999,999 as MNC cannot begin with a 1, thus we're limited to the 7 decimal digits. This removes 6,777,216 ID's from the scope of use.

Another issue is using a 3 digits for the MCC. There are only 226 MCC's leaving 774 blocks of 10000 ID's unused or 7,740,000 total ID's unable to be used. (actually a bit less than this due to the next issue)

Sub Country level (state/province)

In the MMMSUUU decimal format there is only a possible number of 10 sub country levels. This is an issue for many countries which have more than 10 sub-regions. What is done here is to invent new country codes, and DMR has invented 8 unique codes:

263 - Germany 
264 - Germany
313 - USA
314 - USA
315 - USA
317 - USA
535 - USA (Guam)
658 - Ascension Island

In total there are only 170 of the 226 MCCs in use by DMR, even adding in the invalid codes, there's 178. This leaves 822 blocks unused, or 8,220,000 IDs.


Totals

In this DMR-MARC scheme presented there is much waste and having to invent new country codes, thus "shoehorning" the DMR ID into something it was not designed for. Adding up the loss below:

 16,777,215 possible IDs in DMR
  6,777,216 - IDs over 10,000,000
  8,220,000 - Unused MCC IDs
     65,535 - CAP Plus ID range
-------------
  1,714,464 - Total DMR ID's available for use

This scheme throws out 90% of the available ID space as unusable! There was a total lack of thought put into this in terms of scalability.

Capacity Plus IDs

LCP or Link Capacity Plus is a controller-less trunking system for >16 sites put forth by Motorola and is quite popular. One of the requirements for this is the DMR ID of the subscribers must be 2^16, not 2^24 or > 65,536. In all Motorola radios, the unit ID is set once, it cannot be changed per system. This means if you're an amateur and want to run a common radio on your LCP work system and the ham bands, you need a Capacity Plus ID.

When it was administered by DMR-MARC a person had to have a valid reason (LCP use) for a low ID, but was still assigned as using MCC as the first 3 digits, thus restricting the availability further. Knowing hams if there was not a restriction they all would want a "Low ID" and go for this, so it's a valid concern to ensure the availability of this for people who need it.

Now the problem is at some point the Brandmeister DMR network stopped importing the low ID's from radioid.net and has capped the allowed ID's. If you have a CPLUS ID in the Brandmeister network, you're allowed to use it, but they will refuse all new ID's. This presents a problem for new users, as Brandmeister will drop any traffic that doesn't have a "valid" ID, but they refuse to import/update their DB. As Brandmeister provides some of the most popular talkgroups, this can lead to further fracturing of the DMR network, and one-way audio where traffic goes via Brandmeister.

I'd suggest Brandmeister start allowing these CPLUS ID's to be passed and allowed on their network. There's not many in use, under 300 total are registered with radioid.net.

Ideas to fix this mess

I'd propose the following features of any number scheme

  • Must be scalable
  • group ID's on national/state boundaries to facilitate importing into radios
  • support sub-delegation of authority

Proposal

Since we're dealing with a 24 bit (3 byte/octet) binary number, we should dispose with the idea of thinking in decimal. If we think how IANA and the RIR's allocate IP space, we can do the same with DMR ID's.

Looking at the number of DMR IDs in the USA:

awk -F\; '$1 !~ /^1/ && length($1) == 7 && $1 ~ /^31[0-5]/ { print }' dmrid.dat |wc
   58574   58574  900443

58574 total ID's for 779626 [1] license amateurs in the US is about 7.5% of all licenses. This is the highest number of DMR ID's of any country.

The UK has the highest number of IDs by percentage, but is still well under the USA in terms of number of ID's in use.

US        7.5% of licenses  58,574 Total IDs 
UK       17.5% of licenses  13,305 Total IDs
Canada    7.1% of licenses   5,000 Total IDs
Germany  14.0% of licenses   8,883 Total IDs

The counts for amateur radio operators per controy were taken from Wikipedia [2]

Per this data, if we look at the top 10 countries there are 1,827,613 total hams (and after this the counts drop off where the rest of countries total are under 200k). If we assume 10% of all hams will have a DMR ID, this is ~183,000 DMR ID's or 18k per country of the top 10. Other than the USA, this is indeed the case.

What I'd propose is using a Bit/Mask for allocating blocks of ID's per country, and each country gets a segment that could be further subdivided.

The world is divided into 4 regions:

  • Americas (North America, South America, Central America, Caribbean)
  • Asia Pacific (Central & South Asia, Northeastern Asia, Southeastern Asia, Australia and Oceania)
  • Europe (Northern Europe, Southern Europe, Eastern Europe, Western Europe)
  • Middle East/Africa (Middle East, Northern Africa, Southern Africa)

We allocate each region on the first 4 bits (nibble) of the DMR ID:

00.00.00 - Americas
10.00.00 - OPEN
20.00.00 - OPEN
30.00.00 - OPEN
40.00.00 - Asia Pacific
50.00.00 - OPEN
60.00.00 - OPEN
70.00.00 - OPEN
80.00.00 - Europe
90.00.00 - OPEN
A0.00.00 - OPEN
B0.00.00 - OPEN
C0.00.00 - Middle East/Africa
D0.00.00 - OPEN
E0.00.00 - OPEN
F0.00.00 - OPEN

This would provide 2^20 addresses per region (1 million) with the ability to expand up to 2 million by moving to a /3 or /2 for 2 or 4 million IDs respectively. Should we get over 4 million in use in a region we can move to allocating an unused block earmarked for another region.

Each region would then allocate as needed to each country.

Americas 0/4 - 000000 - 0FFFFF - allocate on the /8 boundary, but give /10's or 16k blocks to each country:

00.00.00 - Reserved (00.00.00-00.FF.FF is CPLUS ID range) 
01.00.00 - OPEN
02.00.00 - Canada 
03.00.00 - OPEN
04.00.00 - Latin America 
05.00.00 - OPEN 
06.00.00 - OPEN
07.00.00 - OPEN
08.00.00 - USA North East
09.00.00 - USA South East
0A.00.00 - USA West
0B.00.00 - USA Big States
0C.00.00 - OPEN
0D.00.00 - OPEN
0E.00.00 - OPEN
0F.00.00 - OPEN

Now USA's 08.00.00/6 we allocate on the /10 boundary (16,383), but give /12 to each state (4095) Other than the popular states. This can be adjusted easily if needed, and many of the western states could use a smaller pool.

08.00.00/10 - 
            - 08.00.00/12 - Maine
            - 08.10.00/12 - Vermont
            - 08.20.00/12 - New Hampshire
            - 08.30.00/12 - Rhode Island
08.40.00/10 
            - 08.40.00/12 - Connecticut
            - 08.50.00/12 - Pennsylvania 
            - 08.60.00/12 - Maryland 
            - 08.70.00/12 - DC
08.80.00/10 
            - 08.80.00/12 - North Virginia 
            - 08.90.00/12 - Virginia 
            - 08.A0.00/12 - West VA 
            - 08.B0.00/12 - Massachusetts 
08.C0.00/10 - 
            - 08.80.00/11 - NYC Metro 
            -             - NYC Metro 
            - 08.A0.00/12 - NY 
            - 08.B0.00/12 - NJ 

09.00.00/10 - NC, SC, GA, TN
09.40.00/10 - OH, KY, IN, AL
09.80.00/10 - MS, LA, AR, MO
09.C0.00/10 - UNUSED
0A.00.00/10 - UNUSED
0A.40.00/10 - WA, OR, ID, NV, UT
0A.80.00/10 - MO, WY, ND, SD, OK, NE, KS  (smaller than a /12)
0A.C0.00/10 - AZ, NM, TX
0B.00.00/10 - FL
0B.40.00/10 - UNUSED
0B.80.00/10 - CA
0B.C0.00/10 - UNUSED


Advantages to Proposal

If a user wants to program their HT with contacts for a given state, they can simply download the range/mask they want. If they want all Florida they select 0B.00.00/10 which is range 0x0B0000-0B3FFF or decimal 720,896 - 737,279.

Should Florida need to expand they can simply increase their mask size to /9 and combine 0B.00.00/10 and 0B.40.00/10 into 0B.00.00/9.

Regions could manage their own databases. It's quite easy to define a range of 81.00.00/8 to France and let them manage their own ID assignments. They could be carved out and any lookup would know if you see something in 81.00.00-81.FF.FF go it's managed by the regional authority.

Country wide expansion is easy as well, should there not be an adjacent block, we can allocate another block which is not contiguous. IANA does this all the time and it works well. At the projected growth rate of amateur radio DMR this would allow some 50 years of growth if planned well.

Conversion and compatibility. Obviously converting to this method would take some time. Due to the way allocations work in the prior MCC scheme, there would be the ability to move users into anything above 50.00.00/4 immediately, as there are no MCC's above that in use today.


backup data

awk to process dmrid file

#match DMR ID's not starting with 1 and 7 digits long.
awk -F\; '$1 !~ /^1/ && length($1) == 7 { print }' dmrid.dat |wc
  184388  184388 2870086

# process the db, and sort into unique country codes.  
awk -F\; '$1 !~ /^1/ && length($1) == 7 { print substr($1,1,3) }' dmrid.dat | sort | uniq |wc 
     178     178     712
#Compare the unique MCC from the DMR db and the real MCC file 
awk -F";" 'NR==FNR{a[$1]=$1;next} {if (a[$1]) ;else print "Invalid MCC", $1;} ' MCC-Valid.txt dmr-mcc-in-use.txt 
Invalid MCC 263
Invalid MCC 264
Invalid MCC 313
Invalid MCC 314
Invalid MCC 315
Invalid MCC 317
Invalid MCC 535
Invalid MCC 658
  |wc
8      24     128

Python 3 Program

I've written this program to help calculate the DMR ID ranges based on masks. It will take the input as 6 decimal point seperated bytes, and output the ranges in hex and decimal.

$ ./dmr-calc.py  10.00.00/12
DMR ID: 0x100000  Dec: 01048576
Mask  : 0xFFF000  Alt: /12
First : 0x100000  Dec: 01048576
Last  : 0x100FFF  Dec: 01052671
Size  : 0x000FFF  Dec: 00004095

= dmr-calc.py

#!/usr/bin/env python3.8
# Use: ./dmr-calc.py <hh.hh.hh/mask>
# copyright 2021 Bryan Fields bryan@bryanfields.net
# Licensed under the AGPL 3.0
# Vursion 0.90
# this is a PoC for assigning 24 bit DMR ID's in accordance with the
# method proposed in https://wiki.w9cr.net/index.php/DMR_IDs

import sys
if __name__=="__main__":
    id = 0
    mask = 0
    slash = 0
    if len(sys.argv) == 2:
        (id, slash) = sys.argv[1].split('/')
        #split it up if it exists
        id = [int(x, 16) for x in id.split(".")]
        #confert it to int
        id = int((int(id[0]<<16)) + (int(id[1]<<8)) + (int(id[2])))
        slash = int(slash)
        mask = (((1<<24)-1) << (24-slash)) & (int(0xFFFFFF))
    else:
        print("Use: {0} <DMR ID in hh.hh.hh/mask bits>".format(sys.argv[0]))
        sys.exit(-1)
    baseid = (id & mask)
    lastid = (id & mask) | (0xffffff^mask)
    nworksize = (lastid - baseid)
    dmrid = ( str( "%06.0X" % id))
    #print ("dmr ID == %s.%s.%s Decima: %08i" % (dmrid[0:2], dmrid[2:4], dmrid[4:6], id))
    print("DMR ID: 0x%06.0X  Dec: %08i" % (id, id))
    print("Mask  : 0x%06.0X  Alt: /%s" % (mask, slash))
    print("First : 0x%06.0X  Dec: %08i" % (baseid, baseid))
    print("Last  : 0x%06.0X  Dec: %08i" % (lastid, lastid))
    print("Size  : 0x%06.0X  Dec: %08i" % (nworksize, nworksize))

sys.exit()