From 4a36fc662e0b99c03582993a64e1d22c40e6270f Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 17:07:37 -0700 Subject: [PATCH 1/6] anytone778uv: Fix incorrect DTCS handling This driver has been broken forever in how it handles DTCS tone mode. It was always using the rx_dtcs field, which should only be honored in a ->DTCS cross mode. The new GUI, being in hide-unused-fields mode always made this clear, as it appeared to be snapping back to 023 always when the user selected a different value. Note that the test is *also* broken, which I will fix at the end of this series. Fixes: #11506 --- chirp/drivers/anytone778uv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chirp/drivers/anytone778uv.py b/chirp/drivers/anytone778uv.py index c662b6d69..93fbef023 100644 --- a/chirp/drivers/anytone778uv.py +++ b/chirp/drivers/anytone778uv.py @@ -1040,10 +1040,10 @@ def set_memory(self, mem): elif mem.tmode == 'DTCS': _mem.dtcs_encode_en = 1 _mem.dtcs_encode_code, _mem.dtcs_encode_code_highbit = \ - dtcs_code_val_to_bits(mem.rx_dtcs) + dtcs_code_val_to_bits(mem.dtcs) _mem.dtcs_decode_en = 1 _mem.dtcs_decode_code, _mem.dtcs_decode_code_highbit = \ - dtcs_code_val_to_bits(mem.rx_dtcs) + dtcs_code_val_to_bits(mem.dtcs) _mem.tone_squelch_en = 1 elif mem.tmode == 'Cross': txmode, rxmode = mem.cross_mode.split('->') From cb39845a63fda869e37ff1dc7b2c1e1b50524a62 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 17:09:21 -0700 Subject: [PATCH 2/6] ft4: Fix incorrect DTCS handling This driver has been broken forever in how it handles DTCS tone mode. It was always using the rx_dtcs field, which should only be honored in a ->DTCS cross mode. The new GUI, being in hide-unused-fields mode always made this clear, as it appeared to be snapping back to 023 always when the user selected a different value. Note that the test is *also* broken, which I will fix at the end of this series. Related to #11506 --- chirp/drivers/ft4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chirp/drivers/ft4.py b/chirp/drivers/ft4.py index c748e9512..ec0a63f7e 100644 --- a/chirp/drivers/ft4.py +++ b/chirp/drivers/ft4.py @@ -1030,7 +1030,7 @@ def encode_sql(self, mem, chan): if mem.tmode == "TSQL": chan.tx_ctcss = chan.rx_ctcss # CHIRP uses ctone for TSQL if mem.tmode == "DTCS": - chan.tx_dcs = chan.rx_dcs # CHIRP uses rx_dtcs for DTCS + chan.rx_dcs = chan.tx_dcs # CHIRP uses rx_dtcs for DTCS # select the correct internal dictionary and key mode_dict, key = [ (TONE_DICT, mem.tmode), From e5a18ea3d87e8d7892e1702aa6111cc2fede0de4 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 17:09:52 -0700 Subject: [PATCH 3/6] ft857: Fix incorrect DTCS handling This driver has been broken forever in how it handles DTCS tone mode. It was always using the rx_dtcs field, which should only be honored in a ->DTCS cross mode. The new GUI, being in hide-unused-fields mode always made this clear, as it appeared to be snapping back to 023 always when the user selected a different value. Note that the test is *also* broken, which I will fix at the end of this series. Related to #11506 --- chirp/drivers/ft857.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chirp/drivers/ft857.py b/chirp/drivers/ft857.py index 57acf9b11..13e13f0da 100644 --- a/chirp/drivers/ft857.py +++ b/chirp/drivers/ft857.py @@ -514,7 +514,7 @@ def _set_tmode(self, mem, _mem): # this code has already been tested _mem.dcs = _mem.rxdcs = chirp_common.DTCS_CODES.index(mem.dtcs) elif mem.tmode == "DTCS": - _mem.dcs = _mem.rxdcs = chirp_common.DTCS_CODES.index(mem.rx_dtcs) + _mem.dcs = _mem.rxdcs = chirp_common.DTCS_CODES.index(mem.dtcs) elif mem.tmode == "Cross": _mem.tone = chirp_common.TONES.index(mem.rtone) _mem.rxtone = chirp_common.TONES.index(mem.ctone) From 14c33f1775831b4aff52a4437a0714f6c8865aec Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 17:10:04 -0700 Subject: [PATCH 4/6] Fix test_dtcs and test_cross skipping This test has been wrong for a long time, basically never properly asserting DTCS values for drivers. The three drivers fixed immediately before this commit should have been caught by this test, but were not because it was always skipping the dtcs field. Related to #11506 --- tests/base.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/base.py b/tests/base.py index 093bedef0..c2033aa79 100644 --- a/tests/base.py +++ b/tests/base.py @@ -174,15 +174,16 @@ def assertEqualMem(self, a, b, ignore=None): (a.tmode == "Cross" and rx_mode == "Tone"))): continue - elif k == "dtcs" and not ( - (a.tmode == "DTCS" and not self.rf.has_rx_dtcs) or - (a.tmode == "Cross" and tx_mode == "DTCS") or - (a.tmode == "Cross" and rx_mode == "DTCS" and - not self.rf.has_rx_dtcs)): + elif k == "dtcs" and (a.tmode != 'DTCS' or + (a.tmode == 'Cross' and tx_mode != 'DTCS')): + # If we are not in a tmode where a transmit DTCS code is + # required, we do not care if the code is persisted. continue elif k == "rx_dtcs" and (not self.rf.has_rx_dtcs or not (a.tmode == "Cross" and rx_mode == "DTCS")): + # If we are not in a tmode where a receive DTCS code is + # required, we do not care if the code is persisted. continue elif k == "offset" and not a.duplex: continue From f2a8faaaa4f149d5911c125f5715bad827fdce61 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 17:16:19 -0700 Subject: [PATCH 5/6] ga510: Fix ANI group code out of range Fixes #11508 --- chirp/drivers/ga510.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chirp/drivers/ga510.py b/chirp/drivers/ga510.py index 1642a9011..62304f28c 100644 --- a/chirp/drivers/ga510.py +++ b/chirp/drivers/ga510.py @@ -855,11 +855,17 @@ def apply_code(setting, obj): dtmf.append(rs) + try: + current_group = DTMFCHARS[int(anicode.groupcode)] + except IndexError: + LOG.warning('ANI group code index %i out of range', + anicode.groupcode) + current_group = DTMFCHARS[0] dtmf.append( RadioSetting( "anicode.groupcode", "Group Code", RadioSettingValueList(list(DTMFCHARS), - DTMFCHARS[int(anicode.groupcode)]))) + current_group))) else: dtmf.append( From f4a337c79bcdcc77aab5e09f67b20da92b9f9962 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 29 Aug 2024 19:23:49 -0700 Subject: [PATCH 6/6] Expose detected model variants in clone box This should make it more obvious for people looking for model variantions (like RT95 VOX). Related to #11509 --- chirp/wxui/clone.py | 52 +++++++++++++++++++++++++---- tests/unit/test_wxui_radiothread.py | 18 ++++++++++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/chirp/wxui/clone.py b/chirp/wxui/clone.py index 9591fa385..2ba15fc2a 100644 --- a/chirp/wxui/clone.py +++ b/chirp/wxui/clone.py @@ -247,6 +247,34 @@ def model_value(rclass): return ('%s %s' % (rclass.MODEL, rclass.VARIANT)).strip() +def detected_value(parent_rclass, rclass): + """Try to calculate a label for detected classes. + + This should be short and only represent the difference between them (like + a VARIANT or different MODEL). + """ + if parent_rclass.MODEL != rclass.MODEL: + # If the model is different, use that + label = rclass.MODEL + # If the detected class is a modified MODEL (i.e. 'RT95' and 'RT95 VOX' + # then strip off the prefix and the delimiter, if any. + if label.startswith(parent_rclass.MODEL): + label = label.replace(parent_rclass.MODEL, '').strip(' -_') + else: + # Assume the VARIANT is the distinguisher + label = rclass.VARIANT + + # In case the detected class is a different vendor, prefix that + if parent_rclass.VENDOR != rclass.VENDOR: + label = '%s %s' % (rclass.VENDOR, label) + + label = label.strip() + if not label: + LOG.error('Calculated blank detected value of %s from %s', + rclass, parent_rclass) + return label + + # Make this global so it sticks for a session CUSTOM_PORTS = [] @@ -284,7 +312,8 @@ def _add_grid(label, control): _add_grid(_('Vendor'), self._vendor) self.Bind(wx.EVT_CHOICE, self._selected_vendor, self._vendor) - self._model = wx.Choice(self, choices=[]) + self._model_choices = [] + self._model = wx.Choice(self, choices=self._model_choices) _add_grid(_('Model'), self._model) self.Bind(wx.EVT_CHOICE, self._selected_model, self._model) @@ -475,9 +504,19 @@ def _selected_port(self, event): self._persist_choices() def _select_vendor(self, vendor): - models = [model_value(x) - for x in self._vendors[vendor]] - self._model.Set(models) + display_models = [] + actual_models = [] + for rclass in self._vendors[vendor]: + display = model_value(rclass) + actual_models.append(display) + detected = ','.join(detected_value(rclass, x) for x in + rclass.detected_models(include_self=False)) + if detected: + display += ' (+ %s)' % detected + display_models.append(display) + + self._model_choices = actual_models + self._model.Set(display_models) self._model.SetSelection(0) def _selected_vendor(self, event): @@ -490,7 +529,7 @@ def _selected_model(self, event): def select_vendor_model(self, vendor, model): self._vendor.SetSelection(self._vendor.GetItems().index(vendor)) self._select_vendor(vendor) - self._model.SetSelection(self._model.GetItems().index(model)) + self._model.SetSelection(self._model_choices.index(model)) def _status(self, status): def _safe_status(): @@ -635,7 +674,8 @@ def _action(self, event): def _persist_choices(self): # On download, persist the selections from the actual UI boxes CONF.set('last_vendor', self._vendor.GetStringSelection(), 'state') - CONF.set('last_model', self._model.GetStringSelection(), 'state') + CONF.set('last_model', self._model_choices[self._model.GetSelection()], + 'state') CONF.set('last_port', self.get_selected_port(), 'state') diff --git a/tests/unit/test_wxui_radiothread.py b/tests/unit/test_wxui_radiothread.py index 9039ab4eb..02770341e 100644 --- a/tests/unit/test_wxui_radiothread.py +++ b/tests/unit/test_wxui_radiothread.py @@ -16,6 +16,8 @@ # These need to be imported after the above mock so that we don't require # wx to be present for these tests from tests.unit import base # noqa +from chirp import chirp_common # noqa +from chirp import directory # noqa from chirp.wxui import clone # noqa from chirp.wxui import config # noqa from chirp.wxui import radiothread # noqa @@ -156,6 +158,22 @@ def test_sort_ports_windows(self, system): [clone.port_label(p) for p in sorted(ports, key=clone.port_sort_key)]) + def test_detected_model_labels(self): + # Make sure all our detected model labels will be reasonable + # (and nonzero) in length. If the full label is too long, it will not + # be visible in the model box. + for rclass in [x for x in directory.DRV_TO_RADIO.values() + if issubclass(x, chirp_common.DetectableInterface)]: + labels = [] + for child_rclass in rclass.detected_models(include_self=False): + label = clone.detected_value(rclass, child_rclass) + self.assertNotEqual('', label) + labels.append(label) + if labels: + label = '%s (+ %s)' % (rclass.MODEL, ','.join(labels)) + self.assertLessEqual(len(label), 32, + 'Label %r is too long' % label) + class TestException(Exception): pass