RepeaterBook CSV to CPS CSV converter

OpenGD77CPS
KJ5DFL
Posts: 13
Joined: Mon May 06, 2024 3:46 am

RepeaterBook CSV to CPS CSV converter

Post by KJ5DFL » Tue May 07, 2024 3:57 am

Hey all! I was getting tired of manually typing in all my channels, so I put together a little script to automatically take a Repeaterbook CSV export and convert it into Zones and Channels for the CPS to import. Zones are based on the county that's associated with each channel, so it's mostly location based. This does DMR and Analog repeaters, and copies locations over. It also filters out any repeaters with the status listed as unknown or offline, or any repeaters that aren't analog or DMR.

Go to repeaterbook.com, open the repeater listing for whatever category you want (I usually go by bands, as the script automatically sorts out all the modes) and then Export > CSV. If you want to put together multiple listings, just combine the files, but remember to only have one header. Also check that the downloaded CSV file doesn't have any PHP errors (stuff like <br>) at the top. If it does, just delete those lines.
Usage is pretty simple, albeit only tested on Linux. Just mark it as executable (chmod +x convert.py) and then run it and give the path of your Repeaterbook CSV. My command would be: ./convert.py repeaterbookfull.csv while Windows would probably look like python convert.py repeaterbook.csv
Take the two output files, Channels.csv and Zones.csv, don't rename them, and put them in a directory. In the CPS, press Ctrl-I and it'll ask you to point it at the folder you put the files into. Hit OK and it'll pull them in and give you a summary of what it did to your codeplug. From here, make whatever modifications you like, and upload it normally.
See you on the air :). 73 KJ5DFL

