[chirp_devel] Icom IC-7300 Clone-mode driver

Rick (AA0RD) DeWitt
Wed Feb 10 07:32:08 PST 2021


Issue #4013; new clone-mode driver for IC-7300 with duplex Split mode.
-- 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210210/985fa981/attachment-0001.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Signature-RIck-AA0RD-Image.jpg
Type: image/jpeg
Size: 18469 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210210/985fa981/attachment-0001.jpg 
-------------- next part --------------
./chirp/__init__.py
./chirp/bandplan.py
./chirp/bandplan_au.py
./chirp/bandplan_iaru_r1.py
./chirp/bandplan_iaru_r2.py
./chirp/bandplan_iaru_r3.py
./chirp/bandplan_na.py
./chirp/bitwise.py
./chirp/bitwise_grammar.py
./chirp/chirp_common.py
./chirp/detect.py
./chirp/directory.py
./chirp/drivers/__init__.py
./chirp/drivers/alinco.py
./chirp/drivers/anytone.py
./chirp/drivers/anytone_ht.py
./chirp/drivers/ap510.py
./chirp/drivers/baofeng_common.py
./chirp/drivers/baofeng_uv3r.py
./chirp/drivers/baofeng_wp970i.py
./chirp/drivers/bjuv55.py
./chirp/drivers/btech.py
./chirp/drivers/ft1500m.py
./chirp/drivers/ft1802.py
./chirp/drivers/ft1d.py
./chirp/drivers/ft2800.py
./chirp/drivers/ft2900.py
./chirp/drivers/ft4.py
./chirp/drivers/ft50.py
./chirp/drivers/ft60.py
./chirp/drivers/ft7100.py
./chirp/drivers/ft7800.py
./chirp/drivers/ft817.py
./chirp/drivers/ft818.py
./chirp/drivers/ft857.py
./chirp/drivers/ft90.py
./chirp/drivers/ftm350.py
./chirp/drivers/generic_csv.py
./chirp/drivers/generic_tpe.py
./chirp/drivers/generic_xml.py
./chirp/drivers/gmrsuv1.py
./chirp/drivers/h777.py
./chirp/drivers/ic208.py
./chirp/drivers/ic2100.py
./chirp/drivers/ic2200.py
./chirp/drivers/ic2720.py
./chirp/drivers/ic2730.py
./chirp/drivers/ic2820.py
./chirp/drivers/ic7300.py
./chirp/drivers/ic9x.py
./chirp/drivers/ic9x_icf.py
./chirp/drivers/ic9x_icf_ll.py
./chirp/drivers/ic9x_ll.py
./chirp/drivers/icf.py
./chirp/drivers/icomciv.py
./chirp/drivers/icq7.py
./chirp/drivers/ict70.py
./chirp/drivers/ict7h.py
./chirp/drivers/ict8.py
./chirp/drivers/icw32.py
./chirp/drivers/icx8x.py
./chirp/drivers/icx8x_ll.py
./chirp/drivers/id31.py
./chirp/drivers/id51.py
./chirp/drivers/id800.py
./chirp/drivers/id880.py
./chirp/drivers/idrp.py
./chirp/drivers/kenwood_hmk.py
./chirp/drivers/kenwood_itm.py
./chirp/drivers/kenwood_live.py
./chirp/drivers/kguv8d.py
./chirp/drivers/kguv9dplus.py
./chirp/drivers/kyd.py
./chirp/drivers/leixen.py
./chirp/drivers/lt725uv.py
./chirp/drivers/mursv1.py
./chirp/drivers/puxing.py
./chirp/drivers/radioddity_r2.py
./chirp/drivers/retevis_rt1.py
./chirp/drivers/retevis_rt21.py
./chirp/drivers/retevis_rt22.py
./chirp/drivers/retevis_rt23.py
./chirp/drivers/retevis_rt26.py
./chirp/drivers/rfinder.py
./chirp/drivers/tdxone_tdq8a.py
./chirp/drivers/template.py
./chirp/drivers/th350.py
./chirp/drivers/th9800.py
./chirp/drivers/th_uv3r.py
./chirp/drivers/th_uv3r25.py
./chirp/drivers/th_uv8000.py
./chirp/drivers/th_uv88.py
./chirp/drivers/th_uvf8d.py
./chirp/drivers/thd72.py
./chirp/drivers/thuv1f.py
./chirp/drivers/tk8102.py
./chirp/drivers/tk8180.py
./chirp/drivers/tmd710.py
./chirp/drivers/tmv71.py
./chirp/drivers/tmv71_ll.py
./chirp/drivers/ts480.py
./chirp/drivers/ts590.py
./chirp/drivers/uv5r.py
./chirp/drivers/uv5x3.py
./chirp/drivers/uv6r.py
./chirp/drivers/uvb5.py
./chirp/drivers/vgc.py
./chirp/drivers/vx170.py
./chirp/drivers/vx2.py
./chirp/drivers/vx3.py
./chirp/drivers/vx5.py
./chirp/drivers/vx510.py
./chirp/drivers/vx6.py
./chirp/drivers/vx7.py
./chirp/drivers/vx8.py
./chirp/drivers/vxa700.py
./chirp/drivers/wouxun.py
./chirp/drivers/wouxun_common.py
./chirp/drivers/yaesu_clone.py
./chirp/elib_intl.py
./chirp/errors.py
./chirp/import_logic.py
./chirp/logger.py
./chirp/memmap.py
./chirp/platform.py
./chirp/pyPEG.py
./chirp/radioreference.py
./chirp/settings.py
./chirp/ui/__init__.py
./chirp/ui/bandplans.py
./chirp/ui/bankedit.py
./chirp/ui/clone.py
./chirp/ui/cloneprog.py
./chirp/ui/common.py
./chirp/ui/config.py
./chirp/ui/dstaredit.py
./chirp/ui/editorset.py
./chirp/ui/fips.py
./chirp/ui/importdialog.py
./chirp/ui/inputdialog.py
./chirp/ui/mainapp.py
./chirp/ui/memdetail.py
./chirp/ui/memedit.py
./chirp/ui/miscwidgets.py
./chirp/ui/radiobrowser.py
./chirp/ui/reporting.py
./chirp/ui/settingsedit.py
./chirp/ui/shiftdialog.py
./chirp/util.py
./chirp/xml_ll.py
./chirpc
./chirpw
./locale/check_parameters.py
./rpttool
./setup.py
./share/make_supported.py
./tests/__init__.py
./tests/run_tests
./tests/unit/__init__.py
./tests/unit/base.py
./tests/unit/test_bitwise.py
./tests/unit/test_chirp_common.py
./tests/unit/test_import_logic.py
./tests/unit/test_mappingmodel.py
./tests/unit/test_memedit_edits.py
./tests/unit/test_platform.py
./tests/unit/test_settings.py
./tests/unit/test_shiftdialog.py
./tools/bitdiff.py
./tools/cpep8.py
./tools/img2thd72.py
-------------- next part --------------
# HG changeset patch
# User Rick DeWitt <aa0rd at yahoo.com>
# Date 1612970702 28800
#      Wed Feb 10 07:25:02 2021 -0800
# Node ID cb68077daffff2b99b11ee63c453b736601247aa
# Parent  786c6252a1edeea27e66a22cc028b8f4b42bb964
[ic7300] New clone-mode driver for Icom IC-7300 Issue # 4013
Includes duplex Split mode, no tuning steps non-US modes

