From 222a5017caaab03863456cff774598e129b7d775 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 22 Aug 2024 15:58:54 -0700 Subject: [PATCH] Add Icom IC-9700 support Note this does not implement any D-STAR stuff or satellite memories yet, it's just basic support. Fixes #10018 --- chirp/drivers/icomciv.py | 123 +++++++++++++++++++++++++++++++- tests/Python3_Driver_Testing.md | 7 +- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/chirp/drivers/icomciv.py b/chirp/drivers/icomciv.py index e72550469..e17f84e85 100644 --- a/chirp/drivers/icomciv.py +++ b/chirp/drivers/icomciv.py @@ -142,6 +142,30 @@ char name[10]; // 18-27 Callsign """ +MEM_IC9700_FORMAT = """ +u8 bank; +bbcd number[2]; +u8 select_memory; +lbcd freq[5]; +bbcd mode; +u8 filter; +bbcd data_mode; +u8 duplex:4, + tmode:4; +u8 dig_sql:4, + unused1:4; +bbcd rtone[3]; +bbcd ctone[3]; +u8 dtcs_polarity; +bbcd dtcs[2]; +u8 dig_code; +lbcd duplexOffset[3]; +char urcall[8]; +char rpt1call[8]; +char rpt2call[8]; +char name[16]; +""" + MEM_IC7400_FORMAT = """ bbcd number[2]; u8 unknown1; @@ -356,6 +380,10 @@ class IC7610MemFrame(MemFrame): FORMAT = MEM_IC7610_FORMAT +class IC9700MemFrame(BankMemFrame): + FORMAT = MEM_IC9700_FORMAT + + class SpecialChannel(object): """Info for special (named) channels""" @@ -614,7 +642,9 @@ def get_memory(self, number): mem.freq = int(memobj.freq) try: - mem.mode = self._MODES[memobj.mode] + # Note that memobj.mode could be a bcd on some radios, so we must + # coerce it to an int for the index operation. + mem.mode = self._MODES[int(memobj.mode)] # We do not know what a variety of the positions between # PSK and DV mean, so let's behave as if those values @@ -1154,6 +1184,97 @@ def _initialize(self): self._classes['mem'] = IC7610MemFrame +@directory.register +class Icom9700Radio(IcomCIVRadio): + MODEL = 'IC-9700' + _model = '\xA2' + _template = 100 + + def get_sub_devices(self): + return [Icom9700RadioBand(self, 1), + Icom9700RadioBand(self, 2), + Icom9700RadioBand(self, 3)] + + def _initialize(self): + super()._initialize() + self._rf.has_sub_devices = True + self._rf.memory_bounds = (1, 99) + self._classes['mem'] = IC9700MemFrame + + +class Icom9700RadioBand(Icom9700Radio): + BANDS = { + 1: (144, 148), + 2: (430, 450), + 3: (1240, 1300), + } + _MODES = [ + "LSB", "USB", "AM", "CW", "RTTY", "FM", "CWR", + "RTTY-R", None, None, None, None, None, None, None, None, None, + "DV", None, None, None, None, "DD", None, None, None, None, None, + ] + _SPECIAL_CHANNELS = { + "1A": 100, + "1B": 101, + "2A": 102, + "2B": 103, + "3A": 104, + "4B": 105, + "C1": 106, + "C2": 107, + } + _SPECIAL_CHANNELS_REV = dict(zip(_SPECIAL_CHANNELS.values(), + _SPECIAL_CHANNELS.keys())) + + def _is_special(self, number): + return isinstance(number, str) or number > 99 + + def _get_special_info(self, number): + info = BankSpecialChannel() + info.bank = self._band + if isinstance(number, str): + info.name = number + info.channel = self._SPECIAL_CHANNELS[number] + info.location = info.channel + else: + info.location = number + info.name = self._SPECIAL_CHANNELS_REV[number] + info.channel = info.location + return info + + def __init__(self, parent, band): + self._parent = parent + self._band = band + self.VARIANT = '%i band' % (self.BANDS[band][0]) + super().__init__(parent.pipe) + + def mem_to_ch_bnk(self, mem): + return mem, self._band + + def _initialize(self): + super()._initialize() + self._rf.has_name = True + self._rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS'] + self._rf.has_dtcs = True + self._rf.has_dtcs_polarity = True + self._rf.has_bank = True + self._rf.has_tuning_step = False + self._rf.has_nostep_tuning = True + # Satellite memories definitely + self._rf.can_odd_split = False + self._rf.memory_bounds = (1, 99) + self._rf.valid_bands = [(x * 1000000, y * 1000000) for x, y in + [self.BANDS[self._band]]] + self._rf.valid_name_length = 16 + self._rf.valid_characters = (chirp_common.CHARSET_ALPHANUMERIC + + '!#$%&\\?"\'`^+-*/.,:=<>()[]{}|_~@') + self._rf.valid_special_chans = sorted(self._SPECIAL_CHANNELS.keys()) + # Last item is RPS for DD mode + self._rf.valid_duplexes = ['', '-', '+'] + self._rf.valid_modes = [x for x in self._MODES if x] + self._classes['mem'] = IC9700MemFrame + + def probe_model(ser): """Probe the radio attached to @ser for its model""" f = Frame() diff --git a/tests/Python3_Driver_Testing.md b/tests/Python3_Driver_Testing.md index 5f7dd1ece..cbff80d73 100644 --- a/tests/Python3_Driver_Testing.md +++ b/tests/Python3_Driver_Testing.md @@ -123,6 +123,7 @@ | Icom_IC-7610 | [@kk7ds](https://github.com/kk7ds) | 24-Oct-2022 | Yes | 0.00% | | Icom_IC-910 | [@mfncooper](https://github.com/mfncooper) | 1-Oct-2021 | Yes | 0.00% | | Icom_IC-91_92AD | [@kk7ds](https://github.com/kk7ds) | 23-Nov-2022 | Yes | 0.03% | +| Icom_IC-9700 | | | Yes | | | Icom_IC-E90 | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/icf.py) | 12-Dec-2022 | Yes | 0.04% | | Icom_IC-P7 | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/icf.py) | 12-Dec-2022 | Yes | 0.01% | | Icom_IC-Q7A | [@KC9HI](https://github.com/KC9HI) | 20-Nov-2022 | Yes | 0.01% | @@ -469,11 +470,11 @@ | Zastone_ZT-X6 | [Implied by Retevis_RT22](#user-content-Retevis_RT22) | 9-Dec-2022 | Yes | 0.11% | ## Stats -**Drivers:** 466 +**Drivers:** 467 -**Tested:** 87% (407/59) (93% of usage stats) +**Tested:** 87% (407/60) (93% of usage stats) -**Byte clean:** 91% (427/39) +**Byte clean:** 91% (428/39) ## Meaning of this testing