diff --git a/chirp/drivers/anytone778uv.py b/chirp/drivers/anytone778uv.py index c662b6d6..93fbef02 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('->') diff --git a/chirp/drivers/ft4.py b/chirp/drivers/ft4.py index c748e951..ec0a63f7 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), diff --git a/chirp/drivers/ft857.py b/chirp/drivers/ft857.py index 57acf9b1..13e13f0d 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) diff --git a/chirp/drivers/ga510.py b/chirp/drivers/ga510.py index 1642a901..62304f28 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( diff --git a/chirp/wxui/clone.py b/chirp/wxui/clone.py index 9591fa38..2ba15fc2 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/base.py b/tests/base.py index 093bedef..c2033aa7 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 diff --git a/tests/unit/test_wxui_radiothread.py b/tests/unit/test_wxui_radiothread.py index 9039ab4e..02770341 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