From 8ccecfb4cc939722dbe52bb08bdbb40d620d0244 Mon Sep 17 00:00:00 2001 From: sq5bpf Date: Tue, 13 Jun 2023 11:11:16 +0200 Subject: [PATCH] add scanlist support --- uvk5.py | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 164 insertions(+), 12 deletions(-) diff --git a/uvk5.py b/uvk5.py index 3491bb2..e2a404d 100644 --- a/uvk5.py +++ b/uvk5.py @@ -47,7 +47,7 @@ DEBUG_SHOW_OBFUSCATED_COMMANDS = False # might be useful for someone debugging some obscure memory issue DEBUG_SHOW_MEMORY_ACTIONS = False -DRIVER_VERSION = "Quansheng UV-K5 driver v20230610 (c) Jacek Lipkowski SQ5BPF" +DRIVER_VERSION = "Quansheng UV-K5 driver v20230613 (c) Jacek Lipkowski SQ5BPF" PRINT_CONSOLE = False MEM_FORMAT = """ @@ -66,7 +66,14 @@ struct { } channel[214]; #seekto 0xd60; -u8 channel_attributes[200]; +struct { +u8 is_scanlist1:1, +is_scanlist2:1, +unknown1:1, +unknown2:1, +is_free:1, +band:3; +} channel_attributes[200]; #seekto 0xe40; ul16 fmfreq[20]; @@ -109,6 +116,17 @@ u8 repeater_tail_elimination; char logo_line1[16]; char logo_line2[16]; +#seekto 0xf18; +u8 scanlist_default; +u8 scanlist1_priority_scan; +u8 scanlist1_priority_ch1; +u8 scanlist1_priority_ch2; +u8 scanlist2_priority_scan; +u8 scanlist2_priority_ch1; +u8 scanlist2_priority_ch2; +u8 scanlist_unknown_0xff; + + #seekto 0xf40; u8 int_flock; u8 int_350tx; @@ -263,6 +281,8 @@ VFO_CHANNEL_NAMES = ["F1(50M-76M)A", "F1(50M-76M)B", "F6(400M-470M)A", "F6(400M-470M)B", "F7(470M-600M)A", "F7(470M-600M)B"] +SCANLIST_LIST = ["None", "1", "2", "1+2"] + # the communication is obfuscated using this fine mechanism def xorarr(data: bytes): @@ -516,7 +536,6 @@ class UVK5Radio(chirp_common.CloneModeRadio): VENDOR = "Quansheng" MODEL = "UV-K5" BAUD_RATE = 38400 - NEEDS_COMPAT_SERIAL = False FIRMWARE_VERSION = "" FIRMWARE_NOLIMITS = False @@ -530,7 +549,7 @@ class UVK5Radio(chirp_common.CloneModeRadio): 'the memory image from the radio with chirp or k5prog ' 'and keep it. This can be later used to recover the ' 'original settings. \n\n' - 'FM radio, DTMF settings and scanlists are not yet implemented') + 'DTMF settings and other details are not yet implemented') rp.pre_download = _( "1. Turn radio on.\n" "2. Connect cable to mic/spkr connector.\n" @@ -681,7 +700,6 @@ class UVK5Radio(chirp_common.CloneModeRadio): # Extract a high-level memory object from the low-level memory map # This is called to populate a memory in the UI - def get_memory(self, number2): number = number2-1 # in the radio memories start with 0 @@ -704,11 +722,21 @@ class UVK5Radio(chirp_common.CloneModeRadio): if (_mem.freq == 0xffffffff) or (_mem.freq == 0): is_empty = True + tmpscn = SCANLIST_LIST[0] + # We'll also look at the channel attributes if a memory has them if number < 200: _mem3 = self._memobj.channel_attributes[number] - if _mem3 & 0x08 > 0: + # free memory bit + if _mem3.is_free > 0: is_empty = True + # scanlists + if _mem3.is_scanlist1 > 0 and _mem3.is_scanlist2 > 0: + tmpscn = SCANLIST_LIST[3] # "1+2" + elif _mem3.is_scanlist1 > 0: + tmpscn = SCANLIST_LIST[1] # "1" + elif _mem3.is_scanlist2 > 0: + tmpscn = SCANLIST_LIST[2] # "2" if is_empty: mem.empty = True @@ -730,6 +758,10 @@ class UVK5Radio(chirp_common.CloneModeRadio): SCRAMBLER_LIST, SCRAMBLER_LIST[0])) mem.extra.append(rs) + rs = RadioSetting("scanlists", "Scanlists", RadioSettingValueList( + SCANLIST_LIST, SCANLIST_LIST[0])) + mem.extra.append(rs) + # actually the step and duplex are overwritten by chirp based on # bandplan. they are here to document sane defaults for IARU r1 # mem.tuning_step = 25.0 @@ -739,7 +771,7 @@ class UVK5Radio(chirp_common.CloneModeRadio): if number > 199: mem.name = VFO_CHANNEL_NAMES[number-200] - mem.immutable = ["name"] + mem.immutable = ["name", "scanlists"] else: _mem2 = self._memobj.channelname[number] for char in _mem2.name: @@ -837,6 +869,12 @@ class UVK5Radio(chirp_common.CloneModeRadio): mem.extra.append(rs) tmpcomment += "Scrambler:"+SCRAMBLER_LIST[enc]+" " + # scanlists + pttid = (_mem.dtmf_flags & FLAGS_DTMF_PTTID_MASK) >> 1 + rs = RadioSetting("scanlists", "Scanlists", RadioSettingValueList( + SCANLIST_LIST, tmpscn)) + mem.extra.append(rs) + return mem def set_settings(self, settings): @@ -986,15 +1024,105 @@ class UVK5Radio(chirp_common.CloneModeRadio): # "in the range %.1f - %.1f" % (FMMIN , FMMAX)) _mem.fmfreq[i-1] = val2 + # scanlist stuff + if element.get_name() == "scanlist_default": + val = (int(element.value) == 2) and 1 or 0 + _mem.scanlist_default = val + + if element.get_name() == "scanlist1_priority_scan": + _mem.scanlist1_priority_scan = \ + element.value and 1 or 0 + + if element.get_name() == "scanlist2_priority_scan": + _mem.scanlist2_priority_scan = \ + element.value and 1 or 0 + + if element.get_name() == "scanlist1_priority_ch1" or \ + element.get_name() == "scanlist1_priority_ch2" or \ + element.get_name() == "scanlist2_priority_ch1" or \ + element.get_name() == "scanlist2_priority_ch2": + + val = int(element.value) + + if val > 200 or val < 1: + val = 0xff + else: + val -= 1 + + if element.get_name() == "scanlist1_priority_ch1": + _mem.scanlist1_priority_ch1 = val + if element.get_name() == "scanlist1_priority_ch2": + _mem.scanlist1_priority_ch2 = val + if element.get_name() == "scanlist2_priority_ch1": + _mem.scanlist2_priority_ch1 = val + if element.get_name() == "scanlist2_priority_ch2": + _mem.scanlist2_priority_ch2 = val + def get_settings(self): _mem = self._memobj basic = RadioSettingGroup("basic", "Basic Settings") + scanl = RadioSettingGroup("scn", "Scan Lists") unlock = RadioSettingGroup("unlock", "Unlock Settings") fmradio = RadioSettingGroup("fmradio", "FM Radio") roinfo = RadioSettingGroup("roinfo", "Driver information") - top = RadioSettings(basic, unlock, fmradio, roinfo) + top = RadioSettings(basic, scanl, unlock, fmradio, roinfo) + + if _mem.scanlist_default == 1: + tmpsc = 2 + else: + tmpsc = 1 + rs = RadioSetting("scanlist_default", + "Default scanlist", + RadioSettingValueInteger(1, 2, tmpsc)) + scanl.append(rs) + + tmppr = bool((_mem.scanlist1_priority_scan & 1) > 0) + rs = RadioSetting( + "scanlist1_priority_scan", + "Scanlist 1 priority channel scan", + RadioSettingValueBoolean(tmppr)) + scanl.append(rs) + + tmpch = _mem.scanlist1_priority_ch1 + 1 + if tmpch > 200: + tmpch = 0 + rs = RadioSetting("scanlist1_priority_ch1", + "Scanlist 1 priority channel 1 (0 - off)", + RadioSettingValueInteger(0, 200, tmpch)) + scanl.append(rs) + + tmpch = _mem.scanlist1_priority_ch2 + 1 + if tmpch > 200: + tmpch = 0 + rs = RadioSetting("scanlist1_priority_ch2", + "Scanlist 1 priority channel 2 (0 - off)", + RadioSettingValueInteger(0, 200, tmpch)) + scanl.append(rs) + + tmppr = bool((_mem.scanlist2_priority_scan & 1) > 0) + rs = RadioSetting( + "scanlist2_priority_scan", + "Scanlist 2 priority channel scan", + RadioSettingValueBoolean(tmppr)) + scanl.append(rs) + + tmpch = _mem.scanlist2_priority_ch1 + 1 + if tmpch > 200: + tmpch = 0 + rs = RadioSetting("scanlist2_priority_ch1", + "Scanlist 2 priority channel 1 (0 - off)", + RadioSettingValueInteger(0, 200, tmpch)) + scanl.append(rs) + + tmpch = _mem.scanlist2_priority_ch2 + 1 + if tmpch > 200: + tmpch = 0 + rs = RadioSetting("scanlist2_priority_ch2", + "Scanlist 2 priority channel 2 (0 - off)", + RadioSettingValueInteger(0, 200, tmpch)) + scanl.append(rs) # basic settings @@ -1298,7 +1426,12 @@ class UVK5Radio(chirp_common.CloneModeRadio): if number < 200: _mem2 = self._memobj.channelname[number] _mem2.set_raw("\xFF" * 16) - _mem4.channel_attributes[number] = 0x0f + _mem4.channel_attributes[number].is_scanlist1 = 0 + _mem4.channel_attributes[number].is_scanlist2 = 0 + _mem4.channel_attributes[number].unknown1 = 0 + _mem4.channel_attributes[number].unknown2 = 0 + _mem4.channel_attributes[number].is_free = 1 + _mem4.channel_attributes[number].band = 0x7 return mem # clean the channel memory, restore some bits if it was used before @@ -1319,7 +1452,12 @@ class UVK5Radio(chirp_common.CloneModeRadio): chr(prev_0d) + chr(prev_0e) + chr(prev_0f)) if number < 200: - _mem4.channel_attributes[number] = 0x0f + _mem4.channel_attributes[number].is_scanlist1 = 0 + _mem4.channel_attributes[number].is_scanlist2 = 0 + _mem4.channel_attributes[number].unknown1 = 0 + _mem4.channel_attributes[number].unknown2 = 0 + _mem4.channel_attributes[number].is_free = 1 + _mem4.channel_attributes[number].band = 0x7 # find tx frequency if mem.duplex == '-': @@ -1370,8 +1508,8 @@ class UVK5Radio(chirp_common.CloneModeRadio): # set band if number < 200: - _mem4.channel_attributes[number] = ( - _mem4.channel_attributes[number] & ~BANDMASK) | band + _mem4.channel_attributes[number].is_free = 0 + _mem4.channel_attributes[number].band = band # channels >200 are the 14 VFO chanells and don't have names if number < 200: @@ -1426,6 +1564,20 @@ class UVK5Radio(chirp_common.CloneModeRadio): _mem.scrambler = ( _mem.scrambler & 0xf0) | SCRAMBLER_LIST.index(svalue) + if number < 200 and sname == "scanlists": + if svalue == "1": + _mem4.channel_attributes[number].is_scanlist1 = 1 + _mem4.channel_attributes[number].is_scanlist2 = 0 + elif svalue == "2": + _mem4.channel_attributes[number].is_scanlist1 = 0 + _mem4.channel_attributes[number].is_scanlist2 = 1 + elif svalue == "1+2": + _mem4.channel_attributes[number].is_scanlist1 = 1 + _mem4.channel_attributes[number].is_scanlist2 = 1 + else: + _mem4.channel_attributes[number].is_scanlist1 = 0 + _mem4.channel_attributes[number].is_scanlist2 = 0 + return mem