[chirp_devel] [PATCH] [icomciv] Restructuring of IcomCIVRadio radio classes; breakout each radio into its own module in preparetion for simplifying support for upcomming CIV feature #4547
# 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:")
This patch actually combines two distinct changes:
1) Changing the way probe_model() works to enumerate the global driver list instead of the local model list. This would have been a trivial change in and of itself, and is unrelated to the refactoring. Presumably this change is somehow necessitated by your approach to #4547, but it's not at all clear that it's a meaningful change. If it's needed at all, I would think it might make more sense as a part of the patch for #4547, and not as part of an unrelated refactoring.
2) The splitting up of the classes. Without seeing how you intend to implement #4547, it is hard to see how this huge change could simplify anything. Most importantly, this change means the loss of history for all of the drivers currently in icomciv.py insofar as someone would have to know to look at the history of that module in order to find any history at all for all of the drivers being split out from here. I just don't see a good reason to make this change.
Personally, I would prefer to not see this patch applied at least until we understand why it might be helpful to the upcoming changes for #4547, since I just don't see the justification right now.
Martin. KD6YAM
On Fri, Apr 30, 2021 at 9:42 AM Kosta A. via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
# 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:")
chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
Thanks for the feedback Martin, please see my responses inline below. I remain open to suggestions and feedback.
On Fri, Apr 30, 2021 at 10:51 AM Martin Cooper mfncooper@gmail.com wrote:
This patch actually combines two distinct changes:
- Changing the way probe_model() works to enumerate the global driver
list instead of the local model list. This would have been a trivial change in and of itself, and is unrelated to the refactoring. Presumably this change is somehow necessitated by your approach to #4547, but it's not at all clear that it's a meaningful change. If it's needed at all, I would think it might make more sense as a part of the patch for #4547, and not as part of an unrelated refactoring.
This change is precipitated by the requirement to support configurable civ's (#4547) such that we are no longer able to hard code these values into the code base. While I agree that it would be ideal to not include it as part of the refactoring effort, so as to reduce its scope, the refactoring itself would introduce a circular dependency on the hard coded classes and thus makes it a requirement of this change. The directory method fwiw, is a fairly common practice in the codebase as it stands.
https://chirp.danplanet.com/projects/chirp/repository/entry/chirp/detect.py#...
- The splitting up of the classes. Without seeing how you intend to
implement #4547, it is hard to see how this huge change could simplify anything.
The intent of this change is to reduce potential conflicts amongst developers working on similar radio models. Currently there are multiple implementations of icom live mode radios being introduced into the code base - this change should minimize the scope of those changes making it i) easier to develop and ii) reducing merge conflicts and iii) easier to test.
https://chirp.danplanet.com/issues/8431 https://chirp.danplanet.com/issues/8987
Another outcome of this change is to allow for a clear isolation of radio specific implementation details from the base class, thus reducing the potential for knock on effects from a developer introducing a new radio model while unknowingly breaking another.
The root of the impending changes to support #4547 stems from the current implementation of how CIV values are handled, in that they are currently implemented as static variables and will need to become class variables. As this is a significant deviation to the implementation of the base IcomCIVRadio class, this change will allow for the development of specific radio models mentioned above to continue unimpeded by that work.
Most importantly, this change means the loss of history for all of the drivers currently in icomciv.py insofar as someone would have to know to look at the history of that module in order to find any history at all for all of the drivers being split out from here.
This particular situation arises anytime new source files are introduced into the code base; there is however precedence for this type of change on this code base, for example:
https://chirp.danplanet.com/projects/chirp/repository/revisions/d135e492dfa3 .
In our particular situation the history is not lost as in the previous revision. In fact, the icomciv.py file is retained in the repo along with its history.
I just don't see a good reason to make this change.
Every one of us is welcome to an opinion, in my own personal view I simply find that the separation of implementation classes from their inherited base class to be cleaner and good programming practice which in the long run creates for a healthier code base and development experience overall. In this case however the intent was not imposed merely for aesthetic reasons but rather to avoid conflict and allow for concurrent development to take place moving forward. Given the scope of the changed code itself is relatively small I see a small risk impact.
Personally, I would prefer to not see this patch applied at least until we understand why it might be helpful to the upcoming changes for #4547, since I just don't see the justification right now.
Martin. KD6YAM
On Fri, Apr 30, 2021 at 9:42 AM Kosta A. via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
# 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:")
chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
Hi Kosta,
Comments inline.
This change is precipitated by the requirement to support configurable
civ's (#4547) such that we are no longer able to hard code these values into the code base. While I agree that it would be ideal to not include it as part of the refactoring effort, so as to reduce its scope, the refactoring itself would introduce a circular dependency on the hard coded classes and thus makes it a requirement of this change.
You are assuming that the change to probe_model() would be applied *after* splitting up the file if it's not embedded within the same patch. But there is no reason that a patch to change probe_model() could not be applied *before* any other possible refactoring. It would, as you imply, make the Icom detect mechanism more consistent with the Kenwood one insofar as they would use the same driver enumeration mechanism, and at the same time remove the reliance on the module-global dict of radio models.
The intent of this change is to reduce potential conflicts amongst
developers working on similar radio models. Currently there are multiple implementations of icom live mode radios being introduced into the code base - this change should minimize the scope of those changes making it i) easier to develop and ii) reducing merge conflicts and iii) easier to test.
I don't really buy this. The rate of change in that file is very low. The addition of a radio involves (a) adding new classes that are not used in any way by other existing specific driver classes, and (b) *maybe* changing some common code if the new radio involves new features that none of the existing radios use. The former isn't going to introduce merge conflicts, and the latter will introduce them just as much if the common code is in a separate file as if it's in the same file. What changes is whether one or two files are involved, that's all.
As for easier testing, testing is a real issue with the Icom Live mode radios regardless of the changes you're proposing. I'm pretty sure that none of the developers has all of these radios. Any change to the common code - including yours - has the potential to break some or all of the others.
Another outcome of this change is to allow for a clear isolation of radio specific implementation details from the base class, thus reducing the potential for knock on effects from a developer introducing a new radio model while unknowingly breaking another.
Not really, no. Either the new radio involves changes to the common code or it doesn't. The potential for breakage doesn't change just because the common code is in a separate file.
The root of the impending changes to support #4547 stems from the current
implementation of how CIV values are handled, in that they are currently implemented as static variables and will need to become class variables. As this is a significant deviation to the implementation of the base IcomCIVRadio class, this change will allow for the development of specific radio models mentioned above to continue unimpeded by that work.
I get the requirement to move to a configurable ID value, and I get that changing probe_model() is a first step, eliminating the need for the current static dict. I'm not arguing against making that change. What you seem to be arguing is that splitting the file means that a slew of changes to the base class won't affect development of subclasses. In theory, it does keep the changes in separate files. However, if I'm working on a new radio, and the base class is undergoing changes right underneath my feet, so to speak, I'm going to be a bit concerned anyway about whether or not my new radio is going to work. And what if a change in the base class creates a need to update all of the existing subclasses? Then there are going to be merge conflicts anyway.
This particular situation arises anytime new source files are introduced into the code base; there is however precedence for this type of change on this code base, for example:
Precedent doesn't mean it's a good thing. :-)
In our particular situation the history is not lost as in the previous revision. In fact, the icomciv.py file is retained in the repo along with its history.
It's there, but who will know it's there in a couple of years? You and I (and Dan) might know where to look to find the previous commit messages, but someone coming along and looking to implement a new radio would not know where to look.
Every one of us is welcome to an opinion, in my own personal view I simply
find that the separation of implementation classes from their inherited base class to be cleaner and good programming practice which in the long run creates for a healthier code base and development experience overall. In this case however the intent was not imposed merely for aesthetic reasons but rather to avoid conflict and allow for concurrent development to take place moving forward. Given the scope of the changed code itself is relatively small I see a small risk impact.
Unless you tell me you have all of the Icom Live radios, I'm going to disagree on the level of risk. :-) How will you test?
Anyway, as you said, we're all entitled to our opinions. We've expressed ours, but of course the decisions belong to Dan. :-)
Martin. KD6YAM
Personally, I would prefer to not see this patch applied at least until we understand why it might be helpful to the upcoming changes for #4547, since I just don't see the justification right now.
Martin. KD6YAM
On Fri, Apr 30, 2021 at 9:42 AM Kosta A. via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
# 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:")
chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
- Changing the way probe_model() works to enumerate the global driver list instead of the local model list. This would have been a trivial change in and of itself, and is unrelated to the refactoring. Presumably this change is somehow necessitated by your approach to #4547, but it's not at all clear that it's a meaningful change. If it's needed at all, I would think it might make more sense as a part of the patch for #4547, and not as part of an unrelated refactoring.
This change is precipitated by the requirement to support configurable civ's (#4547) such that we are no longer able to hard code these values into the code base. While I agree that it would be ideal to not include it as part of the refactoring effort, so as to reduce its scope, the refactoring itself would introduce a circular dependency on the hard coded classes and thus makes it a requirement of this change. The directory method fwiw, is a fairly common practice in the codebase as it stands.
Yeah, I'm with Martin on this - the probe_model() change can and should come before the refactor. When I saw this patch yesterday, before the other comments, I had spotted functional changes and knew that it would need more investigation to make sure as little change came with the refactor as possible. I definitely don't want to mix the two.
The intent of this change is to reduce potential conflicts amongst developers working on similar radio models. Currently there are multiple implementations of icom live mode radios being introduced into the code base - this change should minimize the scope of those changes making it i) easier to develop and ii) reducing merge conflicts and iii) easier to test.
While there are multiple things going on right now, we've gone years where that hasn't been true. There's a lot of benefit of having them all in one file for contextual relativity, as well as consistency. It's a lot more likely that things will remain consistent in one file, and the drivers are so small that it seems a little silly to split them out, to me. I totally don't get how this split will improve testing. A compromise would be to merge your probe changes, which will allow for new models to be built in their own file instead of having to be in icomciv.py for the probe routine to work. Further, splitting each model one at a time would also make the manual verification a lot easier to do. Like Martin said, I don't have all of the models we support here available for testing, so I'd want to be extra sure we're not going to break any of them in the process.
Independent of whether or not we're going to merge the split, I'm not going to consider it until after Rick updates his 7300 patch. I asked him not to move things around in that file, so I'm definitely not going to move *everything* underneath him in the meantime :)
Another outcome of this change is to allow for a clear isolation of radio specific implementation details from the base class, thus reducing the potential for knock on effects from a developer introducing a new radio model while unknowingly breaking another.
I can appreciate this argument, but as simple as these are, I'm not really sure I see this as a strong one.
The root of the impending changes to support #4547 stems from the current implementation of how CIV values are handled, in that they are currently implemented as static variables and will need to become class variables. As this is a significant deviation to the implementation of the base IcomCIVRadio class, this change will allow for the development of specific radio models mentioned above to continue unimpeded by that work.
Most importantly, this change means the loss of history for all of the drivers currently in icomciv.py insofar as someone would have to know to look at the history of that module in order to find any history at all for all of the drivers being split out from here.
This particular situation arises anytime new source files are introduced into the code base; there is however precedence for this type of change on this code base, for example:
https://chirp.danplanet.com/projects/chirp/repository/revisions/d135e492dfa3.
In our particular situation the history is not lost as in the previous revision. In fact, the icomciv.py file is retained in the repo along with its history.
Yeah the history is there, but it's really annoying to look beyond that point. That reorganization was a holistic restructuring at a single point in time. It stemmed from an original not-very-modular history where CHIRP supported like six icom VHF radios which clearly didn't scale to hundreds of models and vendors, and was required to get on a cleaner path. You could argue the same for icomciv.py I guess, but the cost/benefit doesn't strike me as worth it.
On the topic of the configurable address feature, I'll be honest and say I'm not sure why this is important. I've got several Icom CIV radios and have never needed to change the address on any of them, for any reason or because software required it. I feel like in the past when there was more purpose-built hardware that interfaced with these radios, it may have been important, but I'm not sure why it is now. I'm interested in what the proposed solution(s) are, because I'm sure I will have an opinion. I think probably the only thing I'd really be in favor of is a hidden config option to override the address used for extreme circumstances, where the user really needs to be able to control it. I don't think it's important enough to justify exposing through the driver model and into the UI. If there are some real use-cases, I'd like to hear them.
Anyway, as I mentioned, I'm not going to apply the reorg part of the patch underneath Rick while he's working to rebase his stuff. If you want to submit the probe change now to facilitate future drivers being outside of the main module (and presumably the configurable address thing), then that's fine.
--Dan
Happy to break the change apart and wait for Rick as well...
Some further comments inline below.
Thanks Dan.
On Sat, May 1, 2021 at 10:18 AM Dan Smith via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
- Changing the way probe_model() works to enumerate the global driver
list instead of the local model list. This would have been a trivial change in and of itself, and is unrelated to the refactoring. Presumably this change is somehow necessitated by your approach to #4547, but it's not at all clear that it's a meaningful change. If it's needed at all, I would think it might make more sense as a part of the patch for #4547, and not as part of an unrelated refactoring.
This change is precipitated by the requirement to support configurable
civ's (#4547) such that we are no longer able to hard code these values into the code base. While I agree that it would be ideal to not include it as part of the refactoring effort, so as to reduce its scope, the refactoring itself would introduce a circular dependency on the hard coded classes and thus makes it a requirement of this change. The directory method fwiw, is a fairly common practice in the codebase as it stands.
Yeah, I'm with Martin on this - the probe_model() change can and should come before the refactor. When I saw this patch yesterday, before the other comments, I had spotted functional changes and knew that it would need more investigation to make sure as little change came with the refactor as possible. I definitely don't want to mix the two.
The intent of this change is to reduce potential conflicts amongst
developers working on similar radio models. Currently there are multiple implementations of icom live mode radios being introduced into the code base - this change should minimize the scope of those changes making it i) easier to develop and ii) reducing merge conflicts and iii) easier to test.
While there are multiple things going on right now, we've gone years where that hasn't been true. There's a lot of benefit of having them all in one file for contextual relativity, as well as consistency. It's a lot more likely that things will remain consistent in one file, and the drivers are so small that it seems a little silly to split them out, to me. I totally don't get how this split will improve testing. A compromise would be to merge your probe changes, which will allow for new models to be built in their own file instead of having to be in icomciv.py for the probe routine to work. Further, splitting each model one at a time would also make the manual verification a lot easier to do. Like Martin said, I don't have all of the models we support here available for testing, so I'd want to be extra sure we're not going to break any of them in the process.
Independent of whether or not we're going to merge the split, I'm not going to consider it until after Rick updates his 7300 patch. I asked him not to move things around in that file, so I'm definitely not going to move *everything* underneath him in the meantime :)
Another outcome of this change is to allow for a clear isolation of
radio specific implementation details from the base class, thus reducing the potential for knock on effects from a developer introducing a new radio model while unknowingly breaking another.
I can appreciate this argument, but as simple as these are, I'm not really sure I see this as a strong one.
The root of the impending changes to support #4547 stems from the
current implementation of how CIV values are handled, in that they are currently implemented as static variables and will need to become class variables. As this is a significant deviation to the implementation of the base IcomCIVRadio class, this change will allow for the development of specific radio models mentioned above to continue unimpeded by that work.
Most importantly, this change means the loss of history for all of the
drivers currently in icomciv.py insofar as someone would have to know to look at the history of that module in order to find any history at all for all of the drivers being split out from here.
This particular situation arises anytime new source files are introduced
into the code base; there is however precedence for this type of change on this code base, for example:
https://chirp.danplanet.com/projects/chirp/repository/revisions/d135e492dfa3.
In our particular situation the history is not lost as in the previous
revision. In fact, the icomciv.py file is retained in the repo along with its history.
Yeah the history is there, but it's really annoying to look beyond that point. That reorganization was a holistic restructuring at a single point in time. It stemmed from an original not-very-modular history where CHIRP supported like six icom VHF radios which clearly didn't scale to hundreds of models and vendors, and was required to get on a cleaner path. You could argue the same for icomciv.py I guess, but the cost/benefit doesn't strike me as worth it.
On the topic of the configurable address feature, I'll be honest and say I'm not sure why this is important. I've got several Icom CIV radios and have never needed to change the address on any of them, for any reason or because software required it. I feel like in the past when there was more purpose-built hardware that interfaced with these radios, it may have been important, but I'm not sure why it is now. I'm interested in what the proposed solution(s) are, because I'm sure I will have an opinion. I think probably the only thing I'd really be in favor of is a hidden config option to override the address used for extreme circumstances, where the user really needs to be able to control it. I don't think it's important enough to justify exposing through the driver model and into the UI. If there are some real use-cases, I'd like to hear them.
In terms of the CIV address change - it really is a question of user experience. First, each model of icom radios are given a unique ci-v address. ICom supports configuration of their CI-V address for the situation where multiple radios are connected to the same cable in order to be able to address each one individually - much like multiple peripherals on an I2C bus. But I suspect you knew that and really doubt that any chirp user is suffering from this limitation... most likely true.
As such, while there is technically nothing wrong with simply supporting the default ci-v address, the scenario which presents itself in chirp is that in cases of communication failures due to a misconfigured ci-v address there is no indication to the user as to which address chirp is utilizing and as such this has led users to believe it to be an issue with their cable and frustration ensues (no surprise there) .
It would also be a fair argument to say that at this point neither of these scenarios are a common occurrence and that how or if this features needs to be exposed into the UI or at all is of course a matter of discussion. I will add however, that laying the groundwork to support configurable ci-v addresses, opens the door for additional per 'radio model' configurable attributes to be utilized in the future; such as baud-rate, etc..
Without actually advocating for anything, here are a couple potential scenarios:
1. Do nothing for the minority of users impacted by this issue and hope they figure out to change their CI-V on their own. 2. Improve the error messaging to present the end-user with additional diagnostic information regarding their failed connection (may not be a bad idea either way). 3. Implement radio model attributes to support ci-v addresses - keeping the hard-coded defaults in place while allowing for them to be overridden by an optional user defined config file. 4. Exposing configurable attributes mentioned above in the UI in some manner that makes sense.
Anyway, as I mentioned, I'm not going to apply the reorg part of the patch underneath Rick while he's working to rebase his stuff. If you want to submit the probe change now to facilitate future drivers being outside of the main module (and presumably the configurable address thing), then that's fine.
--Dan _______________________________________________ chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
In terms of the CIV address change - it really is a question of user experience. First, each model of icom radios are given a unique ci-v address. ICom supports configuration of their CI-V address for the situation where multiple radios are connected to the same cable in order to be able to address each one individually - much like multiple peripherals on an I2C bus. But I suspect you knew that and really doubt that any chirp user is suffering from this limitation... most likely true.
Correct. I can imagine some complex automated stations needing two (say) IC-7000s connected to a single machine for some sort of tracking or repeating, but I suspect it is exceedingly rare.
As such, while there is technically nothing wrong with simply supporting the default ci-v address, the scenario which presents itself in chirp is that in cases of communication failures due to a misconfigured ci-v address there is no indication to the user as to which address chirp is utilizing and as such this has led users to believe it to be an issue with their cable and frustration ensues (no surprise there) .
I would think we could handle the UX issue by simply making sure the error message includes something about "Be sure your radio is configured for the default address of XX". Perhaps also with a link to a wiki doc about how to change it in the config if you *really* need it.
It would also be a fair argument to say that at this point neither of these scenarios are a common occurrence and that how or if this features needs to be exposed into the UI or at all is of course a matter of discussion. I will add however, that laying the groundwork to support configurable ci-v addresses, opens the door for additional per 'radio model' configurable attributes to be utilized in the future; such as baud-rate, etc..
We can add per-driver configuration options of course. Perhaps you mean communicating with two like radios on the same bus at different speeds? That seems like a counter-intuitive scenario for a shared-bus arrangement.
Without actually advocating for anything, here are a couple potential scenarios:
- Do nothing for the minority of users impacted by this issue and hope they figure out to change their CI-V on their own.
- Improve the error messaging to present the end-user with additional diagnostic information regarding their failed connection (may not be a bad idea either way).
- Implement radio model attributes to support ci-v addresses - keeping the hard-coded defaults in place while allowing for them to be overridden by an optional user defined config file.
- Exposing configurable attributes mentioned above in the UI in some manner that makes sense.
Yeah, #2 for sure, #3 if you want. I think #4 needs a lot more justification, personally. I'd rather wait on that until a reasonable number of people show up really wanting it to be configurable, and with some real-world use case for why.
--Dan
On Sun, May 2, 2021 at 8:04 AM Dan Smith via chirp_devel < chirp_devel@intrepid.danplanet.com> wrote:
Correct. I can imagine some complex automated stations needing two (say) IC-7000s connected to a single machine for some sort of tracking or repeating, but I suspect it is exceedingly rare.
I would think so too. And in this situation, the user has already figured out how to change the CI-V address on one of the radios in order to get that working. In my experience, people don't change their memory settings all that often (at least on a scale that necessitates Chirp), so to me it doesn't seem particularly onerous to tell them they need to change the CI-V address to the default before running Chirp and change it back afterwards. On the IC-910H, at least, going into 'set mode' to change the CI-V address is a matter of a few seconds.
Martin. KD6YAM
As such, while there is technically nothing wrong with simply supporting
the default ci-v address, the scenario which presents itself in chirp is that in cases of communication failures due to a misconfigured ci-v address there is no indication to the user as to which address chirp is utilizing and as such this has led users to believe it to be an issue with their cable and frustration ensues (no surprise there) .
I would think we could handle the UX issue by simply making sure the error message includes something about "Be sure your radio is configured for the default address of XX". Perhaps also with a link to a wiki doc about how to change it in the config if you *really* need it.
It would also be a fair argument to say that at this point neither of
these scenarios are a common occurrence and that how or if this features needs to be exposed into the UI or at all is of course a matter of discussion. I will add however, that laying the groundwork to support configurable ci-v addresses, opens the door for additional per 'radio model' configurable attributes to be utilized in the future; such as baud-rate, etc..
We can add per-driver configuration options of course. Perhaps you mean communicating with two like radios on the same bus at different speeds? That seems like a counter-intuitive scenario for a shared-bus arrangement.
Without actually advocating for anything, here are a couple potential
scenarios:
- Do nothing for the minority of users impacted by this issue and hope
they figure out to change their CI-V on their own.
- Improve the error messaging to present the end-user with additional
diagnostic information regarding their failed connection (may not be a bad idea either way).
- Implement radio model attributes to support ci-v addresses - keeping
the hard-coded defaults in place while allowing for them to be overridden by an optional user defined config file.
- Exposing configurable attributes mentioned above in the UI in some
manner that makes sense.
Yeah, #2 for sure, #3 if you want. I think #4 needs a lot more justification, personally. I'd rather wait on that until a reasonable number of people show up really wanting it to be configurable, and with some real-world use case for why.
--Dan _______________________________________________ chirp_devel mailing list chirp_devel@intrepid.danplanet.com http://intrepid.danplanet.com/mailman/listinfo/chirp_devel Developer docs: http://chirp.danplanet.com/projects/chirp/wiki/Developers
participants (4)
-
Dan Smith
-
Kosta A.
-
Kosta Arvanitis
-
Martin Cooper