From 71e588aabafcd6762cb85d6cf86d555217ad57f0 Mon Sep 17 00:00:00 2001 From: David Snyder <37163053+AlwaysLearningTech@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:31:51 -0700 Subject: [PATCH 1/2] Update gb3gf.py Updated for OpenGD77 R2025.03.23.01 --- src/dzcb/gb3gf.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/dzcb/gb3gf.py b/src/dzcb/gb3gf.py index dd97224..e8918c5 100644 --- a/src/dzcb/gb3gf.py +++ b/src/dzcb/gb3gf.py @@ -1,5 +1,6 @@ """ -Write series of CSV files acceptable for import into gb3gf codeplug tool +Write series of CSV files acceptable for import into OpenGD77. This previously used the gb3gf codeplug tool, but the tool has been removed from the web due to integration with OpenGD77. + Ex: OpenGD77 """ @@ -53,20 +54,29 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "Channel Type", "Rx Frequency", "Tx Frequency", + "Bandwidth (kHz)", "Colour Code", "Timeslot", "Contact", "TG List", + "DMR ID", # New entry + "TS1_TA_Tx", # New entry + "TS2_TA_Tx ID", # New entry "RX Tone", "TX Tone", - "Power", - "Bandwidth", "Squelch", + "Power", "Rx Only", "Zone Skip", "All Skip", "TOT", "VOX", + "No Beep", # New entry + "No Eco", # New entry + "APRS", # New entry + "Latitude", # New entry + "Longitude", # New entry + "Use Location" # New entry ] with open("{}/Channels.csv".format(output_dir), "w", newline="") as f: csvw = csv.DictWriter(f, channel_fields, delimiter=";") @@ -89,6 +99,9 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "Colour Code": channel.color_code, "Contact": "N/A", "TG List": channel.grouplist_name(cp) if channel.grouplist else "None", + "DMR ID": "None", + "TS1_TA_Tx": "Off", + "TS2_TA_Tx ID": "Off" } if channel.talkgroup: d["Contact"] = channel.talkgroup.name_with_timeslot @@ -101,13 +114,19 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "Tx Frequency": round(channel.frequency + channel.offset, 5), "Timeslot": 1, "Power": str(channel.power), - "Bandwidth": channel.bandwidth.flattened([Bandwidth._25, Bandwidth._125]).value + "KHz", + "Bandwidth (kHz)": channel.bandwidth.flattened([Bandwidth._25, Bandwidth._125]).value + "KHz", "Squelch": str(channel.squelch) if channel.squelch else "Disabled", "Rx Only": value_replacements[channel.rx_only], "Zone Skip": "No", "All Skip": "No", "TOT": 90, "VOX": "No", + "No Beep": "Yes", + "No Eco": "No", + "APRS": "None", + "Latitude": "None", + "Longitude": "None", + "Use Location": "Yes" } ) csvw.writerow(d) From 497c879001ddbdb738a4a3072388846f7ef818ec Mon Sep 17 00:00:00 2001 From: David Snyder <37163053+AlwaysLearningTech@users.noreply.github.com> Date: Sat, 9 Aug 2025 15:38:31 -0700 Subject: [PATCH 2/2] Updated --- src/dzcb/gb3gf.py | 30 +++++++++++++++--------------- src/dzcb/k7abd.py | 16 ++++++++++++++++ src/dzcb/model.py | 21 +++++++++++++++++++++ src/dzcb/repeaterbook.py | 2 ++ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/dzcb/gb3gf.py b/src/dzcb/gb3gf.py index e8918c5..e01a4a8 100644 --- a/src/dzcb/gb3gf.py +++ b/src/dzcb/gb3gf.py @@ -59,9 +59,9 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "Timeslot", "Contact", "TG List", - "DMR ID", # New entry - "TS1_TA_Tx", # New entry - "TS2_TA_Tx ID", # New entry + "DMR ID", + "TS1_TA_Tx", + "TS2_TA_Tx ID", "RX Tone", "TX Tone", "Squelch", @@ -71,15 +71,15 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "All Skip", "TOT", "VOX", - "No Beep", # New entry - "No Eco", # New entry - "APRS", # New entry - "Latitude", # New entry - "Longitude", # New entry - "Use Location" # New entry + "No Beep", + "No Eco", + "APRS", + "Latitude", + "Longitude", + "Use Location" ] with open("{}/Channels.csv".format(output_dir), "w", newline="") as f: - csvw = csv.DictWriter(f, channel_fields, delimiter=";") + csvw = csv.DictWriter(f, channel_fields, delimiter=",") csvw.writeheader() for ix, channel in enumerate(cp.channels): if isinstance(channel, AnalogChannel): @@ -124,15 +124,15 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): "No Beep": "Yes", "No Eco": "No", "APRS": "None", - "Latitude": "None", - "Longitude": "None", + "Latitude": channel.latitude, + "Longitude": channel.longitude, "Use Location": "Yes" } ) csvw.writerow(d) tg_fields = ["TG List Name"] + ["Contact {}".format(x) for x in range(1, 33)] with open("{}/TG_Lists.csv".format(output_dir), "w", newline="") as f: - csvw = csv.DictWriter(f, tg_fields, delimiter=";") + csvw = csv.DictWriter(f, tg_fields, delimiter=",") csvw.writeheader() n_grouplists = len(cp.grouplists) for gl in cp.grouplists: @@ -154,7 +154,7 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): csvw.writerow(tg_list) zone_fields = ["Zone Name"] + ["Channel {}".format(x) for x in range(1, 81)] with open("{}/Zones.csv".format(output_dir), "w", newline="") as f: - csvw = csv.DictWriter(f, zone_fields, delimiter=";") + csvw = csv.DictWriter(f, zone_fields, delimiter=",") csvw.writeheader() zone_names = [z.name for z in cp.zones] # OpenGD77 doesn't have scanlist, so simulate it with separate zones @@ -170,7 +170,7 @@ def Codeplug_to_gb3gf_opengd77_csv(cp, output_dir): csvw.writerow(row) with open("{}/Contacts.csv".format(output_dir), "w", newline="") as f: csvw = csv.DictWriter( - f, ["Contact Name", "ID", "ID Type", "TS Override"], delimiter=";" + f, ["Contact Name", "ID", "ID Type", "TS Override"], delimiter="," ) csvw.writeheader() for tg in sorted(contacts, key=lambda c: c.name_with_timeslot): diff --git a/src/dzcb/k7abd.py b/src/dzcb/k7abd.py index 0249459..ac6f099 100644 --- a/src/dzcb/k7abd.py +++ b/src/dzcb/k7abd.py @@ -54,6 +54,8 @@ CTCSS_DECODE = "CTCSS Decode" CTCSS_ENCODE = "CTCSS Encode" TX_PROHIBIT = "TX Prohibit" +LATITUDE = "Latitude" +LONGITUDE = "Longitude" ANALOG_CSV_FIELDS = [ ZONE, CHANNEL_NAME, @@ -64,6 +66,8 @@ CTCSS_DECODE, CTCSS_ENCODE, TX_PROHIBIT, + LATITUDE, + LONGITUDE, ] @@ -156,6 +160,8 @@ def Analog_from_csv(analog_repeaters_csv): frequency = float(r[RX_FREQ]) offset = round(float(r[TX_FREQ]) - frequency, 1) power = r[POWER] + latitude = float(r[LATITUDE]) + longitude = float(r[LONGITUDE]) bandwidth = r[BANDWIDTH].rstrip("K") tone_encode = ( r[CTCSS_ENCODE] if r[CTCSS_ENCODE].lower() not in ("off", "") else None @@ -173,6 +179,8 @@ def Analog_from_csv(analog_repeaters_csv): tone_decode=tone_decode, power=power, bandwidth=bandwidth, + latitude=latitude, + longitude=longitude, ) ) except ValueError as ve: @@ -204,6 +212,8 @@ def DigitalRepeaters_from_k7abd_csv(digital_repeaters_csv, talkgroups_by_name): offset = round(float(r.pop("TX Freq")) - frequency, 1) color_code = r.pop("Color Code") power = r.pop("Power") + latitude = float(r.pop("Latitude")) + longitude = float(r.pop("Longitude")) talkgroups = [] for tg_name, timeslot in r.items(): if timeslot.strip() == "-": @@ -235,6 +245,8 @@ def DigitalRepeaters_from_k7abd_csv(digital_repeaters_csv, talkgroups_by_name): offset=offset, color_code=color_code, power=power, + latitude=latitude, + longitude=longitude, static_talkgroups=sorted(talkgroups, key=lambda tg: tg.name), ) yield repeater @@ -258,6 +270,8 @@ def DigitalChannels_from_k7abd_csv(digital_others_csv, talkgroups_by_name): offset = round(float(r.pop("TX Freq")) - frequency, 1) color_code = r.pop("Color Code") power = r.pop("Power") + latitude = float(r.pop("Latitude")) + longitude = float(r.pop("Longitude")) tg_name = r.pop("Talk Group") try: talkgroup = Talkgroup.from_contact( @@ -280,6 +294,8 @@ def DigitalChannels_from_k7abd_csv(digital_others_csv, talkgroups_by_name): offset=offset, color_code=color_code, power=power, + latitude=latitude, + longitude=longitude, talkgroup=talkgroup, ) ) diff --git a/src/dzcb/model.py b/src/dzcb/model.py index e964a36..769668c 100644 --- a/src/dzcb/model.py +++ b/src/dzcb/model.py @@ -211,6 +211,24 @@ class Channel: rx_only = attr.ib( default=False, validator=attr.validators.instance_of(bool), converter=bool ) + no_beep = attr.ib( + default=True, validator=attr.validators.instance_of(bool), converter=bool + ) + no_eco = attr.ib( + default=False, validator=attr.validators.instance_of(bool), converter=bool + ) + arps = attr.ib( + default=False, validator=attr.validators.instance_of(bool), converter=bool + ) + latitude = attr.ib( + default=None, validator=attr.validators.instance_of(float), converter=float + ) + longitude = attr.ib( + default=None, validator=attr.validators.instance_of(float), converter=float + ) + use_location = attr.ib( + default=True, validator=attr.validators.instance_of(bool), converter=bool + ) scanlist = attr.ib( eq=False, default=None, @@ -293,6 +311,9 @@ class DigitalChannel(Channel): bandwidth = Bandwidth._125 squelch = 0 color_code = attr.ib(default=1) + dmr_id = attr.ib(default=None) + ts1_ta_tx = attr.ib(default=None) + ts2_ta_tx_id = attr.ib(default=None) grouplist = attr.ib( default=None, validator=attr.validators.optional(attr.validators.instance_of(uuid.UUID)), diff --git a/src/dzcb/repeaterbook.py b/src/dzcb/repeaterbook.py index 885b12b..7f4685d 100644 --- a/src/dzcb/repeaterbook.py +++ b/src/dzcb/repeaterbook.py @@ -182,6 +182,8 @@ def repeater_to_k7abd_row(repeater, zone_name, name_format=None): k7abd.CTCSS_DECODE: normalize_tone(repeater["TSQ"]), k7abd.CTCSS_ENCODE: normalize_tone(repeater["PL"]), k7abd.TX_PROHIBIT: k7abd.OFF, + k7abd.LATITUDE: repeater["Lat"], + k7abd.LONGITUDE: repeater["Long"], }