Skip to content

Commit

Permalink
Add Icom IC-9700 support
Browse files Browse the repository at this point in the history
Note this does not implement any D-STAR stuff or satellite memories
yet, it's just basic support.

Fixes #10018
  • Loading branch information
kk7ds committed Aug 23, 2024
1 parent 3935e1c commit 61fa50f
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 4 deletions.
127 changes: 126 additions & 1 deletion chirp/drivers/icomciv.py
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"""

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1154,6 +1184,101 @@ def _initialize(self):
self._classes['mem'] = IC7610MemFrame


@directory.register
class Icom9700Radio(IcomCIVRadio):
MODEL = 'IC-9700'
_model = '\xA2'
_template = 100
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,
]

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):
_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 _detect_echo(self):
self._parent._willecho

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
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]
if self._band != 3:
self._rf.valid_modes.remove('DD')
self._classes['mem'] = IC9700MemFrame


def probe_model(ser):
"""Probe the radio attached to @ser for its model"""
f = Frame()
Expand Down
7 changes: 4 additions & 3 deletions tests/Python3_Driver_Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
| <a name="Icom_IC-7610"></a> Icom_IC-7610 | [@kk7ds](https://github.com/kk7ds) | 24-Oct-2022 | Yes | 0.00% |
| <a name="Icom_IC-910"></a> Icom_IC-910 | [@mfncooper](https://github.com/mfncooper) | 1-Oct-2021 | Yes | 0.00% |
| <a name="Icom_IC-91_92AD"></a> Icom_IC-91_92AD | [@kk7ds](https://github.com/kk7ds) | 23-Nov-2022 | Yes | 0.03% |
| <a name="Icom_IC-9700"></a> Icom_IC-9700 | | | Yes | |
| <a name="Icom_IC-E90"></a> Icom_IC-E90 | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/icf.py) | 12-Dec-2022 | Yes | 0.04% |
| <a name="Icom_IC-P7"></a> Icom_IC-P7 | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/icf.py) | 12-Dec-2022 | Yes | 0.01% |
| <a name="Icom_IC-Q7A"></a> Icom_IC-Q7A | [@KC9HI](https://github.com/KC9HI) | 20-Nov-2022 | Yes | 0.01% |
Expand Down Expand Up @@ -469,11 +470,11 @@
| <a name="Zastone_ZT-X6"></a> 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

Expand Down

0 comments on commit 61fa50f

Please sign in to comment.