[chirp_devel] Icom ic-7300 new driver

Rick (AA0RD) DeWitt
Fri Oct 16 07:20:23 PDT 2020


The CLONE MODE version of the ic7300 driver. The IC-7300 does not 
respond to the icf clone mode code.
I removed my debug test code and changed the BAUD parameter to a radio 
property.
This driver uses CAT commands to build an equivalent clone mode img 
file. The operators I know HATE using live mode when their radios have 
more than 10 memory channels.
The special encode and decode methods are required because this radio 
sends frequency data as strings of reverse HEX values, not lbcd, but 
lhcd, if such a thing exists.

-- 
Rick DeWitt
AA0RD
Sequim, Washington, USA 98382
(360) 681-3494

-------------- next part --------------
# HG changeset patch
# User Rick DeWitt <aa0rd at yahoo.com>
# Date 1602857091 25200
#      Fri Oct 16 07:04:51 2020 -0700
# Node ID 4d596e517a55f3e8df3508de6a145e348816f9a0
# Parent  98b8a850b0f136c77fe09a4922c32a2a28f708be
[ic7300] New driver for Icom IC-7300. Issue #4013

diff -r 98b8a850b0f1 -r 4d596e517a55 chirp/drivers/ic7300.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/ic7300.py	Fri Oct 16 07:04:51 2020 -0700
@@ -0,0 +1,734 @@
+# Copyright 2020 Rick DeWitt <aa0rd at yahoo.com>
+# Icom IC-7300 Vers 1.1: Channel memory and some important settings
+#       Using CI-V data requests to generate a clone-style memory block
+#       Vers 1.1 uses py3 bytes()
+#       Vers 1.2 Moved BAUD from global to radio property
+# 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 = ["", "-", "+"]
+MODES = ["LSB", "USB", "AM", "CW", "RTTY", "FM", "CWR", "RTTYR",
+         "Data+LSB", "Data+USB"]
+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 Transceiver 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 coomunicate 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
+
+    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))[4:]       # 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.cfmbeep))             # Confirmation beep
+    cmdx = "%s\x1A\x05\x00\x92%s\xFD" % (preamble, vstr)
+    _sendcmd(radio, status, cmdx)
+    vstr = _fhcd_encode(int(_sets.beepv))[4:]   # 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_settings = True
+        rf.has_dtcs = False
+        rf.has_dtcs_polarity = False
+        rf.has_bank = False
+        rf.memory_bounds = (1, 99)
+        rf.valid_modes = list(MODES)
+        rf.valid_tmodes = list(TMODES)
+        rf.valid_duplexes = list(set(DUPLEX))
+        rf.valid_tuning_steps = [2.5, 5.0, 10.0, 15.0, 20.0, 25.0,
+                                 30.0, 50.0, 100.0]
+        rf.valid_bands = [(5000, 74800000)]     # Requires validate_memory
+        rf.valid_skips = ["", "S", "P"]
+        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.
+            """))
+        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 validate_memory(self, mem):
+        """ Overide the run_tests.py Brute_Force memory validation test """
+        if mem.freq >= 3000 and mem.freq <= 74800000:
+            return True
+        else:
+            return False
+
+    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
+            # mem.name = ""
+            # mem.mode = MODES[0]
+            return mem
+        for char in _mem.name:
+            mnx += chr(char)
+        mem.name = mnx.strip()
+        mem.empty = False
+        mem.freq = int(_mem.rxfreq)
+        mem.duplex = DUPLEX[0]    # None by default
+        mem.offset = 0
+        if _mem.rxfreq < _mem.txfreq:   # + shift
+            mem.duplex = DUPLEX[2]
+            mem.offset = _mem.txfreq - _mem.rxfreq
+        if _mem.rxfreq > _mem.txfreq:   # - shift
+            mem.duplex = DUPLEX[1]
+            mem.offset = _mem.rxfreq - _mem.txfreq
+        if _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
+        if mem.duplex == "":
+            _mem.split = 0
+            _mem.txfreq = _mem.rxfreq
+        elif mem.duplex == "+":
+            _mem.txfreq = mem.freq + mem.offset
+            _mem.split = 1
+        else:
+            _mem.txfreq = mem.freq - mem.offset
+            _mem.split = 1
+        if mem.mode == "Data+LSB" or mem.mode == "Data+USB":
+            _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)
+            v1 = float(sv)
+            if opr == "M":
+                v2 = v1 * arg
+                # Need to round up or down for percent settings
+                if math.modf(v2)[0] < 0.5:
+                    v3 = math.floor(v2)
+                else:
+                    v3 = math.ceil(v2)
+            else:
+                v3 = v1 + arg
+            setattr(obj, atrb, int(v3))
+            return
+
+        # --- Basic
+        vlu = 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 = int(_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 98b8a850b0f1 -r 4d596e517a55 tools/cpep8.manifest
--- a/tools/cpep8.manifest	Sun Oct 11 15:39:26 2020 -0400
+++ b/tools/cpep8.manifest	Fri Oct 16 07:04:51 2020 -0700
@@ -43,6 +43,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 --------------
./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/ap510.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/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/puxing.py
./chirp/drivers/radioddity_r2.py
./chirp/drivers/rfinder.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_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/uvb5.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 --------------
A non-text attachment was scrubbed...
Name: Icom_IC-7300.img
Type: application/octet-stream
Size: 3343 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20201016/c2fdf3bb/attachment-0001.img 


More information about the chirp_devel mailing list