Thanks for the feedback Martin, please see my responses inline below. I remain open to suggestions and feedback.
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.
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.
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.
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.
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:
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
# 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