# HG changeset patch # User Kosta A. ve7kcy@gmail.com # Date 1619800737 25200 # Fri Apr 30 09:38:57 2021 -0700 # Node ID 941a95de942d7c263a6197bf115cc75bc3624de9 # Parent cd3e2444040876b4a19b41c6cfecedb79ff4a8fe [icomciv] Restructuring of IcomCIVRadio radio classes; breakout each radio into its own module in preparetion for simplifying support for upcomming CIV feature #4547.
diff --git a/chirp/drivers/ic7000.py b/chirp/drivers/ic7000.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic7000.py @@ -0,0 +1,84 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + +# http://www.vk4adc.com/ +# web/index.php/reference-information/49-general-ref-info/182-civ7400 +MEM_IC7000_FORMAT = """ +u8 bank; +bbcd number[2]; +u8 spl:4, + skip:4; +lbcd freq[5]; +u8 mode; +u8 filter; +u8 duplex:4, + tmode:4; +bbcd rtone[3]; +bbcd ctone[3]; +u8 dtcs_polarity; +bbcd dtcs[2]; +lbcd freq_tx[5]; +u8 mode_tx; +u8 filter_tx; +u8 duplex_tx:4, + tmode_tx:4; +bbcd rtone_tx[3]; +bbcd ctone_tx[3]; +u8 dtcs_polarity_tx; +bbcd dtcs_tx[2]; +char name[9]; +""" + +class IC7000MemFrame(icomciv.BankMemFrame): + FORMAT = MEM_IC7000_FORMAT + +@directory.register +class Icom7000Radio(icomciv.IcomCIVRadio): + """Icom IC-7000""" + MODEL = "IC-7000" + _model = "\x70" + _template = 102 + + _num_banks = 5 # Banks A-E + _bank_index_bounds = (1, 99) + _bank_class = icf.IcomBank + + def _initialize(self): + self._classes["mem"] = IC7000MemFrame + self._rf.has_bank = True + self._rf.has_dtcs_polarity = True + self._rf.has_dtcs = True + self._rf.has_ctone = True + self._rf.has_offset = True + self._rf.has_name = True + self._rf.has_tuning_step = False + self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM"] + self._rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"] + self._rf.valid_duplexes = ["", "-", "+", "split"] + self._rf.valid_bands = [(30000, 199999999), (400000000, 470000000)] + self._rf.valid_tuning_steps = [] + self._rf.valid_skips = ["S", ""] + self._rf.valid_name_length = 9 + self._rf.valid_characters = chirp_common.CHARSET_ASCII + self._rf.memory_bounds = (0, 99 * self._num_banks - 1) + self._rf.can_odd_split = True diff --git a/chirp/drivers/ic7100.py b/chirp/drivers/ic7100.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic7100.py @@ -0,0 +1,88 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + + +MEM_IC7100_FORMAT = """ +u8 bank; // 1 bank number +bbcd number[2]; // 2,3 +u8 splitSelect; // 4 split and select memory settings +lbcd freq[5]; // 5-9 operating freq +u8 mode; // 10 operating mode +u8 filter; // 11 filter +u8 dataMode; // 12 data mode setting (on or off) +u8 duplex:4, // 13 duplex on/-/+ + tmode:4; // 13 tone +u8 dsql:4, // 14 digital squelch + unknown1:4; // 14 zero +bbcd rtone[3]; // 15-17 repeater tone freq +bbcd ctone[3]; // 18-20 tone squelch setting +u8 dtcsPolarity; // 21 DTCS polarity +u8 unknown2:4, // 22 zero + firstDtcs:4; // 22 first digit of DTCS code +u8 secondDtcs:4, // 23 second digit DTCS + thirdDtcs:4; // 23 third digit DTCS +u8 digitalSquelch; // 24 Digital code squelch setting +lbcd duplexOffset[3]; // 25-27 duplex offset freq +char destCall[8]; // 28-35 destination call sign +char accessRepeaterCall[8];// 36-43 access repeater call sign +char linkRepeaterCall[8]; // 44-51 gateway/link repeater call sign +bbcd duplexSettings[47]; // repeat of 5-51 for duplex +char name[16]; // 52-60 Name of station +""" + +class IC7100MemFrame(icomciv.BankMemFrame): + FORMAT = MEM_IC7100_FORMAT + +@directory.register +class Icom7100Radio(icomciv.IcomCIVRadio): + """Icom IC-7100""" + MODEL = "IC-7100" + _model = "\x88" + _template = 102 + + _num_banks = 5 + _bank_index_bounds = (1, 99) + _bank_class = icf.IcomBank + + def _initialize(self): + self._classes["mem"] = IC7100MemFrame + self._rf.has_bank = True + self._rf.has_bank_index = False + self._rf.has_bank_names = False + self._rf.has_dtcs_polarity = False + self._rf.has_dtcs = False + self._rf.has_ctone = True + self._rf.has_offset = False + self._rf.has_name = True + self._rf.has_tuning_step = False + self._rf.valid_modes = [ + "LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM", "CWR", "RTTYR", "DV" + ] + self._rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"] + self._rf.valid_duplexes = ["", "-", "+"] + self._rf.valid_bands = [(30000, 199999999), (400000000, 470000000)] + self._rf.valid_tuning_steps = [] + self._rf.valid_skips = [] + self._rf.valid_name_length = 16 + self._rf.valid_characters = chirp_common.CHARSET_ASCII + self._rf.memory_bounds = (0, 99 * self._num_banks - 1) diff --git a/chirp/drivers/ic7200.py b/chirp/drivers/ic7200.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic7200.py @@ -0,0 +1,48 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + + +@directory.register +class Icom7200Radio(icomciv.IcomCIVRadio): + """Icom IC-7200""" + MODEL = "7200" + _model = "\x76" + _template = 201 + + _num_banks = 1 # Banks not supported + + def _initialize(self): + self._rf.has_bank = False + self._rf.has_dtcs_polarity = False + self._rf.has_dtcs = False + self._rf.has_ctone = False + self._rf.has_offset = False + self._rf.has_name = False + self._rf.has_tuning_step = False + self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", + "CWR", "RTTYR"] + self._rf.valid_tmodes = [] + self._rf.valid_duplexes = [] + self._rf.valid_bands = [(30000, 60000000)] + self._rf.valid_skips = [] + self._rf.memory_bounds = (1, 201) diff --git a/chirp/drivers/ic7300.py b/chirp/drivers/ic7300.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic7300.py @@ -0,0 +1,103 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + + +MEM_IC7300_FORMAT = """ +bbcd number[2]; // 1,2 +u8 spl:4, // 3 split and select memory settings + select:4; +lbcd freq[5]; // 4-8 receive freq +u8 mode; // 9 operating mode +u8 filter; // 10 filter 1-3 (undocumented) +u8 dataMode:4, // 11 data mode setting (on or off) + tmode:4; // 11 tone type +char pad1; +bbcd rtone[2]; // 12-14 tx tone freq +char pad2; +bbcd ctone[2]; // 15-17 tone rx squelch setting +lbcd freq_tx[5]; // 4-8 transmit freq +u8 mode_tx; // 9 tx operating mode +u8 filter_tx; // 10 +u8 dataMode_tx:4, // 11 tx data mode setting (on or off) + tmode_tx:4; // 11 tx tone type +char pad3; +bbcd rtone_tx[2]; // 12-14 repeater tone freq +char pad4; +bbcd ctone_tx[2]; // 15-17 tone squelch setting +char name[10]; // 18-27 Callsign +""" + +class IC7300MemFrame(icomciv.MemFrame): + FORMAT = MEM_IC7300_FORMAT + +@directory.register +class Icom7300Radio(icomciv.IcomCIVRadio): + """Icom IC-7300""" + MODEL = "IC-7300" + _model = "\x94" + _template = 100 # Use P1 as blank template + + _SPECIAL_CHANNELS = { + "P1": 100, + "P2": 101, + } + _SPECIAL_CHANNELS_REV = dict(zip(_SPECIAL_CHANNELS.values(), + _SPECIAL_CHANNELS.keys())) + + def _is_special(self, number): + return number > 99 or isinstance(number, str) + + def _get_special_info(self, number): + info = SpecialChannel() + 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 _initialize(self): + self._classes["mem"] = IC7300MemFrame + self._rf.has_name = True + self._rf.has_dtcs = False + self._rf.has_dtcs_polarity = False + self._rf.has_bank = False + self._rf.has_tuning_step = False + self._rf.has_nostep_tuning = True + self._rf.can_odd_split = True + self._rf.memory_bounds = (1, 99) + self._rf.valid_modes = [ + "LSB", "USB", "AM", "CW", "RTTY", "FM", "CWR", "RTTYR", + "Data+LSB", "Data+USB", "Data+AM", "N/A", "N/A", "Data+FM" + ] + self._rf.valid_tmodes = ["", "Tone", "TSQL"] + # self._rf.valid_duplexes = ["", "-", "+", "split"] + self._rf.valid_duplexes = [] # To prevent using memobj.duplex + self._rf.valid_bands = [(1800000, 70500000)] + self._rf.valid_skips = [] + self._rf.valid_name_length = 10 + self._rf.valid_characters = chirp_common.CHARSET_ASCII + self._rf.valid_special_chans = sorted(self._SPECIAL_CHANNELS.keys()) diff --git a/chirp/drivers/ic746.py b/chirp/drivers/ic746.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic746.py @@ -0,0 +1,76 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + + +MEM_IC746_FORMAT = """ +bbcd number[2]; +u8 unknown1; +lbcd freq[5]; +u8 unknown2:5, + mode:3; +u8 unknown1; +u8 unknown2:2, + duplex:2, + unknown3:1, + tmode:3; +u8 unknown4; +bbcd rtone[2]; +u8 unknown5; +bbcd ctone[2]; +u8 dtcs_polarity; +bbcd dtcs[2]; +u8 unknown[11]; +char name[9]; +""" + +class IC746MemFrame(icomciv.MemFrame): + FORMAT = MEM_IC746_FORMAT + +@directory.register +class Icom746Radio(icomciv.IcomCIVRadio): + """Icom IC-746""" + MODEL = "746" + BAUD_RATE = 9600 + _model = "\x56" + _template = 102 + + _num_banks = 1 # Banks not supported + + def _initialize(self): + self._classes["mem"] = IC746MemFrame + self._rf.has_bank = False + self._rf.has_dtcs_polarity = False + self._rf.has_dtcs = False + self._rf.has_ctone = True + self._rf.has_offset = False + self._rf.has_name = True + self._rf.has_tuning_step = False + self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", "FM"] + self._rf.valid_tmodes = ["", "Tone", "TSQL"] + self._rf.valid_duplexes = ["", "-", "+"] + self._rf.valid_bands = [(30000, 199999999)] + self._rf.valid_tuning_steps = [] + self._rf.valid_skips = [] + self._rf.valid_name_length = 9 + self._rf.valid_characters = chirp_common.CHARSET_ASCII + self._rf.memory_bounds = (1, 99) diff --git a/chirp/drivers/ic910.py b/chirp/drivers/ic910.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ic910.py @@ -0,0 +1,142 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import struct +import logging + +from chirp import chirp_common, errors, directory +from chirp.drivers import icf, icomciv + +LOG = logging.getLogger(__name__) + + +MEM_IC910_FORMAT = """ +u8 bank; // 1 bank number +bbcd number[2]; // 2,3 +lbcd freq[5]; // 4-8 operating freq +u8 mode; // 9 operating mode +u8 filter; // 10 filter +u8 tmode:4, // 11 tone + duplex:4; // 11 duplex off/-/+ +bbcd rtone[3]; // 12-14 repeater tone freq +bbcd ctone[3]; // 15-17 tone squelch setting +lbcd duplexOffset[3]; // 18-20 duplex offset freq +""" + +class IC910MemFrame(icomciv.BankMemFrame): + FORMAT = MEM_IC910_FORMAT + +@directory.register +class Icom910Radio(icomciv.IcomCIVRadio): + """Icom IC-910""" + MODEL = "IC-910" + BAUD_RATE = 19200 + _model = "\x60" + _template = 100 + + _num_banks = 3 # Banks for 2m, 70cm, 23cm + _bank_index_bounds = (1, 99) + _bank_class = icf.IcomBank + + _SPECIAL_CHANNELS = { + "1A": 100, + "1b": 101, + "2A": 102, + "2b": 103, + "3A": 104, + "3b": 105, + "C": 106, + } + _SPECIAL_CHANNELS_REV = {v: k for k, v in _SPECIAL_CHANNELS.items()} + + _SPECIAL_BANKS = { + "2m": 1, + "70cm": 2, + "23cm": 3, + } + _SPECIAL_BANKS_REV = {v: k for k, v in _SPECIAL_BANKS.items()} + + def _get_special_names(self, band): + return sorted([band + "-" + key + for key in self._SPECIAL_CHANNELS.keys()]) + + def _is_special(self, number): + return number >= 1000 or isinstance(number, str) + + def _get_special_info(self, number): + info = BankSpecialChannel() + if isinstance(number, str): + info.name = number + (band_name, chan_name) = number.split("-") + info.bank = self._SPECIAL_BANKS[band_name] + info.channel = self._SPECIAL_CHANNELS[chan_name] + info.location = info.bank * 1000 + info.channel + else: + info.location = number + (info.bank, info.channel) = divmod(number, 1000) + band_name = self._SPECIAL_BANKS_REV[info.bank] + chan_name = self._SPECIAL_CHANNELS_REV[info.channel] + info.name = band_name + "-" + chan_name + return info + + # The IC-910 has a bank of memories for each band. The 23cm band is only + # available when the optional UX-910 unit is installed, but there is no + # direct means of detecting its presence. Instead, attempt to access the + # first memory in the 23cm bank. If that's successful, the unit is there, + # and we can present all 3 banks to the user. Otherwise, the unit is not + # installed, so we present 2 banks to the user, for 2m and 70cm. + def _detect_23cm_unit(self): + if not self.pipe: + return True + f = IC910MemFrame() + f.set_location(1, 3) # First memory in 23cm bank + self._send_frame(f) + f.read(self.pipe) + if f._cmd == 0xFA: # Error code lands in command field + self._num_banks = 2 + LOG.debug("UX-910 unit is %sinstalled" % + ("not " if self._num_banks == 2 else "")) + return self._num_banks == 3 + + def _initialize(self): + self._classes["mem"] = IC910MemFrame + self._has_23cm_unit = self._detect_23cm_unit() + self._rf.has_bank = True + self._rf.has_dtcs_polarity = False + self._rf.has_dtcs = False + self._rf.has_ctone = True + self._rf.has_offset = True + self._rf.has_name = False + self._rf.has_tuning_step = False + self._rf.valid_modes = ["LSB", "USB", "CW", "NCW", "FM", "NFM"] + self._rf.valid_tmodes = ["", "Tone", "TSQL"] + self._rf.valid_duplexes = ["", "-", "+"] + self._rf.valid_bands = [(136000000, 174000000), + (420000000, 480000000)] + self._rf.valid_tuning_steps = [] + self._rf.valid_skips = [] + self._rf.valid_special_chans = (self._get_special_names("2m") + + self._get_special_names("70cm")) + self._rf.memory_bounds = (1, 99 * self._num_banks) + + if self._has_23cm_unit: + self._rf.valid_bands.append((1240000000, 1320000000)) + self._rf.valid_special_chans += self._get_special_names("23cm") + + # Combine mode and filter into unified mode + self._unified_modes = True + + # Use Chirp locations starting with 1 + self._adjust_bank_loc_start = True diff --git a/chirp/drivers/icomciv.py b/chirp/drivers/icomciv.py --- a/chirp/drivers/icomciv.py +++ b/chirp/drivers/icomciv.py @@ -1,6 +1,21 @@ -# Latest update: March, 2021 RJ DeWitt added IC-7300 +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + import struct import logging + from chirp.drivers import icf from chirp import chirp_common, util, errors, bitwise, directory from chirp.memmap import MemoryMap @@ -23,122 +38,6 @@ unknown_2:4; """
- -# http://www.vk4adc.com/ -# web/index.php/reference-information/49-general-ref-info/182-civ7400 -MEM_IC7000_FORMAT = """ -u8 bank; -bbcd number[2]; -u8 spl:4, - skip:4; -lbcd freq[5]; -u8 mode; -u8 filter; -u8 duplex:4, - tmode:4; -bbcd rtone[3]; -bbcd ctone[3]; -u8 dtcs_polarity; -bbcd dtcs[2]; -lbcd freq_tx[5]; -u8 mode_tx; -u8 filter_tx; -u8 duplex_tx:4, - tmode_tx:4; -bbcd rtone_tx[3]; -bbcd ctone_tx[3]; -u8 dtcs_polarity_tx; -bbcd dtcs_tx[2]; -char name[9]; -""" - -MEM_IC7100_FORMAT = """ -u8 bank; // 1 bank number -bbcd number[2]; // 2,3 -u8 splitSelect; // 4 split and select memory settings -lbcd freq[5]; // 5-9 operating freq -u8 mode; // 10 operating mode -u8 filter; // 11 filter -u8 dataMode; // 12 data mode setting (on or off) -u8 duplex:4, // 13 duplex on/-/+ - tmode:4; // 13 tone -u8 dsql:4, // 14 digital squelch - unknown1:4; // 14 zero -bbcd rtone[3]; // 15-17 repeater tone freq -bbcd ctone[3]; // 18-20 tone squelch setting -u8 dtcsPolarity; // 21 DTCS polarity -u8 unknown2:4, // 22 zero - firstDtcs:4; // 22 first digit of DTCS code -u8 secondDtcs:4, // 23 second digit DTCS - thirdDtcs:4; // 23 third digit DTCS -u8 digitalSquelch; // 24 Digital code squelch setting -lbcd duplexOffset[3]; // 25-27 duplex offset freq -char destCall[8]; // 28-35 destination call sign -char accessRepeaterCall[8];// 36-43 access repeater call sign -char linkRepeaterCall[8]; // 44-51 gateway/link repeater call sign -bbcd duplexSettings[47]; // repeat of 5-51 for duplex -char name[16]; // 52-60 Name of station -""" - -MEM_IC910_FORMAT = """ -u8 bank; // 1 bank number -bbcd number[2]; // 2,3 -lbcd freq[5]; // 4-8 operating freq -u8 mode; // 9 operating mode -u8 filter; // 10 filter -u8 tmode:4, // 11 tone - duplex:4; // 11 duplex off/-/+ -bbcd rtone[3]; // 12-14 repeater tone freq -bbcd ctone[3]; // 15-17 tone squelch setting -lbcd duplexOffset[3]; // 18-20 duplex offset freq -""" - -mem_duptone_format = """ -bbcd number[2]; -u8 unknown1; -lbcd freq[5]; -u8 unknown2:5, - mode:3; -u8 unknown1; -u8 unknown2:2, - duplex:2, - unknown3:1, - tmode:3; -u8 unknown4; -bbcd rtone[2]; -u8 unknown5; -bbcd ctone[2]; -u8 dtcs_polarity; -bbcd dtcs[2]; -u8 unknown[11]; -char name[9]; -""" - -MEM_IC7300_FORMAT = """ -bbcd number[2]; // 1,2 -u8 spl:4, // 3 split and select memory settings - select:4; -lbcd freq[5]; // 4-8 receive freq -u8 mode; // 9 operating mode -u8 filter; // 10 filter 1-3 (undocumented) -u8 dataMode:4, // 11 data mode setting (on or off) - tmode:4; // 11 tone type -char pad1; -bbcd rtone[2]; // 12-14 tx tone freq -char pad2; -bbcd ctone[2]; // 15-17 tone rx squelch setting -lbcd freq_tx[5]; // 4-8 transmit freq -u8 mode_tx; // 9 tx operating mode -u8 filter_tx; // 10 -u8 dataMode_tx:4, // 11 tx data mode setting (on or off) - tmode_tx:4; // 11 tx tone type -char pad3; -bbcd rtone_tx[2]; // 12-14 repeater tone freq -char pad4; -bbcd ctone_tx[2]; // 15-17 tone squelch setting -char name[10]; // 18-27 Callsign -""" - SPLIT = ["", "spl"]
@@ -207,6 +106,7 @@
class MemFrame(Frame): """A memory frame""" + FORMAT = MEM_FORMAT _cmd = 0x1A _sub = 0x00 _loc = 0 @@ -227,7 +127,7 @@ def get_obj(self): """Return a bitwise parsed object""" self._data = MemoryMap(str(self._data)) # Make sure we're assignable - return bitwise.parse(MEM_FORMAT, self._data) + return bitwise.parse(self.FORMAT, self._data)
def initialize(self): """Initialize to sane values""" @@ -236,7 +136,7 @@
class BankMemFrame(MemFrame): """A memory frame for radios with multiple banks""" - FORMAT = MEM_IC7000_FORMAT + FORMAT = None _bnk = 0
def set_location(self, loc, bank=1): @@ -256,28 +156,6 @@ return bitwise.parse(self.FORMAT, self._data)
-class IC7100MemFrame(BankMemFrame): - FORMAT = MEM_IC7100_FORMAT - - -class IC910MemFrame(BankMemFrame): - FORMAT = MEM_IC910_FORMAT - - -class DupToneMemFrame(MemFrame): - def get_obj(self): - self._data = MemoryMap(str(self._data)) - return bitwise.parse(mem_duptone_format, self._data) - - -class IC7300MemFrame(MemFrame): - FORMAT = MEM_IC7300_FORMAT - - def get_obj(self): - self._data = MemoryMap(str(self._data)) - return bitwise.parse(self.FORMAT, self._data) - - class SpecialChannel(object): """Info for special (named) channels"""
@@ -442,10 +320,10 @@ else: return repr(f.get_obj())
-# We have a simple mapping between the memory location in the frequency -# editor and (bank, channel) of the radio. The mapping doesn't -# change so we use a little math to calculate what bank a location -# is in. We can't change the bank a location is in so we just pass. + # We have a simple mapping between the memory location in the frequency + # editor and (bank, channel) of the radio. The mapping doesn't + # change so we use a little math to calculate what bank a location + # is in. We can't change the bank a location is in so we just pass. def _get_bank(self, loc): if self._adjust_bank_loc_start: loc -= 1 @@ -562,6 +440,8 @@
if self._rf.can_odd_split and memobj.spl: mem.duplex = "split" + if hasattr(memobj, "duplex"): + mem.duplex = "split" mem.offset = int(memobj.freq_tx) mem.immutable = [] elif hasattr(memobj, "duplexOffset"): @@ -620,10 +500,10 @@ f.make_empty() self._send_frame(f)
-# The next two lines accept the radio's status after setting the memory -# and reports the results to the debug log. This is needed for the -# IC-7000. No testing was done to see if it breaks memory delete on the -# IC-746 or IC-7200. + # The next two lines accept the radio's status after setting the memory + # and reports the results to the debug log. This is needed for the + # IC-7000. No testing was done to see if it breaks memory delete on the + # IC-746 or IC-7200. f = self._recv_frame() LOG.debug("Result:\n%s" % util.hexprint(f.get_data())) return @@ -708,311 +588,28 @@ LOG.debug("Result:\n%s" % util.hexprint(f.get_data()))
-@directory.register -class Icom7200Radio(IcomCIVRadio): - """Icom IC-7200""" - MODEL = "7200" - _model = "\x76" - _template = 201 - - _num_banks = 1 # Banks not supported - - def _initialize(self): - self._rf.has_bank = False - self._rf.has_dtcs_polarity = False - self._rf.has_dtcs = False - self._rf.has_ctone = False - self._rf.has_offset = False - self._rf.has_name = False - self._rf.has_tuning_step = False - self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", - "CWR", "RTTYR"] - self._rf.valid_tmodes = [] - self._rf.valid_duplexes = [] - self._rf.valid_bands = [(30000, 60000000)] - self._rf.valid_skips = [] - self._rf.memory_bounds = (1, 201) - - -@directory.register -class Icom7000Radio(IcomCIVRadio): - """Icom IC-7000""" - MODEL = "IC-7000" - _model = "\x70" - _template = 102 - - _num_banks = 5 # Banks A-E - _bank_index_bounds = (1, 99) - _bank_class = icf.IcomBank - - def _initialize(self): - self._classes["mem"] = BankMemFrame - self._rf.has_bank = True - self._rf.has_dtcs_polarity = True - self._rf.has_dtcs = True - self._rf.has_ctone = True - self._rf.has_offset = True - self._rf.has_name = True - self._rf.has_tuning_step = False - self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM"] - self._rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"] - self._rf.valid_duplexes = ["", "-", "+", "split"] - self._rf.valid_bands = [(30000, 199999999), (400000000, 470000000)] - self._rf.valid_tuning_steps = [] - self._rf.valid_skips = ["S", ""] - self._rf.valid_name_length = 9 - self._rf.valid_characters = chirp_common.CHARSET_ASCII - self._rf.memory_bounds = (0, 99 * self._num_banks - 1) - self._rf.can_odd_split = True - - -@directory.register -class Icom7100Radio(IcomCIVRadio): - """Icom IC-7100""" - MODEL = "IC-7100" - _model = "\x88" - _template = 102 - - _num_banks = 5 - _bank_index_bounds = (1, 99) - _bank_class = icf.IcomBank - - def _initialize(self): - self._classes["mem"] = IC7100MemFrame - self._rf.has_bank = True - self._rf.has_bank_index = False - self._rf.has_bank_names = False - self._rf.has_dtcs_polarity = False - self._rf.has_dtcs = False - self._rf.has_ctone = True - self._rf.has_offset = False - self._rf.has_name = True - self._rf.has_tuning_step = False - self._rf.valid_modes = [ - "LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM", "CWR", "RTTYR", "DV" - ] - self._rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"] - self._rf.valid_duplexes = ["", "-", "+"] - self._rf.valid_bands = [(30000, 199999999), (400000000, 470000000)] - self._rf.valid_tuning_steps = [] - self._rf.valid_skips = [] - self._rf.valid_name_length = 16 - self._rf.valid_characters = chirp_common.CHARSET_ASCII - self._rf.memory_bounds = (0, 99 * self._num_banks - 1) - - -@directory.register -class Icom746Radio(IcomCIVRadio): - """Icom IC-746""" - MODEL = "746" - BAUD_RATE = 9600 - _model = "\x56" - _template = 102 - - _num_banks = 1 # Banks not supported - - def _initialize(self): - self._classes["mem"] = DupToneMemFrame - self._rf.has_bank = False - self._rf.has_dtcs_polarity = False - self._rf.has_dtcs = False - self._rf.has_ctone = True - self._rf.has_offset = False - self._rf.has_name = True - self._rf.has_tuning_step = False - self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY", "FM"] - self._rf.valid_tmodes = ["", "Tone", "TSQL"] - self._rf.valid_duplexes = ["", "-", "+"] - self._rf.valid_bands = [(30000, 199999999)] - self._rf.valid_tuning_steps = [] - self._rf.valid_skips = [] - self._rf.valid_name_length = 9 - self._rf.valid_characters = chirp_common.CHARSET_ASCII - self._rf.memory_bounds = (1, 99) - - -@directory.register -class Icom910Radio(IcomCIVRadio): - """Icom IC-910""" - MODEL = "IC-910" - BAUD_RATE = 19200 - _model = "\x60" - _template = 100 - - _num_banks = 3 # Banks for 2m, 70cm, 23cm - _bank_index_bounds = (1, 99) - _bank_class = icf.IcomBank - - _SPECIAL_CHANNELS = { - "1A": 100, - "1b": 101, - "2A": 102, - "2b": 103, - "3A": 104, - "3b": 105, - "C": 106, - } - _SPECIAL_CHANNELS_REV = {v: k for k, v in _SPECIAL_CHANNELS.items()} - - _SPECIAL_BANKS = { - "2m": 1, - "70cm": 2, - "23cm": 3, - } - _SPECIAL_BANKS_REV = {v: k for k, v in _SPECIAL_BANKS.items()} - - def _get_special_names(self, band): - return sorted([band + "-" + key - for key in self._SPECIAL_CHANNELS.keys()]) - - def _is_special(self, number): - return number >= 1000 or isinstance(number, str) - - def _get_special_info(self, number): - info = BankSpecialChannel() - if isinstance(number, str): - info.name = number - (band_name, chan_name) = number.split("-") - info.bank = self._SPECIAL_BANKS[band_name] - info.channel = self._SPECIAL_CHANNELS[chan_name] - info.location = info.bank * 1000 + info.channel - else: - info.location = number - (info.bank, info.channel) = divmod(number, 1000) - band_name = self._SPECIAL_BANKS_REV[info.bank] - chan_name = self._SPECIAL_CHANNELS_REV[info.channel] - info.name = band_name + "-" + chan_name - return info - - # The IC-910 has a bank of memories for each band. The 23cm band is only - # available when the optional UX-910 unit is installed, but there is no - # direct means of detecting its presence. Instead, attempt to access the - # first memory in the 23cm bank. If that's successful, the unit is there, - # and we can present all 3 banks to the user. Otherwise, the unit is not - # installed, so we present 2 banks to the user, for 2m and 70cm. - def _detect_23cm_unit(self): - if not self.pipe: - return True - f = IC910MemFrame() - f.set_location(1, 3) # First memory in 23cm bank - self._send_frame(f) - f.read(self.pipe) - if f._cmd == 0xFA: # Error code lands in command field - self._num_banks = 2 - LOG.debug("UX-910 unit is %sinstalled" % - ("not " if self._num_banks == 2 else "")) - return self._num_banks == 3 - - def _initialize(self): - self._classes["mem"] = IC910MemFrame - self._has_23cm_unit = self._detect_23cm_unit() - self._rf.has_bank = True - self._rf.has_dtcs_polarity = False - self._rf.has_dtcs = False - self._rf.has_ctone = True - self._rf.has_offset = True - self._rf.has_name = False - self._rf.has_tuning_step = False - self._rf.valid_modes = ["LSB", "USB", "CW", "NCW", "FM", "NFM"] - self._rf.valid_tmodes = ["", "Tone", "TSQL"] - self._rf.valid_duplexes = ["", "-", "+"] - self._rf.valid_bands = [(136000000, 174000000), - (420000000, 480000000)] - self._rf.valid_tuning_steps = [] - self._rf.valid_skips = [] - self._rf.valid_special_chans = (self._get_special_names("2m") + - self._get_special_names("70cm")) - self._rf.memory_bounds = (1, 99 * self._num_banks) - - if self._has_23cm_unit: - self._rf.valid_bands.append((1240000000, 1320000000)) - self._rf.valid_special_chans += self._get_special_names("23cm") - - # Combine mode and filter into unified mode - self._unified_modes = True - - # Use Chirp locations starting with 1 - self._adjust_bank_loc_start = True - - -@directory.register -class Icom7300Radio(IcomCIVRadio): # Added March, 2021 by Rick DeWitt - """Icom IC-7300""" - MODEL = "IC-7300" - _model = "\x94" - _template = 100 # Use P1 as blank template - - _SPECIAL_CHANNELS = { - "P1": 100, - "P2": 101, - } - _SPECIAL_CHANNELS_REV = dict(zip(_SPECIAL_CHANNELS.values(), - _SPECIAL_CHANNELS.keys())) - - def _is_special(self, number): - return number > 99 or isinstance(number, str) - - def _get_special_info(self, number): - info = SpecialChannel() - 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 _initialize(self): - self._classes["mem"] = IC7300MemFrame - self._rf.has_name = True - self._rf.has_dtcs = False - self._rf.has_dtcs_polarity = False - self._rf.has_bank = False - self._rf.has_tuning_step = False - self._rf.has_nostep_tuning = True - self._rf.can_odd_split = True - self._rf.memory_bounds = (1, 99) - self._rf.valid_modes = [ - "LSB", "USB", "AM", "CW", "RTTY", "FM", "CWR", "RTTYR", - "Data+LSB", "Data+USB", "Data+AM", "N/A", "N/A", "Data+FM" - ] - self._rf.valid_tmodes = ["", "Tone", "TSQL"] - # self._rf.valid_duplexes = ["", "-", "+", "split"] - self._rf.valid_duplexes = [] # To prevent using memobj.duplex - self._rf.valid_bands = [(1800000, 70500000)] - self._rf.valid_skips = [] - self._rf.valid_name_length = 10 - self._rf.valid_characters = chirp_common.CHARSET_ASCII - self._rf.valid_special_chans = sorted(self._SPECIAL_CHANNELS.keys()) - - -CIV_MODELS = { - (0x76, 0xE0): Icom7200Radio, - (0x88, 0xE0): Icom7100Radio, - (0x70, 0xE0): Icom7000Radio, - (0x46, 0xE0): Icom746Radio, - (0x60, 0xE0): Icom910Radio, - (0x94, 0xE0): Icom7300Radio, -} - - def probe_model(ser): """Probe the radio attatched to @ser for its model""" f = Frame() f.set_command(0x19, 0x00)
- for model, controller in CIV_MODELS.keys(): - f.send(model, controller, ser) + models = {} + for rclass in directory.DRV_TO_RADIO.values(): + if issubclass(rclass, IcomCIVRadio): + models[rclass.MODEL] = rclass + + for rclass in models.values(): + model = ord(rclass._model) + f.send(model, 0xE0, ser) try: f.read(ser) except errors.RadioError: continue
if len(f.get_data()) == 1: - model = ord(f.get_data()[0]) - return CIV_MODELS[(model, controller)] + md = ord(f.get_data()[0]) + if (md == model): + return rclass
if f.get_data(): LOG.debug("Got data, but not 1 byte:")