[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

Kosta A.
Fri Apr 30 09:42:04 PDT 2021


# HG changeset patch
# User Kosta A. <ve7kcy at 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 at 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
+
+ at 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 at 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
+
+ at 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 at 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__)
+
+
+ at 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 at 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
+
+ at 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 at 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
+
+ at 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 at 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
+
+ at 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 at 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()))
 
 
- at 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)
-
-
- at 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
-
-
- at 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)
-
-
- at 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)
-
-
- at 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
-
-
- at 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:")



More information about the chirp_devel mailing list