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