diff -r 786c6252a1ed -r cb68077dafff chirp/drivers/ic7300.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/ic7300.py	Wed Feb 10 07:25:02 2021 -0800
@@ -0,0 +1,739 @@
+# Copyright 2020 Rick DeWitt <aa0rd at yahoo.com>
+# Icom IC-7300 Clone Mode
+#   Vers 1.1: Channel memory and some important settings
+#       Using CI-V data requests to generate a memory block
+#   Vers 1.1 uses py3 bytes()
+#   Vers 1.2 Moved BAUD from global to radio property
+#   Vers 1.3 Added non-USA data modes, opened TX range, no tuning steps
+#            Added true Split duplex mode support
+# 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 time
+import struct
+import logging
+import re
+import math
+from chirp import chirp_common, directory, memmap
+from chirp import bitwise, errors, util
+from chirp.settings import RadioSettingGroup, RadioSetting, \
+    RadioSettingValueBoolean, RadioSettingValueList, \
+    RadioSettingValueString, RadioSettingValueInteger, \
+    RadioSettingValueFloat, RadioSettings, InvalidValueError
+from textwrap import dedent
+
+LOG = logging.getLogger(__name__)
+
+HAS_FUTURE = True
+try:                         # PY3 compliance
+    from builtins import bytes
+except ImportError:
+    HAS_FUTURE = False
+    LOG.warning('python-future package is not '
+                'available; %s requires it' % __name__)
+
+MEM_FORMAT = """
+#seekto 0x0000;
+struct {            // 32 bytes per chan
+  u8   split:4,     // My CI-V format, not an image
+       selmem:4;
+  ul32 rxfreq;
+  ul32 txfreq;
+  u8   tmode;
+  u8   fltr;
+  u8   data:4,
+       tone:4;
+  u8 rcts;
+  u8 tsql;
+  char name[10];
+  u8   chpad[8];
+} ch_mem[99];
+
+struct {
+  u8   rfpwr;
+  u8   beepv;
+  ul16 hf_ofst;
+  ul16 m6_ofst;
+  u8   scrnsav;
+  char  msg[10];
+  u8   uprflgs:3,
+       rfpwron:1,
+       cfmbeep:1,
+       opnmsg:1,
+       hfsign:1,
+       m6sign:1;
+} settings;
+
+"""
+# === Globals ====
+TMODES = ["", "Tone", "TSQL"]
+DUPLEX = ["", "-", "+", "split"]
+MODES = ["LSB", "USB", "AM", "CW", "RTTY", "FM", "CWR", "RTTYR",
+         "Data+LSB", "Data+USB", "Data+AM", "N/A", "N/A", "Data+FM"]
+TONES = list(chirp_common.TONES)
+FILTER = ["FIL1", "FIL2", "FIL3"]
+STIMEOUT = 0.6
+BAUDRATES = [115200, 57600, 38400, 19200, 9600, 4800, 2400]
+
+
+def _setbaud(radio):
+    """ Determine fastest valid baud rate """
+    radio.pipe.timeout = 0.1
+    for radio.BAUD in BAUDRATES:       # Attempt to read CI-V status
+        radio.pipe.baudrate = radio.BAUD
+        radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x71\xFD")
+        resp = radio.pipe.read(55)
+        if len(resp) > 17:
+            if resp[17] == "\x01":
+                break        # exit For loop
+    if radio.BAUD < 4800:
+        msg = "Unable to communicate with radio! Cable? Power?"
+        raise errors.RadioError(msg)
+    LOG.debug("Baud Rate is %d" % radio.BAUD)
+    radio.pipe.timeout = STIMEOUT
+    return
+
+
+def _rhcd_decode(stx, nbyt, lgw=False):
+    """ Decode decimal value from reverse-hex stx backwards starting
+        at nbyt index. 4 for freq, 3 for offset.
+        Returns 4-byte UL32 string. Ignore upper 0's if format is less"""
+    vstr = ""
+    for ix in range(nbyt-1, -1, -1):
+        vstr += "%02x" % ord(stx[ix])
+    vx = int(vstr)
+    if lgw:
+        LOG.warning("vstr: %s, vs: %04x" % (vstr, vx))
+    vstr = chr(vx & 255)
+    vx = vx >> 8
+    vstr += chr(vx & 255)
+    vx = vx >> 8
+    vstr += chr(vx & 255)
+    vx = vx >> 8
+    vstr += chr(vx & 255)
+    return vstr
+
+
+def _fhdc_decode(stx, nbyt, lgw=False):
+    """ Decode decimal value from hex string stx, not reversed.
+        nbyt is length """
+    vstr = ""
+    for ix in range(0, nbyt):      # Decode hcd rcts
+        vstr += "%02x" % ord(stx[ix])
+    vx = int(vstr)
+    if lgw:     # print values
+        LOG.warning("fhdc vstr: %s, vx: %i" % (vstr, vx))
+    return vx
+
+
+def _encode_chn(chnum):
+    """  Channel pair must be encoded as hex coded decimal """
+    c1 = 0
+    if chnum < 10:
+        c2 = chnum
+    elif chnum > 9 and chnum < 20:
+        c2 = chnum + 6
+    elif chnum > 19 and chnum < 30:
+        c2 = chnum + 12
+    elif chnum > 29 and chnum < 40:
+        c2 = chnum + 18
+    elif chnum > 39 and chnum < 50:
+        c2 = chnum + 24
+    elif chnum > 49 and chnum < 60:
+        c2 = chnum + 30
+    elif chnum > 59 and chnum < 70:
+        c2 = chnum + 36
+    elif chnum > 69 and chnum < 80:
+        c2 = chnum + 42
+    elif chnum > 79 and chnum < 90:
+        c2 = chnum + 48
+    elif chnum > 89 and chnum < 100:
+        c2 = chnum + 54
+    elif chnum > 99:
+        c1 = 1
+        c2 = chnum - 100
+    chstr = chr(c1) + chr(c2)
+    return chstr
+
+
+def _read_mem(radio):
+    """Generate the memory map. """
+    radio.pipe.baudrate = radio.BAUD
+
+    status = chirp_common.Status()
+    status.cur = 0
+    status.max = radio._upper + 10  # 10 settings
+    status.msg = "Reading Memory..."
+    radio.status_fn(status)
+
+    mtx = ""
+    for ix in range(0, 32):      # blank channel block
+        mtx += chr(0)
+    memdat = ""
+    preamble = "\xFE\xFE\x94\xE0\x1A\x00"
+    for chn in range(1, 100):       # 1-99
+        vstr = _encode_chn(chn)
+        cmdx = preamble + vstr + "\xFD"
+        radio.pipe.write(cmdx)
+        resp = radio.pipe.read(18)    # read only the echo and Split bytes
+        if resp[17] == "\xFF":        # This chan is empty
+            memdat += mtx
+            resp = radio.pipe.read(1)   # the trailing FD
+        else:       # read the rest and decode
+            split = resp[17]
+            resp = radio.pipe.read(39)
+            resp = split + resp[:38]     # strip trailing FD
+            memdat += resp[0]       # split & selmem
+            memdat += _rhcd_decode(resp[1:6], 4)        # rxfreq string
+            memdat += _rhcd_decode(resp[15:20], 4)      # txfreq
+            memdat += resp[6]      # tmode
+            vstr = resp[7]         # IF Filter
+            if vstr == "\x00":     # Not valid, must be 1 - 3
+                vstr = "\x01"
+            memdat += vstr
+            memdat += resp[8]        # data & tone
+            v2 = _fhdc_decode(resp[9:], 3)            # Decode  hcd rcts
+            vx = float(v2) / 10
+            ix = TONES.index(vx)
+            memdat += chr(ix)
+            v2 = _fhdc_decode(resp[12:], 3)            # Decode hcd tsql
+            vx = float(v2) / 10
+            ix = TONES.index(vx)
+            memdat += chr(ix)
+            memdat += resp[29:]         # name
+            memdat += mtx[0:8]          # pad
+        # UI Update
+        status.cur = chn
+        radio.status_fn(status)
+    # End for chn loop
+    if len(memdat) == 0:       # To satisfy run_tests
+        raise errors.RadioError('No data received.')
+    # Read some settings
+    vbits = 0
+    radio.pipe.write("\xFE\xFE\x94\xE0\x14\x0A\xFD")    # 14 0A: RF Power
+    resp = radio.pipe.read(16)
+    rfp = _fhdc_decode(resp[13:], 2)          # Decode rf power 0-255
+    memdat += chr(rfp)
+    status.cur += 1
+    radio.status_fn(status)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x21\xFD")
+    resp = radio.pipe.read(20)              # Beep volume 00-255
+    bpv = _fhdc_decode(resp[17:], 2)
+    memdat += chr(bpv)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x31\xFD")
+    resp = radio.pipe.read(22)              # HF Offset
+    ohfs = _rhcd_decode(resp[17:], 3)       # 4-byte RHCD string
+    memdat += ohfs[:2]                      # only store as 16 bit
+    status.cur += 1
+    radio.status_fn(status)
+    if resp[20] == "\x01":                  # sign bit set
+        vbits += 2
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x32\xFD")
+    resp = radio.pipe.read(22)              # 6m Offset
+    o6ms = _rhcd_decode(resp[17:], 3)
+    memdat += o6ms[:2]
+    status.cur += 1
+    radio.status_fn(status)
+    if resp[20] == "\x01":
+        vbits += 1
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x89\xFD")
+    resp = radio.pipe.read(19)     # Screen Saver Timeout
+    memdat += resp[17]
+    status.cur += 1
+    radio.status_fn(status)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x23\xFD")
+    resp = radio.pipe.read(19)     # Confirmation Beep
+    if resp[17] == "\x01":
+        vbits += 8
+    status.cur += 1
+    radio.status_fn(status)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x90\xFD")
+    resp = radio.pipe.read(19)     # Opening Msg on/off
+    if resp[17] == "\x01":
+        vbits += 4
+    status.cur += 1
+    radio.status_fn(status)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x92\xFD")
+    resp = radio.pipe.read(19)     # Show RF power at power on
+    if resp[17] == "\x01":
+        vbits += 16
+    status.cur += 1
+    radio.status_fn(status)
+    radio.pipe.write("\xFE\xFE\x94\xE0\x1A\x05\x00\x91\xFD")
+    resp = radio.pipe.read(30)     # Opening Msg contents: 10 chars
+    memdat += resp[17:27]
+    status.cur += 1
+    radio.status_fn(status)
+    memdat += chr(vbits)
+    return memdat
+
+
+def _rhcd_encode(inum):
+    """ Convert integer inum into reverse hex coded decimal string """
+    sval = "%010d" % inum
+    hval = chr(int(sval[8:], 16)) + chr(int(sval[6:8], 16))
+    hval += chr(int(sval[4:6], 16)) + chr(int(sval[2:4], 16))
+    hval += chr(int(sval[:2], 16))
+    return hval      # 5 bytes
+
+
+def _fhcd_encode(inum):
+    """ Convert integer inum into forward hex coded decimal string """
+    sval = "%010d" % inum
+    hval = chr(int(sval[:2], 16)) + chr(int(sval[2:4], 16))
+    hval += chr(int(sval[4:6], 16)) + chr(int(sval[6:8], 16))
+    hval += chr(int(sval[8:], 16))
+    return hval     # 5 bytes
+
+
+def _sendcmd(radio, stat, cmds):
+    """ Send the cmds command string to the radio """
+    radio.pipe.write(cmds)
+    kx = len(cmds) + 6      # Cmds echo and status
+    stx = radio.pipe.read(kx)          # Read status
+    slx = len(stx)
+    if slx < kx:
+        msg = "No response from radio. Cable? Power?"
+        raise errors.RadioError(msg)
+    # Raise error if next to last status char is not \xFB
+    if stx[slx - 2] != "\xFB":
+        msg = "Verification error writing to radio."
+        raise errors.RadioError(msg)
+    stat.cur += 1
+    radio.status_fn(stat)
+    return
+
+
+def _write_mem(radio):
+    """ Send CI-V data to radio """
+    radio.pipe.baudrate = radio.BAUD
+    # Flush the input buffer
+    radio.pipe.timeout = 0.005
+    junk = radio.pipe.read(256)
+    radio.pipe.timeout = STIMEOUT
+
+    status = chirp_common.Status()
+    status.cur = 0
+    status.max = radio._upper + 7  # 8 settings
+    status.msg = "Writing Memory..."
+    radio.status_fn(status)
+
+    preamble = "\xFE\xFE\x94\xE0\x1A\x00"
+    for chn in range(1, 100):
+        _mem = radio._memobj.ch_mem[chn - 1]
+        vstr = _encode_chn(chn)
+        cmdx = preamble + vstr
+        if _mem.rxfreq == 0:
+            cmdx += "\xFF\xFD"
+        else:
+            datstr = chr((_mem.split << 4) + _mem.selmem)
+            datstr += _rhcd_encode(_mem.rxfreq)
+            datstr += chr(_mem.tmode)
+            if _mem.fltr < 1:      # Not allowed
+                _mem.fltr = 1
+            datstr += chr(_mem.fltr)
+            datstr += chr((_mem.data << 4) + _mem.tone)
+            datstr += _fhcd_encode(TONES[_mem.rcts] * 10.0)[2:]
+            datstr += _fhcd_encode(TONES[_mem.tsql] * 10.0)[2:]
+            # Repeat tx freq and same tmode, fltr, data, tone, rcts, tsql
+            datstr += _rhcd_encode(_mem.txfreq)
+            datstr += chr(_mem.tmode)
+            datstr += chr(_mem.fltr)
+            datstr += chr((_mem.data << 4) + _mem.tone)
+            datstr += _fhcd_encode(TONES[_mem.rcts] * 10.0)[2:]
+            datstr += _fhcd_encode(TONES[_mem.tsql] * 10.0)[2:]
+            datstr += str(_mem.name)
+            cmdx += datstr + "\xFD"
+        _sendcmd(radio, status, cmdx)
+    # Send Settings
+    _sets = radio._memobj.settings
+    preamble = "\xFE\xFE\x94\xE0"
+    vstr = _fhcd_encode(int(_sets.rfpwr))[3:]       # RF Power 0-255 hcd
+    cmdx = "%s\x14\x0A%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = chr(int(_sets.rfpwron))             # RF power msg at poweron
+    cmdx = "%s\x1A\x05\x00\x92%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = str(_sets.msg).upper()     # Requires uppercase
+    cmdx = "%s\x1A\x05\x00\x91%s\xFD" % (preamble, vstr)    # opening msg
+    _sendcmd(radio, status, cmdx)
+    vstr = chr(int(_sets.opnmsg))             # open msg enabled
+    cmdx = "%s\x1A\x05\x00\x90%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = chr(int(_sets.cfmbeep))             # Confirmation beep
+    cmdx = "%s\x1A\x05\x00\x23%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = _fhcd_encode(int(_sets.beepv))[3:]   # Beep volume 0-255
+    cmdx = "%s\x1A\x05\x00\x21%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = chr(int(_sets.scrnsav))   # Screen saver timeout
+    cmdx = "%s\x1A\x05\x00\x89%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = _rhcd_encode(_sets.hf_ofst)[:3] + chr(int(_sets.hfsign))
+    cmdx = "%s\x1A\x05\x00\x31%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = _rhcd_encode(_sets.m6_ofst)[:3] + chr(int(_sets.m6sign))
+    cmdx = "%s\x1A\x05\x00\x32%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    return
+
+
+ at directory.register
+class IC7300Radio(chirp_common.CloneModeRadio):
+    """Icom IC-7300"""
+    VENDOR = "Icom"
+    MODEL = "IC-7300"
+    BAUD = 115200
+
+    _upper = 99
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_name = True
+        rf.has_settings = True
+        rf.has_dtcs = False
+        rf.has_dtcs_polarity = False
+        rf.has_bank = False
+        rf.has_tuning_step = False
+        rf.can_odd_split = True
+        rf.memory_bounds = (1, 99)
+        rf.valid_modes = list(MODES)
+        rf.valid_tmodes = list(TMODES)
+        rf.valid_duplexes = list(set(DUPLEX))
+        rf.has_nostep_tuning = True
+        rf.valid_bands = [(1800000, 70500000)]
+        rf.valid_skips = []
+        rf_valid_tones = list(TONES)
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_name_length = 10
+        return rf
+
+    @classmethod
+    def get_prompts(cls):
+        rp = chirp_common.RadioPrompts()
+        rp.info = _(dedent("""\
+            Use the channel 'Properties' window, 'Other' tab to
+                set the IF Filter bandwidth and assign the Selected
+                Memory scan group.
+            For true Split-Frequency operation, set the duplex mode to
+                'split' and enter the TX frequency as the 'Offset'.
+            CHIRP does not currently support split TX/RX modulation
+                or data modes.
+            """))
+        rp.pre_download = _(dedent("""\
+            Follow these instructions to download your config:
+
+            1 - Connect a USB type B (Printer) cable to the rear USB jack.
+            2 - In the radio MENU > SET > Connectors > CI-V
+                Set/Verify the CI-V Baud Rate is "Auto"
+                Set/Verify the CI-V Address is "94h"
+                Set/Verify the CI-V Transeive is "ON"
+            3 - In the radio MENU > SET > Connectors > USB Serial Function
+                Set/Verify the mode is "CI-V"
+            4 - Radio > Download from radio
+            """))
+        rp.pre_upload = _(dedent("""\
+            Follow these instructions to upload your config:
+
+            1 - Verify the MENU configuration is the same as for download.
+            2 - Connect a USB type B cable to the rear USB jack.
+            3 - Radio > Upload to radio
+            """))
+        return rp
+
+    def sync_in(self):
+        """Download from radio"""
+        try:
+            _setbaud(self)
+            data = bytes(_read_mem(self))
+        except errors.RadioError:
+            # Pass through any real errors we raise
+            raise
+        except Exception:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during download')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+
+        self._mmap = memmap.MemoryMapBytes(data)
+        self.process_mmap()
+        return
+
+    def sync_out(self):
+        """Upload to radio"""
+        try:
+            _setbaud(self)
+            _write_mem(self)
+        except errors.RadioError:
+            # Pass through any real errors we raise
+            raise
+        except Exception:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during upload')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+        return
+
+    def process_mmap(self):
+        """Process the mem map into the mem object"""
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+        return
+
+    def get_memory(self, number):
+        """Convert raw channel data (_mem) into UI columns (mem)"""
+        mem = chirp_common.Memory()
+        _mem = self._memobj.ch_mem[number - 1]
+        mem.number = number
+        mnx = ""
+        if _mem.rxfreq == 0:
+            mem.empty = True
+            return mem
+        for char in _mem.name:
+            mnx += chr(char)
+        mem.name = mnx.rstrip()
+        mem.empty = False
+        mem.freq = int(_mem.rxfreq)
+        mem.duplex = DUPLEX[0]    # None by default
+        mem.offset = 0
+        if _mem.split:
+            mem.duplex = DUPLEX[3]
+            mem.offset = _mem.txfreq
+        elif _mem.rxfreq < _mem.txfreq:   # + shift
+            mem.duplex = DUPLEX[2]
+            mem.offset = _mem.txfreq - _mem.rxfreq
+        elif _mem.rxfreq > _mem.txfreq:   # - shift
+            mem.duplex = DUPLEX[1]
+            mem.offset = _mem.rxfreq - _mem.txfreq
+        elif _mem.txfreq == 0:
+            # leave offset alone, or run_tests will bomb
+            mem.duplex = DUPLEX[0]
+        mem.mode = MODES[_mem.tmode + (_mem.data * 8)]
+        mem.tmode = TMODES[_mem.tone]
+        mem.ctone = TONES[_mem.tsql]
+        mem.rtone = TONES[_mem.rcts]
+
+        # Extra
+        mem.extra = RadioSettingGroup("extra", "Extra")
+
+        options = ["N/A", "Fil 1: Wide", "Fil 2: Mid", "Fil 3: Narrow"]
+        vx = int(_mem.fltr)
+        if vx == 0:
+            vx = 1
+        rx = RadioSettingValueList(options, options[vx])
+        rset = RadioSetting("fltr", "IF Filter BW", rx)
+        mem.extra.append(rset)
+
+        options = ["None", "*1", "*2", "*3"]
+        vx = int(_mem.selmem)
+        rx = RadioSettingValueList(options, options[vx])
+        rset = RadioSetting("selmem", "Selected Memory Scan Group", rx)
+        mem.extra.append(rset)
+
+        return mem
+
+    def set_memory(self, mem):
+        """Convert UI column data (mem) into MEM_FORMAT memory (_mem)"""
+        _mem = self._memobj.ch_mem[mem.number - 1]
+
+        if mem.empty:
+            _mem.split = 0
+            _mem.selmem = 0
+            _mem.txfreq = 0
+            _mem.rxfreq = 0
+            _mem.tmode = 0
+            _mem.fltr = 1       # fltr can't be <1
+            _mem.data = 0
+            _mem.tone = 0
+            _mem.rcts = 8
+            _mem.tsql = 8
+            _mem.name = "          "
+            return
+
+        _mem.rxfreq = mem.freq
+        _mem.split = 0
+        if mem.duplex == "":
+            _mem.txfreq = _mem.rxfreq
+        elif mem.duplex == "+":
+            _mem.txfreq = mem.freq + mem.offset
+        elif mem.duplex == "split":
+            _mem.split = 1
+            _mem.txfreq = mem.offset
+        else:
+            _mem.txfreq = mem.freq - mem.offset
+        if "Data+" in mem.mode:
+            _mem.tmode = MODES.index(mem.mode) - 8
+            _mem.data = 1
+        else:
+            _mem.tmode = MODES.index(mem.mode)
+            _mem.data = 0
+        _mem.tone = 0
+        if mem.tmode == "Tone":
+            _mem.tone = 1
+        if mem.tmode == "TSQL":
+            _mem.tone = 2
+        _mem.rcts = chirp_common.TONES.index(mem.rtone)
+        _mem.tsql = chirp_common.TONES.index(mem.ctone)
+        _mem.name = mem.name.ljust(10)
+
+        # Extra settings
+        for setting in mem.extra:
+            setattr(_mem, setting.get_name(), setting.value)
+
+        return
+
+    def get_settings(self):
+        """Translate the MEM_FORMAT structs into settings in the UI"""
+        # Define mem struct write-back shortcuts
+        _sets = self._memobj.settings
+        basic = RadioSettingGroup("basic", "Basic Settings")
+        groups = RadioSettings(basic)
+
+        def chars2str(cary, knt):
+            """Convert raw memory char array to a string.
+               NOT a callback."""
+            stx = ""
+            for char in cary[:knt]:
+                stx += chr(char)
+            stx = stx.ljust(10)
+            return stx
+
+        def _do_ofst(setting, obj, atrb, arg):
+            """ Adjust the offset freq and sset sign bit """
+            sv = str(setting.value)
+            vz = float(sv)
+            if atrb == "hf_ofst":
+                if vz < 0:
+                    setattr(obj, "hfsign", 1)
+                    vz = abs(vz)
+                else:
+                    setattr(obj, "hfsign", 0)
+            else:
+                if vz < 0:
+                    setattr(obj, "m6sign", 1)
+                    vz = abs(vz)
+                else:
+                    setattr(obj, "m6sign", 0)
+            vz = vz * arg
+            setattr(obj, atrb, int(vz))
+            return
+
+        def _domath(setting, obj, atrb, opr, arg):
+            """ Apply a math operation to the modified atrb
+            If opr = "M" then multiply by arg, else add arg. """
+            sv = str(setting.value)
+            vz = float(sv)
+            if opr == "M":
+                vz = round(vz * arg)
+            else:
+                vz = vz + arg
+            setattr(obj, atrb, int(vz))
+            return
+
+        # --- Basic
+        vlu = round(int(_sets.rfpwr) / 2.55)
+        rx = RadioSettingValueInteger(0, 100, vlu)
+        rset = RadioSetting("settings.rfpwr",
+                            "RF Power (%)", rx)
+        rset.set_apply_callback(_domath, _sets, "rfpwr", "M", 2.55)
+        basic.append(rset)
+
+        rx = RadioSettingValueBoolean(bool(_sets.rfpwron))
+        rset = RadioSetting("settings.rfpwron",
+                            "Show RF Power at power on", rx)
+        basic.append(rset)
+
+        rx = RadioSettingValueBoolean(bool(_sets.opnmsg))
+        rset = RadioSetting("settings.opnmsg",
+                            "Show MyCall message at power on", rx)
+        basic.append(rset)
+
+        tmp = chars2str(_sets.msg, 10)
+        rx = RadioSettingValueString(0, 10, tmp)
+        rset = RadioSetting("settings.msg",
+                            "MyCall Power-on message", rx)
+        basic.append(rset)
+
+        rx = RadioSettingValueBoolean(bool(_sets.cfmbeep))
+        rset = RadioSetting("settings.cfmbeep", "Confirmation Beep", rx)
+        basic.append(rset)
+
+        vlu = round(_sets.beepv / 2.55)
+        rx = RadioSettingValueInteger(0, 255, vlu)
+        rset = RadioSetting("settings.beepv",
+                            "Confirmation Beep Volume (%)", rx)
+        rset.set_apply_callback(_domath, _sets, "beepv", "M", 2.55)
+        basic.append(rset)
+
+        options = ["Off", "15 Mins", "30 Mins", "60 Mins"]
+        rx = RadioSettingValueList(options, options[_sets.scrnsav])
+        rset = RadioSetting("settings.scrnsav",
+                            "Screensaver Timeout", rx)
+        basic.append(rset)
+
+        vlu = int(_sets.hf_ofst)
+        vlu = vlu / 10000.0
+        if _sets.hfsign:
+            vlu = -vlu
+        rx = RadioSettingValueFloat(-9.999, 9.999, vlu, 0.001, 4)
+        rset = RadioSetting("settings.hf_ofst",
+                            "HF Band default offset (Mhz)", rx)
+        rset.set_apply_callback(_do_ofst, _sets, "hf_ofst", 10000.0)
+        basic.append(rset)
+
+        vlu = int(_sets.m6_ofst)
+        vlu = vlu / 10000.0
+        if _sets.m6sign:
+            vlu = -vlu
+        rx = RadioSettingValueFloat(-9.999, 9.999, vlu, 0.001, 4)
+        rset = RadioSetting("settings.m6_ofst",
+                            "HF Band default offset (Mhz)", rx)
+        rset.set_apply_callback(_do_ofst, _sets, "m6_ofst", 10000.0)
+        basic.append(rset)
+
+        return groups
+
+    def set_settings(self, settings):
+        _settings = self._memobj.settings
+        for element in settings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            else:
+                try:
+                    name = element.get_name()
+                    if "." in name:
+                        bits = name.split(".")
+                        obj = self._memobj
+                        for bit in bits[:-1]:
+                            if "/" in bit:
+                                bit, index = bit.split("/", 1)
+                                index = int(index)
+                                obj = getattr(obj, bit)[index]
+                            else:
+                                obj = getattr(obj, bit)
+                        setting = bits[-1]
+                    else:
+                        obj = _settings
+                        setting = element.get_name()
+
+                    if element.has_apply_callback():
+                        LOG.debug("Using apply callback")
+                        element.run_apply_callback()
+                    elif element.value.get_mutable():
+                        LOG.debug("Setting %s = %s" % (setting, element.value))
+                        setattr(obj, setting, element.value)
+                except Exception, e:
+                    LOG.debug(element.get_name())
+                    raise
diff -r 786c6252a1ed -r cb68077dafff tools/cpep8.manifest
--- a/tools/cpep8.manifest	Sat Jan 09 10:36:59 2021 -0800
+++ b/tools/cpep8.manifest	Wed Feb 10 07:25:02 2021 -0800
@@ -46,6 +46,7 @@
 ./chirp/drivers/ic2720.py
 ./chirp/drivers/ic2730.py
 ./chirp/drivers/ic2820.py
+./chirp/drivers/ic7300.py
 ./chirp/drivers/ic9x.py
 ./chirp/drivers/ic9x_icf.py
 ./chirp/drivers/ic9x_icf_ll.py
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Icom_IC-7300.img
Type: application/octet-stream
Size: 3351 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20210210/985fa981/attachment-0001.img 


More information about the chirp_devel mailing list