Script (just copy-paste this into a file. i saved mine as convert.py) (please don't bully me lol, i know this is awful):

Code: Select all

#!/usr/bin/python
import sys 

def shorten(string):
    return string.replace("a", "").replace("e", "").replace("i", "").replace("o", "").replace("u", "").replace(" ", "").replace("-", " ")

#print(sys.argv)
chanfile = open(sys.argv[1])
chanfiledata = chanfile.read()
chanfile.close()
chans = chanfiledata.split("\n")
splitchans = []
for i in chans:
    if ((("DMR" not in i) and ("Analog" not in i) and ("analog" not in i)) or ("On-Air" not in i) or ("OPEN" not in i)):
        continue
    toapp = i.split(",")
    toapp[5] = toapp[5].replace('"', "")
    splitchans.append(toapp)
#print(chans[0])
#print(splitchans)
#print(splitchans[1])

outchan = open("Channels.csv", "w")
outzones = open("Zones.csv", "w")
counties = {}
channum = 1
outchan.write("Channel Number,Channel Name,Channel Type,Rx Frequency,Tx Frequency,Bandwidth (kHz),Colour Code,Timeslot,Contact,TG List,DMR ID,TS1_TA_Tx,TS2_TA_Tx ID,RX Tone,TX Tone,Squelch,Power,Rx Only,Zone Skip,All Skip,TOT,VOX,No Beep,No Eco,APRS,Latitude,Longitude\n")
for i in splitchans:
    if "analog" in i[12]:
        # Channel number
        outchan.write(str(channum) + ",")
        channum += 1
        # Channel name.
        channame = str(i[9] + "-" + shorten(i[5]))
        channame = channame[:14]
        channame = channame.strip()
        outchan.write(channame + ",")
        # Assign the channel to a zone, and check if there's a zone for its county.
        countyname = i[6].replace(" ","")
        countyname = countyname[:14]
        #print(countyname,channame)
        try:
            counties[countyname].append(channame)
        except KeyError:
            counties[countyname] = []
            counties[countyname].append(channame)

        # Channel type. We've already checked the type if we're running this codeblock.
        outchan.write("Analogue,")
        # Channel frequencies, RX then TX
        outchan.write(str(i[0]) + "," + str(i[1]) + ",")
        # Channel bandwidth. Should always be 25khz.
        outchan.write("25,")
        # Color code, Timeslot, Contact, Talkgroup, DMR ID, and some other nonsense that doesn't matter here.
        outchan.write(",,,,,,,")
        # Tones. We have to make sure an uplink tone is set, and I just don't use downlink tones. 
        if i[3] == "" or i[3] == "CSQ":
            uptone = "None"
        else:
            uptone = i[3]
        outchan.write(f"None,{uptone},")
        # Squelch. "Disabled" just means to use the default setting.
        outchan.write("Disabled,")
        # Power. Master just means to use default.
        outchan.write("Master,")
        # Some other stuff like RX only, Scan skips, TOT, VOX. I turn it all off, but set TOT to 180s.
        outchan.write("No,No,No,180,Off,No,No,None,")
        # Coordinates and end.
        outchan.write(f"{i[7]},{i[8]}\n")
    if "DMR" in i[12]:
        # Channel number
        outchan.write(str(channum) + ",")
        channum += 1
        # Channel name.
        channame = str(i[9] + "-" + shorten(i[5]))
        channame = channame[:15]
        channame = channame.strip()
        outchan.write(channame + ",")
        # Assign the channel to a zone, and check if there's a zone for its county.
        countyname = i[6].replace(" ","")
        countyname = countyname[:14]
        #print(countyname,channame)
        try:
            counties[countyname].append(channame)
        except KeyError:
            counties[countyname] = []
            counties[countyname].append(channame)
        # Channel type. We've already checked the type if we're running this codeblock.
        outchan.write("Digital,")
        # Channel frequencies, RX then TX
        outchan.write(str(i[0]) + "," + str(i[1]) + ",")
        # Channel bandwidth. Not set on digital stations.
        outchan.write(",")
        # All the DMR stuff. Color code to TS2_TA_Tx ID. Don't ask me.
        outchan.write(f"{i[13]},1,None,None,None,Off,Off,")
        # Tones, Squelch.
        outchan.write(",,,")
        # Power. Master just means to use default.
        outchan.write("Master,")
        # Some other stuff like RX only, Scan skips, TOT, VOX. I turn it all off, but set TOT to 180s.
        outchan.write("No,No,No,180,Off,No,No,None,")
        # Coordinates and end.
        outchan.write(f"{i[7]},{i[8]}\n")

# Let's set up zones. Unlike the last part that was awful because I'm awful at programming, this
# is awful because I'm bad at programming _and_ the format is awful. Yay.

outzones.write("Zone Name,Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,Channel11,Channel12,Channel13,Channel14,Channel15,Channel16,Channel17,Channel18,Channel19,Channel20,Channel21,Channel22,Channel23,Channel24,Channel25,Channel26,Channel27,Channel28,Channel29,Channel30,Channel31,Channel32,Channel33,Channel34,Channel35,Channel36,Channel37,Channel38,Channel39,Channel40,Channel41,Channel42,Channel43,Channel44,Channel45,Channel46,Channel47,Channel48,Channel49,Channel50,Channel51,Channel52,Channel53,Channel54,Channel55,Channel56,Channel57,Channel58,Channel59,Channel60,Channel61,Channel62,Channel63,Channel64,Channel65,Channel66,Channel67,Channel68,Channel69,Channel70,Channel71,Channel72,Channel73,Channel74,Channel75,Channel76,Channel77,Channel78,Channel79,Channel80\n")
for i in counties:
    print(f"{i} {len(counties[i])}")
    outzones.write(f"{i},")
    print(i)
    counter = 0
    print(counties[i])
    for b in counties[i]:
        print(b)
        outzones.write(f"{b},")
        counter += 1
    for b in range(79 - counter):
        outzones.write(",")
    outzones.write("\n")
Last edited by KJ5DFL on Tue Jun 04, 2024 8:59 pm, edited 2 times in total.

PU2URT
Posts: 7
Joined: Mon Feb 20, 2023 3:09 pm

Re: RepeaterBook CSV to CPS CSV converter

Post by PU2URT » Thu May 09, 2024 5:47 pm

Thank you for that!

User avatar
W0AMT
Posts: 3
Joined: Tue May 14, 2024 2:48 am

Re: RepeaterBook CSV to CPS CSV converter

Post by W0AMT » Tue May 14, 2024 5:16 pm

This looks great. I will try it out today.

KJ5DFL
Posts: 13
Joined: Mon May 06, 2024 3:46 am

Re: RepeaterBook CSV to CPS CSV converter

Post by KJ5DFL » Thu May 16, 2024 1:10 am

Update time! There were some weird issues with zone stuff but I changed some of the code so it should be fine now? If you ever see any empty zones or channels that aren't in any zones please tell me, there's probably something wrong.

User avatar
DO3THM
Posts: 9
Joined: Wed Jun 19, 2024 1:06 pm
Location: JO64je

Re: RepeaterBook CSV to CPS CSV converter

Post by DO3THM » Thu Jun 20, 2024 6:50 pm

Great work, but the script doesn't work with "rest of world" repeaters because the repeater type is missing in the CSV export.

KJ5DFL
Posts: 13
Joined: Mon May 06, 2024 3:46 am

Re: RepeaterBook CSV to CPS CSV converter

Post by KJ5DFL » Thu Jun 20, 2024 8:42 pm

DO3THM wrote:
Thu Jun 20, 2024 6:50 pm
Great work, but the script doesn't work with "rest of world" repeaters because the repeater type is missing in the CSV export.
Ah, damnit! I didn't even test that, sorry. I'll go have a look, shouldn't be too hard I hope.

KJ5DFL
Posts: 13
Joined: Mon May 06, 2024 3:46 am

Re: RepeaterBook CSV to CPS CSV converter

Post by KJ5DFL » Thu Jun 20, 2024 8:58 pm

DO3THM wrote:
Thu Jun 20, 2024 6:50 pm
Great work, but the script doesn't work with "rest of world" repeaters because the repeater type is missing in the CSV export.
Actually, I'm trying to pull down some CSVs from RepeaterBook for RoW repeaters but all the CSV files are just empty; would you mind giving me one of the files you're trying to use, and if you tried running it what error messages it spits out? Thanks.

User avatar
DO3THM
Posts: 9
Joined: Wed Jun 19, 2024 1:06 pm
Location: JO64je

Re: RepeaterBook CSV to CPS CSV converter

Post by DO3THM » Thu Jun 20, 2024 9:19 pm

KJ5DFL wrote:
Thu Jun 20, 2024 8:58 pm
would you mind giving me one of the files you're trying to use, and if you tried running it what error messages it spits out? Thanks.
The CSV files didn't contain the column with the repeater type (i.e. Analog / Digital) - you cannot fix it :oops: .

In the meantime I wrote my own script based on the Radioddity (GD77) export format. I have to change the delimiter from ',' to ';' and the locale to German (decimal point is ',') and to append 3 columns (aprs, longitude, latitude).

User avatar
DO3THM
Posts: 9
Joined: Wed Jun 19, 2024 1:06 pm
Location: JO64je

Re: RepeaterBook CSV to CPS CSV converter

Post by DO3THM » Mon Jun 24, 2024 8:49 pm

The last evenings I wrote my own converter. It is based on the repeaterbook API and depends not on CSV exports. It is mainly for my (German = Outside of North America) needs and based on this locale. Feel free to download the code and customize it for your requirements.

KJ5DFL
Posts: 13
Joined: Mon May 06, 2024 3:46 am

Re: RepeaterBook CSV to CPS CSV converter

Post by KJ5DFL » Tue Jun 25, 2024 1:42 am

DO3THM wrote:
Mon Jun 24, 2024 8:49 pm
The last evenings I wrote my own converter. It is based on the repeaterbook API and depends not on CSV exports. It is mainly for my (German = Outside of North America) needs and based on this locale. Feel free to download the code and customize it for your requirements.
Hey, nice work! Much cleaner than my awful bitbanging :P
You should also go ahead and create your own thread, would probably make it easier to find.

Post Reply