[chirp_devel] Kenwood TM-D710/G Clone Mode Driver
Rick (AA0RD) DeWitt
Tue May 26 15:23:04 PDT 2020
Re-submitted.
I may have missed a response from an earlier submission...
--
Rick DeWitt
AA0RD
360-681-3494
Sequim, WA USA
CN88jc
-------------- next part --------------
# HG changeset patch
# User Rick DeWitt <aa0rd at yahoo.com>
# Date 1588096680 25200
# Tue Apr 28 10:58:00 2020 -0700
# Node ID 7afcb68bd934012386ee7338a7c7d87c9e218610
# Parent e0dbf1a6f190747b5dcd8d4537daee86a1cb609c
[tmd710] New Clone-Mode driver for Kenwood TM-D710/GA/GE. Issue #7467
Implements true clone mode using PROG MCP binary image.
diff -r e0dbf1a6f190 -r 7afcb68bd934 chirp/drivers/tmd710.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/tmd710.py Tue Apr 28 10:58:00 2020 -0700
@@ -0,0 +1,2099 @@
+# Copyright 2011 Dan Smith <dsmith at danplanet.com>
+# -- 2019 Rick DeWitt <aa0rd at yahoo.com>
+# -- Implementing Kenwood TM-D710G as MCP Clone Mode for Python 2.7
+# -- Thanks to Herm Halbach, W7HRM, for the 710 model testing.
+# 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
+from chirp.drivers import kenwood_live
+
+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__)
+
+BAUD = 0
+STIMEOUT = 0.2
+TERM = chr(13) # Cmd write terminator (CR)
+ACK = chr(6) # Data write acknowledge char
+W8S = 0.001 # short wait, secs
+W8L = 0.1 # long wait
+TMD710_DUPLEX = ["", "+", "-", "n/a", "split"]
+TMD710_SKIP = ["", "S"]
+TMD710_MODES = ["FM", "NFM", "AM"]
+TMD710_BANDS = [(118000000, 135995000),
+ (136000000, 199995000),
+ (200000000, 299995000),
+ (300000000, 399995000),
+ (400000000, 523995000),
+ (800000000, 1299995000)]
+TMD710_STEPS = [5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0,
+ 30.0, 50.0, 100.0]
+# Need string list of those steps for mem.extra value list
+STEPS_STR = []
+for val in TMD710_STEPS:
+ STEPS_STR.append("%3.2f" % val)
+TMD710_TONE_MODES = ["", "Tone", "TSQL", "DTCS", "Cross"]
+TMD710_CROSS = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone"]
+TMD710_DTSC = list(chirp_common.DTCS_CODES)
+TMD710_TONES = list(chirp_common.TONES)
+TMD710_TONES.remove(159.8)
+TMD710_TONES.remove(165.5)
+TMD710_TONES.remove(171.3)
+TMD710_TONES.remove(177.3)
+TMD710_TONES.remove(183.5)
+TMD710_TONES.remove(189.9)
+TMD710_TONES.remove(196.6)
+TMD710_TONES.remove(199.5)
+TMD710_CHARS = chirp_common.CHARSET_ASCII
+TMD710_CHARS += chr(34) # "
+
+
+def _command(ser, cmd, rsplen, w8t=0.01):
+ """Send cmd to radio via ser port
+ cmd is output string with possible terminator
+ rsplen is expected response char count, NOT incl prefix and term
+ If rsplen = 0 then do not0 read after write """
+ ser.write(cmd)
+ LOG.debug(" Out ->: " + util.hexprint(cmd[0: 32].decode("latin-1")))
+ time.sleep(w8t)
+ result = ""
+ if rsplen > 0: # read response
+ result = ser.read(rsplen)
+ LOG.debug(" In <-: " + util.hexprint(result[0: 32]))
+ return result
+
+
+def _connect_radio(radio):
+ """Determine baud rate and verify radio on-line"""
+ global BAUD
+ xid = "D710" + radio.SHORT
+ resp = kenwood_live.get_id(radio.pipe)
+ resp = resp.decode("latin-1")
+ BAUD = radio.pipe.baudrate # As detected by kenwood_live
+ LOG.debug("Got [%s] at %i Baud." % (resp, BAUD))
+ resp = resp[3:] # Strip "ID " prefix
+ if len(resp) > 2: # Got something from "ID"
+ if resp == xid: # Good comms
+ return
+ else:
+ stx = "Radio responded as %s, not %s." % (resp, xid)
+ raise errors.RadioError(stx)
+ raise errors.RadioError("No response from radio")
+
+
+def _update_status(self, status, step=1):
+ """ Increment status bar """
+ status.cur += step
+ self.status_fn(status)
+ return
+
+
+def _make_address(upperbyte, lowerbyte, uppercount, lowercount):
+ """ Create 4-char TMD710G hex address + count string
+ upperbyte lowerbyte uppercount lowercount """
+ blockn = upperbyte + lowerbyte
+ addr = chr((blockn & 0xFF00) >> 8) + chr(blockn & 0xFF)
+ addr += chr((uppercount & 0xFF)) + chr(lowercount & 0x0ff)
+ return addr
+
+
+def _val_list(setting, opts, obj, atrb, fix=0, ndx=-1):
+ """Callback:from ValueList. Set the integer index.
+ This function is here to be available to get_mem and get_set
+ fix is optional additive offset to the list index
+ ndx is optional obj[ndx] array index """
+ value = opts.index(str(setting.value))
+ value += fix
+ if ndx >= 0: # indexed obj
+ setattr(obj[ndx], atrb, value)
+ else:
+ setattr(obj, atrb, value)
+ return
+
+
+class KenwoodTMx710Radio(chirp_common.CloneModeRadio):
+ """ Base class for TMD-710 """
+ VENDOR = "Kenwood"
+ MODEL = "TM-x710"
+ SHORT = "X" # Short model ID code
+
+ _upper = 999 # Number of normal chans
+
+ # Put Special memory channels after normal ones
+ SPECIAL_MEMORIES = {"Scan-0Lo": 1000, "Scan-0Hi": 1001,
+ "Scan-1Lo": 1002, "Scan-1Hi": 1003,
+ "Scan-2Lo": 1004, "Scan-2Hi": 1005,
+ "Scan-3Lo": 1006, "Scan-3Hi": 1007,
+ "Scan-4Lo": 1008, "Scan-4Hi": 1009,
+ "Scan-5Lo": 1010, "Scan-5Hi": 1011,
+ "Scan-6Lo": 1012, "Scan-6Hi": 1013,
+ "Scan-7Lo": 1014, "Scan-7Hi": 1015,
+ "Scan-8Lo": 1016, "Scan-8Hi": 1017,
+ "Scan-9Lo": 1018, "Scan-9Hi": 1019,
+ "WX-1": 1020, "WX-2": 1021,
+ "WX-3": 1022, "WX-4": 1023,
+ "WX-5": 1024, "WX-6": 1025,
+ "WX-7": 1026, "WX-8": 1027,
+ "WX-9": 1028, "WX-10": 1029,
+ "Call C0": 1030, "Call C1": 1031
+ }
+ # _REV dict is used to retrieve name given number
+ SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(),
+ SPECIAL_MEMORIES.keys()))
+
+ def get_features(self):
+ rf = chirp_common.RadioFeatures()
+ rf.can_odd_split = True
+ rf.has_dtcs = True
+ rf.has_dtcs_polarity = False
+ if self.SHORT == "G": # NOT for D710
+ rf.has_rx_dtcs = True # Enable DTCS Rx Code column
+ rf.has_cross = True
+ rf.valid_cross_modes = TMD710_CROSS
+ rf.has_bank = False
+ rf.has_settings = True
+ rf.has_ctone = True
+ rf.has_mode = True
+ rf.has_comment = False
+ rf.valid_tmodes = TMD710_TONE_MODES
+ rf.valid_modes = TMD710_MODES
+ rf.valid_duplexes = TMD710_DUPLEX
+ rf.valid_tuning_steps = TMD710_STEPS
+ rf.valid_dtcs_codes = TMD710_DTSC
+ # Supports upper and lower case text
+ rf.valid_characters = TMD710_CHARS
+ rf.valid_name_length = 8
+ rf.valid_skips = TMD710_SKIP
+ rf.valid_bands = TMD710_BANDS
+ rf.memory_bounds = (0, 1029) # including special chans 1000-1029
+ rf.valid_special_chans = sorted(self.SPECIAL_MEMORIES.keys())
+ return rf
+
+ @classmethod
+ def get_prompts(cls):
+ rp = chirp_common.RadioPrompts()
+ rp.pre_download = _(dedent("""\
+ Connect your interface cable to the PC Port on the
+ back of the 'TX/RX' unit. NOT the Com Port on the head.
+ """))
+ rp.pre_upload = _(dedent("""\
+ Connect your interface cable to the PC Port on the
+ back of the 'TX/RX' unit. NOT the Com Port on the head.
+ """))
+ return rp
+
+ def sync_in(self):
+ """Download from radio"""
+ try:
+ _connect_radio(self)
+ data = bytes(self._read_mem())
+ 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()
+
+ def sync_out(self):
+ """Upload to radio"""
+ try:
+ _connect_radio(self)
+ self._write_mem()
+ 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')
+
+ def process_mmap(self):
+ """Process the mem map into the mem object"""
+ self._memobj = bitwise.parse(self.MEM_FORMAT, self._mmap)
+
+ def get_memory(self, number):
+ """Convert raw channel data (_mem) into UI columns (mem)"""
+ mem = chirp_common.Memory()
+ if self.SHORT == "G":
+ mem.extra = RadioSettingGroup("extra", "Extra")
+ # If called from 'Properties', spcl chans number is integer
+ propflg = False
+ if isinstance(number, int):
+ if number > 999:
+ propflg = True
+ if isinstance(number, str) or propflg:
+ if propflg:
+ mem.number = number
+ mem.name = self.SPECIAL_MEMORIES_REV[number]
+ mem.extd_number = mem.name
+ else:
+ mem.name = number # Spcl chns 1st var
+ mem.number = self.SPECIAL_MEMORIES[number]
+ mem.extd_number = number # Uses name as LOC
+ mem.immutable = ["name"]
+ if mem.number < 1030: # Scan edges, WX
+ _mem = self._memobj.ch_mem[mem.number]
+ _map = self._memobj.chmap[mem.number]
+ else: # Call chans
+ _mem = self._memobj.call[mem.number - 1030]
+ else: # Normal mem chans
+ _mem = self._memobj.ch_mem[number]
+ _nam = self._memobj.ch_nam[number]
+ _map = self._memobj.chmap[number]
+ mem.number = number
+ mnx = ""
+ for char in _nam.name:
+ if int(char) < 127:
+ mnx += chr(char)
+ mem.name = mnx
+ if _mem.rxfreq == 0x0ffffffff or _mem.rxfreq == 0:
+ mem.empty = True
+ return mem
+ mem.empty = False
+ if mem.number < 1030 and _map.skip != 0x0ff: # empty
+ mem.skip = TMD710_SKIP[_map.skip]
+ mem.freq = int(_mem.rxfreq)
+ mem.duplex = TMD710_DUPLEX[_mem.duplex]
+ mem.offset = int(_mem.offset)
+ # Duplex = 4 (split); offset contains the TX freq
+ mem.mode = TMD710_MODES[_mem.mode]
+ # _mem.tmode is 4-bit pattern, not number
+ mx = 0 # No tone
+ mem.cross_mode = TMD710_CROSS[0]
+ mem.rx_dtcs = TMD710_DTSC[_mem.dtcs]
+ mem.dtcs = TMD710_DTSC[_mem.dtcs]
+ if self.SHORT == "G":
+ if _mem.tmode & 8: # Tone
+ mx = 1
+ if _mem.tmode & 4: # Tsql
+ mx = 2
+ if _mem.tmode & 2: # Dtcs
+ mx = 3
+ if _mem.tmode & 1: # Cross
+ mx = 4
+ if _mem.cross == 1: # Tone->DTCS
+ mem.cross_mode = TMD710_CROSS[1]
+ if _mem.cross == 2: # DTCS->Tone
+ mem.cross_mode = TMD710_CROSS[2]
+ else: # D710; may have bit 8 set
+ if _mem.tmode & 4: # Tone
+ mx = 1
+ if _mem.tmode & 2: # Tsql
+ mx = 2
+ if _mem.tmode & 1: # Dtcs
+ mx = 3
+ mem.dtcs = TMD710_DTSC[_mem.dtcs]
+ mem.tmode = TMD710_TONE_MODES[mx]
+ mem.ctone = TMD710_TONES[_mem.ctone]
+ mem.rtone = TMD710_TONES[_mem.rtone]
+ mem.tuning_step = TMD710_STEPS[_mem.tstep]
+
+ if self.SHORT == "G": # Only the 710G
+ rx = RadioSettingValueList(STEPS_STR, STEPS_STR[_mem.splitstep])
+ sx = "Split TX step (KHz)"
+ rset = RadioSetting("splitstep", sx, rx)
+ mem.extra.append(rset)
+
+ return mem
+
+ def set_memory(self, mem):
+ """Convert UI column data (mem) into MEM_FORMAT memory (_mem)"""
+ if mem.number > 999: # Special chans
+ if mem.number < 1030: # Scan, Wx
+ _mem = self._memobj.ch_mem[mem.number]
+ _map = self._memobj.chmap[mem.number]
+ else: # Call chans
+ _mem = self._memobj.call[mem.number - 1030]
+ else:
+ _mem = self._memobj.ch_mem[mem.number]
+ _nam = self._memobj.ch_nam[mem.number]
+ _map = self._memobj.chmap[mem.number]
+ nx = len(mem.name)
+ for ix in range(8):
+ if ix < nx:
+ _nam.name[ix] = mem.name[ix]
+ else:
+ _nam.name[ix] = chr(0x0ff) # needs 8 chrs
+ if mem.empty:
+ _mem.rxfreq = 0x0ffffffff
+ _mem.offset = 0x0ffffff
+ _mem.duplex = 0x0f
+ _mem.tstep = 0x0ff
+ _mem.tmode = 0x0f
+ _mem.mode = 0x0ff
+ _mem.rtone = 0x0ff
+ _mem.ctone = 0x0ff
+ _mem.dtcs = 0x0ff
+ _map.skip = 0x0ff
+ _map.band = 0x0ff
+ for ix in range(8):
+ _nam.name[ix] = chr(0x0ff)
+ return
+ if _mem.rxfreq == 0x0ffffffff: # New Channel needs defaults
+ _mem.rxfreq = 144000000
+ _map.band = 5
+ _map.skip = 0
+ _mem.mode = 0
+ _mem.duplex = 0
+ _mem.offset = 0
+ _mem.rtone = 8
+ _mem.ctone = 8
+ _mem.dtcs = 0
+ _mem.tstep = 0
+ _mem.splitstep = 0
+ # Now use the UI values entered so far
+ _mem.rxfreq = mem.freq
+ _mem.mode = TMD710_MODES.index(mem.mode)
+ try:
+ _tone = mem.rtone
+ _mem.rtone = TMD710_TONES.index(mem.rtone)
+ _tone = mem.ctone
+ _mem.ctone = TMD710_TONES.index(mem.ctone)
+ except ValueError:
+ raise errors.UnsupportedToneError("This radio does not support " +
+ "tone %.1fHz" % _tone)
+ _mem.dtcs = TMD710_DTSC.index(mem.dtcs)
+ _mem.tmode = 0 # None
+ _mem.cross = 0
+ if self.SHORT == "G":
+ if mem.tmode == "Tone":
+ _mem.tmode = 8
+ if mem.tmode == "TSQL":
+ _mem.tmode = 4
+ if mem.tmode == "DTCS":
+ _mem.tmode = 2
+ if mem.tmode == "Cross":
+ _mem.tmode = 1
+ mx = TMD710_CROSS.index(mem.cross_mode)
+ _mem.cross = 3 # t -t
+ if mx == 1:
+ _mem.cross = 1 # t-d
+ _mem.dtcs = TMD710_DTSC.index(mem.rx_dtcs)
+ if mx == 2:
+ _mem.cross = 2 # d-t
+ _mem.dtcs = TMD710_DTSC.index(mem.dtcs)
+ else:
+ _mem.tmode = 0x80 # None
+ if mem.tmode == "Tone":
+ _mem.tmode = 0x0c
+ if mem.tmode == "TSQL":
+ _mem.tmode = 0x0a
+ if mem.tmode == "DTCS":
+ _mem.tmode = 0x09
+ if mem.duplex == "n/a": # Not valid
+ mem.duplex = ""
+ _mem.duplex = TMD710_DUPLEX.index(mem.duplex)
+ _mem.offset = mem.offset
+ _mem.tstep = TMD710_STEPS.index(mem.tuning_step)
+ # Set _map.band for this bank. Not Calls!
+ if mem.number < 1030:
+ _map.band = 5
+ val = mem.freq
+ for mx in range(6): # Band codes are 0, 5, 6, 7, 8, 9
+ if val >= TMD710_BANDS[mx][0] and \
+ val <= TMD710_BANDS[mx][1]:
+ _map.band = mx
+ if mx > 0:
+ _map.band = mx + 4
+ _map.skip = TMD710_SKIP.index(mem.skip)
+ # Only 1 mem.extra entry now
+ for ext in mem.extra:
+ if ext.get_name() == "splitstep":
+ val = STEPS_STR.index(str(ext.value))
+ setattr(_mem, "splitstep", val)
+ else:
+ setattr(_mem, ext.get_name(), ext.value)
+ return
+
+ def get_settings(self):
+ """Translate the MEM_FORMAT structs into settings in the UI"""
+ # Define mem struct write-back shortcuts
+ if self.SHORT == "G":
+ _bmp = self._memobj.bitmap
+ _blk1 = self._memobj.block1
+ _blk1a = self._memobj.block1a
+ _pmg = self._memobj.pmg # array[6] of settings
+ _dtmc = self._memobj.dtmc
+ _dtmn = self._memobj.dtmn
+ _com = self._memobj.mcpcom
+ _skyc = self._memobj.skycmd
+ basic = RadioSettingGroup("basic", "Basic")
+ disp = RadioSettingGroup("disp", "PM0: Display") # PM[0] settings
+ aud = RadioSettingGroup("aud", "PM0: Audio")
+ aux = RadioSettingGroup("aux", "PM0: Aux")
+ txrx = RadioSettingGroup("txrc", "PM0: Transmit/Receive")
+ memz = RadioSettingGroup("memz", "PM0: Memory")
+ pfk = RadioSettingGroup("pfk", "PM0: PF Keys")
+ pvfo = RadioSettingGroup("pvfo", "PM0: Programmable VFO")
+ bmsk = RadioSettingGroup("bmsk", "PM0: Band Masks") # end PM[0]
+ rptr = RadioSettingGroup("rptr", "Repeater")
+ dtmf = RadioSettingGroup("dtmf", "DTMF")
+ skyk = RadioSettingGroup("skyk", "Sky Command")
+ pmm = RadioSettingGroup("pmm", "PM Groups 1-5(Partial)")
+ group = RadioSettings(basic, disp, aud, aux, txrx, memz, pvfo, pfk,
+ bmsk, rptr, dtmf, skyk, pmm)
+
+ mhz1 = 1000000. # Raw freq is stored with 0.1 Htz resolution
+
+ def _adjraw(setting, obj, atrb, fix=0, ndx=-1):
+ """Callback for Integer add or subtract fix from value."""
+ vx = int(str(setting.value))
+ value = vx + int(fix)
+ if value < 0:
+ value = 0
+ if ndx < 0:
+ setattr(obj, atrb, value)
+ else:
+ setattr(obj[ndx], atrb, value)
+ return
+
+ def _mhz_val(setting, obj, atrb, ndx=-1, ndy=-1):
+ """ Callback to set freq back to Htz """
+ vx = float(str(setting.value))
+ vx = int(vx * mhz1)
+ if ndx < 0:
+ setattr(obj, atrb, vx)
+ else:
+ if atrb[0:7] == "progvfo": # 2-deep
+ stx = atrb.split(".")
+ setattr(obj[ndx].progvfo[ndy], stx[1], vx)
+ else:
+ setattr(obj[ndx], atrb, vx)
+ return
+
+ def _char_to_str(chrx):
+ """ Remove ff pads from char array """
+ # chrx is char array
+ str1 = ""
+ for sx in chrx:
+ if int(sx) > 31 and int(sx) < 127:
+ str1 += chr(sx)
+ return str1
+
+ def _pswd_vfy(setting, obj, atrb):
+ """ Verify password is 1-6 chars, numbers 1-5 """
+ str1 = str(setting.value).strip() # initial
+ str2 = filter(lambda c: c in '12345', str1) # valid chars
+ if str1 != str2:
+ # Two lines due to python 73 char limit
+ sx = "Bad characters in Password"
+ raise errors.RadioError(sx)
+ str2 = str1.ljust(6, chr(255)) # pad to 6 with ff's
+ setattr(obj, atrb, str2)
+ return
+
+ def _pad_str(setting, lenstr, padchr, obj, atrb, ndx=-1):
+ """ pad string to lenstr with padchr """
+ str1 = str(setting.value).strip() # initial string
+ str2 = str1.ljust(lenstr, padchr)
+ if ndx < 0:
+ setattr(obj, atrb, str2)
+ else:
+ setattr(obj[ndx], atrb, str2)
+ return
+
+ # ===== BASIC GROUP =====
+ sx = _char_to_str(_com.comnt)
+ rx = RadioSettingValueString(0, 32, sx)
+ sx = "Comment"
+ rset = RadioSetting("mcpcom.comnt", sx, rx)
+ basic.append(rset)
+
+ rx = RadioSettingValueInteger(0, 5, _blk1.pmrecall)
+ sx = "Current PM Select"
+ rset = RadioSetting("block1.pmrecall", sx, rx)
+ basic.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_blk1.pwdon))
+ sx = "Password"
+ rset = RadioSetting("block1.pwdon", sx, rx)
+ basic.append(rset)
+
+ sx = _char_to_str(_blk1.pswd).strip()
+ rx = RadioSettingValueString(0, 6, sx)
+ # rx.set_charset("12345") # Keeps finding `'
+ sx = "- Password (numerals 1-5)"
+ rset = RadioSetting("block1.pswd", sx, rx)
+ rset.set_apply_callback(_pswd_vfy, _blk1, "pswd")
+ basic.append(rset)
+
+ # ===== PM0 (off) DISPLAY GROUP =====
+ rx = RadioSettingValueString(0, 8, _char_to_str(_pmg[0].pwron))
+ sx = "Power-On message"
+ rset = RadioSetting("pmg/0.pwron", sx, rx)
+ disp.append(rset)
+
+ if self.SHORT == "G": # TMD-710G
+ rx = RadioSettingValueBoolean(bool(_bmp.bmpon))
+ sx = "PM0: Custom display bitmap"
+ rset = RadioSetting("bitmap.bmpon", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueString(0, 64, _char_to_str(_bmp.bmpfyl))
+ rx.set_mutable(False)
+ sx = "- Custom bitmap filename"
+ rset = RadioSetting("bitmap.bmpfyl", sx, rx)
+ rset.set_doc("Read-only: To modify, use MCP-6 s/w")
+ disp.append(rset)
+
+ opts = ["VFO", "Mem Recall"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].a_mr])
+ sx = "A: Left Side VFO/MR"
+ rset = RadioSetting("pmg/0.a_mr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "a_mr")
+ disp.append(rset)
+
+ rx = RadioSettingValueInteger(0, 999, _pmg[0].a_chn)
+ sx = "A: Left Side MR Channel"
+ rset = RadioSetting("pmg/0.a_chn", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].b_mr])
+ sx = "B: Right Side VFO/MR"
+ rset = RadioSetting("pmg/0.b_mr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "b_mr")
+ disp.append(rset)
+
+ rx = RadioSettingValueInteger(0, 999, _pmg[0].b_chn)
+ sx = "B: Right Side MR Channel"
+ rset = RadioSetting("pmg/0.b_chn", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueInteger(0, 8, _pmg[0].bright)
+ sx = "Brightness level"
+ rset = RadioSetting("pmg/0.bright", sx, rx)
+ disp.append(rset)
+
+ opts = ["Amber", "Green"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].bkltclr])
+ sx = "Backlight color"
+ rset = RadioSetting("pmg/0.bkltclr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "bkltclr")
+ disp.append(rset)
+
+ val = _pmg[0].bkltcont + 1
+ rx = RadioSettingValueInteger(1, 16, val)
+ sx = "Contrast level"
+ rset = RadioSetting("pmg/0.bkltcont", sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[0], "bkltcont", -1)
+ disp.append(rset)
+
+ opts = ["Positive", "Negative"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].dsprev])
+ sx = "Color mode"
+ rset = RadioSetting("pmg/0.dsprev", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "dsprev")
+ disp.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].autobri))
+ sx = "Auto brightness"
+ rset = RadioSetting("pmg/0.autobri", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].dispbar))
+ sx = "Display partition bar"
+ rset = RadioSetting("pmg/0.dispbar", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].single))
+ sx = "Single band display"
+ rset = RadioSetting("pmg/0.single", sx, rx)
+ disp.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].autopm))
+ sx = "Auto PM Store"
+ rset = RadioSetting("pmg/0.autopm", sx, rx)
+ disp.append(rset)
+
+ # ===== AUDIO GROUP =====
+ rx = RadioSettingValueBoolean(bool(_pmg[0].beepon))
+ sx = "Beep On"
+ rset = RadioSetting("pmg/0.beepon", sx, rx)
+ aud.append(rset)
+
+ val = _pmg[0].beepvol + 1 # 1-7 downloads as 0-6
+ rx = RadioSettingValueInteger(1, 7, val)
+ sx = "Beep volume (1 - 7)"
+ rset = RadioSetting("pmg/0.beepvol", sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[0], "beepvol", -1)
+ aud.append(rset)
+
+ opts = ["Mode1", "Mode2"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].extspkr])
+ sx = "External Speaker"
+ rset = RadioSetting("pmg/0.extspkr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "extspkr")
+ aud.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].pbkrpt))
+ sx = "VGS Plugin: Playback repeat"
+ rset = RadioSetting("pmg/0.pbkrpt", sx, rx)
+ aud.append(rset)
+
+ rx = RadioSettingValueInteger(0, 60, _pmg[0].pbkint)
+ sx = " Playback repeat interval (0 - 60 secs)"
+ rset = RadioSetting("pmg/0.pbkint", sx, rx)
+ aud.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].cntrec))
+ sx = " Continuous recording"
+ rset = RadioSetting("pmg/0.cntrec", sx, rx)
+ aud.append(rset)
+
+ opts = ["Off", "Auto", "Manual"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].ance])
+ sx = " Announce mode"
+ rset = RadioSetting("pmg/0.ance", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "ance")
+ aud.append(rset)
+
+ opts = ["English", "Japanese"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].lang])
+ sx = " Announce language"
+ rset = RadioSetting("pmg/0.lang", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "lang")
+ aud.append(rset)
+
+ rx = RadioSettingValueInteger(1, 7, _pmg[0].vcvol + 1)
+ sx = " Voice volume (1 - 7)"
+ rset = RadioSetting("pmg/0.vcvol", sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[0], "vcvol", -1)
+ aud.append(rset)
+
+ rx = RadioSettingValueInteger(0, 4, _pmg[0].vcspd)
+ sx = " Voice speed (0 - 4)"
+ rset = RadioSetting("pmg/0.vcspd", sx, rx)
+ aud.append(rset)
+
+ # ===== AUX GROUP =====
+ opts = ["9600", "19200", "38400", "57600"]
+ rx = RadioSettingValueList(opts, opts[_blk1.pcbaud])
+ sx = "PC port baud rate"
+ rset = RadioSetting("block1.pcbaud", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _blk1, "pcbaud")
+ aux.append(rset)
+
+ opts = ["A-Band", "B-Band", "TX-A / RX-B", "RX-A / TX-B"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].intband])
+ sx = "Internal TNC band"
+ rset = RadioSetting("pmg/0.intband", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "intband")
+ aux.append(rset)
+
+ opts = ["A-Band", "B-Band", "TX-A / RX-B", "RX-A / TX-B"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].extband])
+ sx = "External TNC band"
+ rset = RadioSetting("pmg/0.extband", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "extband")
+ aux.append(rset)
+
+ opts = ["1200", "9600"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].extbaud])
+ sx = "External TNC baud"
+ rset = RadioSetting("pmg/0.extbaud", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "extbaud")
+ aux.append(rset)
+
+ opts = ["Off", "BUSY", "SQL", "TX", "BUSY/TX", "SQL/TX"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].sqcsrc])
+ sx = "SQC output source"
+ rset = RadioSetting("pmg/0.sqcsrc", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "sqcsrc")
+ aux.append(rset)
+
+ opts = ["Low", "High"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].sqclogic])
+ sx = "SQC logic"
+ rset = RadioSetting("pmg/0.sqclogic", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "sqclogic")
+ aux.append(rset)
+
+ opts = ["Off", "30", "60", "90", "120", "180"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].apo])
+ sx = "APO: Auto Power Off (Mins)"
+ rset = RadioSetting("pmg/0.apo", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "apo")
+ aux.append(rset)
+
+ opts = ["Time Operate (TO)", "Carrier Operate (CO)", "Seek"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].scnrsm])
+ sx = "Scan resume mode"
+ rset = RadioSetting("pmg/0.scnrsm", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "scnrsm")
+ aux.append(rset)
+
+ rx = RadioSettingValueInteger(1, 10, _pmg[0].scntot + 1)
+ sx = " Scan TO delay (Secs)"
+ rset = RadioSetting("pmg/0.scntot", sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[0], "scntot", -1)
+ aux.append(rset)
+
+ rx = RadioSettingValueInteger(1, 10, _pmg[0].scncot + 1)
+ sx = " Scan CO delay (Secs)"
+ rset = RadioSetting("pmg/0.scncot", sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[0], "scncot", -1)
+ aux.append(rset)
+
+ opts = ["Mode 1: 1ch", "Mode 2: 61ch", "Mode 3: 91ch",
+ "Mode 4: 181ch"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].vsmode])
+ sx = "Visual scan"
+ rset = RadioSetting("pmg/0.vsmode", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "vsmode")
+ aux.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_blk1.m10mz))
+ sx = "10 Mhz mode"
+ rset = RadioSetting("block1.m10mz", sx, rx)
+ aux.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_blk1.ansbck))
+ sx = "Remote control answerback"
+ rset = RadioSetting("block1.ansbck", sx, rx)
+ aux.append(rset)
+
+ # ===== TX / RX Group =========
+ opts = ["A: Left", "B: Right"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].txband])
+ sx = "TX Side (PTT)"
+ rset = RadioSetting("pmg/0.txband", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "txband")
+ txrx.append(rset)
+
+ opts = ["High (50W)", "Medium (10W)", "Low (5W)"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].a_pwr])
+ sx = "A-Band transmit power"
+ rset = RadioSetting("pmg/0.a_pwr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "a_pwr")
+ txrx.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].b_pwr])
+ sx = "B-Band transmit power"
+ rset = RadioSetting("pmg/0.b_pwr", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "b_pwr")
+ txrx.append(rset)
+
+ opts = ["Off", "125", "250", "500", "750", "1000"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].mutehu])
+ sx = "Rx Mute hangup time (ms)"
+ rset = RadioSetting("pmg/0.mutehu", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "mutehu")
+ txrx.append(rset)
+
+ opts = ["Off", "125", "250", "500"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].ssqlhu])
+ sx = "S-meter SQL hangup time (ms)"
+ rset = RadioSetting("pmg/0.ssqlhu", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "ssqlhu")
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].beatshft))
+ sx = "Beat shift"
+ rset = RadioSetting("pmg/0.beatshft", sx, rx)
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].asmsql))
+ sx = "A-Band S-meter SQL"
+ rset = RadioSetting("pmg/0.asmsql", sx, rx)
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bsmsql))
+ sx = "B-Band S-meter SQL"
+ rset = RadioSetting("pmg/0.bsmsql", sx, rx)
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].vhfaip))
+ sx = "VHF band AIP"
+ rset = RadioSetting("pmg/0.vhfaip", sx, rx)
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].uhfaip))
+ sx = "UHF band AIP"
+ rset = RadioSetting("pmg/0.uhfaip", sx, rx)
+ txrx.append(rset)
+
+ opts = ["High", "Medium", "Low"]
+ rx = RadioSettingValueList(opts, opts[_blk1.micsens])
+ sx = "Microphone sensitivity (gain)"
+ rset = RadioSetting("block1.micsens", sx, rx)
+ txrx.append(rset)
+
+ opts = ["3", "5", "10"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].tot])
+ sx = "Time-Out timer (Mins)"
+ rset = RadioSetting("pmg/0.tot", sx, rx)
+ # rset.set_apply_callback(_val_list, opts, _pmg[0], "tot")
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].wxalerta))
+ sx = "WX Alert A-band"
+ rset = RadioSetting("pmg/0.wxalerta", sx, rx)
+ txrx.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].wxalertb))
+ sx = "WX Alert B-band"
+ rset = RadioSetting("pmg/0.wxalertb", sx, rx)
+ txrx.append(rset)
+
+ opts = ["Off", "15", "30", "60"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].wxscntm])
+ sx = "WX alert scan memory time (Mins)"
+ rset = RadioSetting("pmg/0.wxscntm", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "wxscntm")
+ txrx.append(rset)
+
+ # ===== DTMF GROUP =====
+ rx = RadioSettingValueBoolean(bool(_pmg[0].dtmfhld))
+ sx = "DTMF hold"
+ rset = RadioSetting("pmg/0.dtmfhld", sx, rx)
+ dtmf.append(rset)
+
+ opts = ["100", "250", "500", "750", "1000", "1500", "2000"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].dtmfpau])
+ sx = "DTMF pause duration (mS)"
+ rset = RadioSetting("pmg/0.dtmfpau", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "dtmfpau")
+ dtmf.append(rset)
+
+ opts = ["Fast", "Slow"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].dtmfspd])
+ sx = "DTMF speed"
+ rset = RadioSetting("pmg/0.dtmfspd", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "dtmfspd")
+ dtmf.append(rset)
+
+ for mx in range(0, 10):
+ csx = _char_to_str(_dtmn[mx].id).strip()
+ rx = RadioSettingValueString(0, 8, csx)
+ sx = "DTMF %i Name (8 chars)" % mx
+ rset = RadioSetting("dtmn.id/%d" % mx, sx, rx)
+ rset.set_apply_callback(_pad_str, 8, chr(255), _dtmn, "id", mx)
+ dtmf.append(rset)
+
+ csx = _char_to_str(_dtmc[mx].code).strip()
+ rx = RadioSettingValueString(0, 16, csx)
+ sx = " Code %i (16 chars)" % mx
+ rset = RadioSetting("dtmc.code/%d" % mx, sx, rx)
+ rset.set_apply_callback(_pad_str, 16, chr(255), _dtmc, "code", mx)
+ dtmf.append(rset)
+
+ # ===== MEMORY GROUP =====
+ opts = ["All Bands", "Current Band"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].recall])
+ sx = "Memory recall method"
+ rset = RadioSetting("pmg/0.recall", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "recall")
+ memz.append(rset)
+
+ rx = RadioSettingValueString(0, 10, _char_to_str(_pmg[0].memgrplk))
+ sx = "Group link"
+ rset = RadioSetting("pmg/0.memgrplk", sx, rx)
+ memz.append(rset)
+
+ opts = ["Fast", "Slow"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].eclnkspd])
+ sx = "Echolink speed"
+ rset = RadioSetting("pmg/0.eclnkspd", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "eclnkspd")
+ memz.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_blk1.dspmemch))
+ sx = "Display memory channel number"
+ rset = RadioSetting("block1.dspmemch", sx, rx)
+ memz.append(rset)
+
+ # ===== REPEATER GROUP =====
+ rx = RadioSettingValueBoolean(bool(_pmg[0].rptr1750))
+ sx = "1750 Hz transmit hold"
+ rset = RadioSetting("pmg/0.rptr1750", sx, rx)
+ rptr.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].rptrofst))
+ sx = "Auto repeater offset"
+ rset = RadioSetting("pmg/0.rptrofst", sx, rx)
+ rptr.append(rset)
+
+ opts = ["Cross Band", "TX:A-Band / RX:B-Band", "RX:A-Band / TX:B-Band"]
+ rx = RadioSettingValueList(opts, opts[_blk1.rptrmode])
+ sx = "Repeater Mode"
+ rset = RadioSetting("block1.rptrmode", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _blk1, "rptrmode")
+ rptr.append(rset)
+
+ opts = ["Off", "Morse", "Voice"]
+ rx = RadioSettingValueList(opts, opts[_blk1.rptridx])
+ sx = "Repeater ID transmit"
+ rset = RadioSetting("block1.rptridx", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _blk1, "rptridx")
+ rptr.append(rset)
+
+ rx = RadioSettingValueString(0, 12, _char_to_str(_blk1a.rptrid))
+ sx = "Repeater ID"
+ rset = RadioSetting("block1a.rptrid", sx, rx)
+ rptr.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_blk1.rptrhold))
+ sx = "Repeater transmit hold"
+ rset = RadioSetting("block1.rptrhold", sx, rx)
+ rptr.append(rset)
+
+ # ===== Prog VFO Group =============
+ for mx in range(0, 10):
+ # Raw freq is 0.1 Mhz resolution
+ vfx = int(_pmg[0].progvfo[mx].blow) / mhz1
+ if vfx == 0:
+ vfx = 118
+ rx = RadioSettingValueFloat(118.0, 1299.9, vfx, 0.005, 3)
+ sx = "VFO-%i Low Limit (MHz)" % mx
+ rset = RadioSetting("pmg/0.progvfo/%d.blow" % mx, sx, rx)
+ rset.set_apply_callback(_mhz_val, _pmg, "progvfo.blow", 0, mx)
+ pvfo.append(rset)
+
+ vfx = int(_pmg[0].progvfo[mx].bhigh) / mhz1
+ if vfx == 0:
+ vfx = 118
+ rx = RadioSettingValueFloat(118.0, 1300.0, vfx, 0.005, 3)
+ sx = " VFO-%i High Limit (MHz)" % mx
+ rset = RadioSetting("pmg/0.progvfo/%d.bhigh" % mx, sx, rx)
+ rset.set_apply_callback(_mhz_val, _pmg, "progvfo.bhigh", 0, mx)
+ pvfo.append(rset)
+
+ # ===== PFK GROUP =====
+ opts = ["WX CH", "FRQ.BAND", "CTRL", "MONITOR", "VGS", "VOICE",
+ "GROUP UP", "MENU", "MUTE", "SHIFT", "DUAL", "M>V",
+ "1750 Tone"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].pf1key])
+ sx = "Front panel PF1 key"
+ rset = RadioSetting("pmg/0.pf1key", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "pf1key")
+ pfk.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].pf2key])
+ sx = "Front panel PF2 key"
+ rset = RadioSetting("pmg/0.pf2key", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "pf2key")
+ pfk.append(rset)
+
+ opts = ["WX CH", "FRQ.BAND", "CTRL", "MONITOR", "VGS", "VOICE",
+ "GROUP UP", "MENU", "MUTE", "SHIFT", "DUAL", "M>V",
+ "VFO", "MR", "CALL", "MHz", "TONE", "REV", "LOW",
+ "LOCK", "A/B", "ENTER", "1750 Tone", "M.LIST",
+ "S.LIST", "MSG.NEW", "REPLY", "POS", "P.MONI",
+ "BEACON", "DX", "WX"]
+ rx = RadioSettingValueList(opts, opts[_pmg[0].micpf1])
+ sx = "Microphone PF1 key"
+ rset = RadioSetting("pmg/0.micpf1", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf1")
+ pfk.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].micpf2])
+ sx = "Microphone PF2 key"
+ rset = RadioSetting("pmg/0.micpf2", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf2")
+ pfk.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].micpf3])
+ sx = "Microphone PF3 key"
+ rset = RadioSetting("pmg/0.micpf3", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf3")
+ pfk.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[0].micpf4])
+ sx = "Microphone PF4 key"
+ rset = RadioSetting("pmg/0.micpf4", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf4")
+ pfk.append(rset)
+
+ # ===== BMSK GROUP =====
+ rx = RadioSettingValueBoolean(bool(_pmg[0].abnd118))
+ sx = "A/Left: 118Mhz Band"
+ rset = RadioSetting("pmg/0.abnd118", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].abnd144))
+ sx = "A/Left: 144Mhz Band"
+ rset = RadioSetting("pmg/0.abnd144", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].abnd220))
+ sx = "A/Left: 220Mhz Band"
+ rset = RadioSetting("pmg/0.abnd220", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].abnd300))
+ sx = "A/Left: 300Mhz Band"
+ rset = RadioSetting("pmg/0.abnd300", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].abnd430))
+ sx = "A/Left: 430Mhz Band"
+ rset = RadioSetting("pmg/0.abnd430", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd144))
+ sx = "B/Right: 144Mhz Band"
+ rset = RadioSetting("pmg/0.bbnd144", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd220))
+ sx = "B/Right: 220Mhz Band"
+ rset = RadioSetting("pmg/0.bbnd220", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd300))
+ sx = "B/Right: 300Mhz Band"
+ rset = RadioSetting("pmg/0.bbnd300", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd430))
+ sx = "B/Right: 430Mhz Band"
+ rset = RadioSetting("pmg/0.bbnd430", sx, rx)
+ bmsk.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd800))
+ sx = "B/Right: 800Mhz Band"
+ rset = RadioSetting("pmg/0.bbnd800", sx, rx)
+ bmsk.append(rset)
+
+ # ===== Sky command Group =============
+ rx = RadioSettingValueString(0, 10, _char_to_str(_skyc.cmdr))
+ sx = "Commandr call sign"
+ rset = RadioSetting("skycmd.cmdr", sx, rx)
+ rset.set_apply_callback(_pad_str, 10, chr(0), _skyc, "cmdr")
+ skyk.append(rset)
+
+ rx = RadioSettingValueString(0, 10, _char_to_str(_skyc.tptr))
+ sx = "Transporter call sign"
+ rset = RadioSetting("skycmd.tptr", sx, rx)
+ rset.set_apply_callback(_pad_str, 10, chr(0), _skyc, "tptr")
+ skyk.append(rset)
+
+ opts = []
+ for val in TMD710_TONES:
+ opts.append(str(val))
+ rx = RadioSettingValueList(opts, opts[_skyc.skytone])
+ sx = "Tone frequency"
+ rset = RadioSetting("skycmd.skytone", sx, rx)
+ rset.set_apply_callback(_val_list, opts, _skyc, "skytone")
+ skyk.append(rset)
+
+ # ===== PM MEMORY GROUP =====
+ """ These 5 blocks of 512 bytes are repeats of the major settings """
+ # Only showing limited settings for now...
+ _pmn = self._memobj.pm_name
+ for ix in range(1, 6):
+ nx = ix - 1 # Names are [0-4]
+ rx = RadioSettingValueString(0, 16, _char_to_str(_pmn[nx].pmname))
+ sx = "PM Group %i Name" % ix
+ rset = RadioSetting("pm_name/%i.pmname" % nx, sx, rx)
+ rset.set_apply_callback(_pad_str, 16, chr(0xff), _pmn,
+ "pmname", nx)
+ pmm.append(rset)
+
+ rx = RadioSettingValueString(0, 8, _char_to_str(_pmg[ix].pwron))
+ sx = "- Power-On Message"
+ rset = RadioSetting("pmg/%i.pwron" % ix, sx, rx)
+ rset.set_apply_callback(_pad_str, 8, chr(0xff), _pmg, "pwron", ix)
+ pmm.append(rset)
+
+ opts = ["VFO", "Mem Recall"]
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].a_mr])
+ sx = "- A: Left Side VFO/MR"
+ rset = RadioSetting("pmg/%i.a_mr" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "a_mr")
+ pmm.append(rset)
+
+ rx = RadioSettingValueInteger(0, 999, _pmg[ix].a_chn)
+ sx = "- A: Left Side MR Channel"
+ rset = RadioSetting("pmg/%i.a_chn" % ix, sx, rx)
+ pmm.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].b_mr])
+ sx = "- B: Right Side VFO/MR"
+ rset = RadioSetting("pmg/%i.b_mr" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "b_mr")
+ pmm.append(rset)
+
+ rx = RadioSettingValueInteger(0, 999, _pmg[ix].b_chn)
+ sx = "- B: Right Side MR Channel"
+ rset = RadioSetting("pmg/%i.b_chn" % ix, sx, rx)
+ pmm.append(rset)
+
+ rx = RadioSettingValueInteger(0, 8, _pmg[ix].bright)
+ sx = "- Brightness level"
+ rset = RadioSetting("pmg/%i.bright" % ix, sx, rx)
+ pmm.append(rset)
+
+ opts = ["Amber", "Green"]
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].bkltclr])
+ sx = "- Backlight color"
+ rset = RadioSetting("pmg/%i.bkltclr" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "bkltclr")
+ pmm.append(rset)
+
+ val = _pmg[ix].bkltcont + 1
+ rx = RadioSettingValueInteger(1, 16, val)
+ sx = "- Contrast level"
+ rset = RadioSetting("pmg/%i.bkltcont" % ix, sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[ix], "bkltcont", -1)
+ pmm.append(rset)
+
+ opts = ["Positive", "Negative"]
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].dsprev])
+ sx = "- Color mode"
+ rset = RadioSetting("pmg/%i.dsprev" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "dsprev")
+ pmm.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[ix].beepon))
+ sx = "- Beep On"
+ rset = RadioSetting("pmg/%i.beepon" % ix, sx, rx)
+ pmm.append(rset)
+
+ val = _pmg[ix].beepvol + 1 # 1-7 downloads as 0-6
+ rx = RadioSettingValueInteger(1, 7, val)
+ sx = "- Beep volume (1 - 7)"
+ rset = RadioSetting("pmg/%i.beepvol" % ix, sx, rx)
+ rset.set_apply_callback(_adjraw, _pmg[ix], "beepvol", -1)
+ pmm.append(rset)
+
+ rx = RadioSettingValueBoolean(bool(_pmg[ix].autopm))
+ sx = "- Auto PM Store"
+ rset = RadioSetting("pmg/%i.autopm" % ix, sx, rx)
+ pmm.append(rset)
+
+ opts = ["A: Left", "B: Right"]
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].txband])
+ sx = "- X Side (PTT)"
+ rset = RadioSetting("pmg/%i.txband" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "txband")
+ pmm.append(rset)
+
+ opts = ["High (50W)", "Medium (10W)", "Low (5W)"]
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].a_pwr])
+ sx = "- A-Band transmit power"
+ rset = RadioSetting("pmg/%i.a_pwr" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "a_pwr")
+ pmm.append(rset)
+
+ rx = RadioSettingValueList(opts, opts[_pmg[ix].b_pwr])
+ sx = "- B-Band transmit power"
+ rset = RadioSetting("pmg/%i.b_pwr" % ix, sx, rx)
+ rset.set_apply_callback(_val_list, opts, _pmg[ix], "b_pwr")
+ pmm.append(rset)
+
+ return group # END get_settings()
+
+ def set_settings(self, settings):
+ """ Convert UI modified changes into mem_format values """
+ blks = (self._memobj.block1, self._memobj.block1a,
+ self._memobj.pmg, self._memobj.pm_name)
+ for _settings in blks:
+ 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 as e:
+ LOG.debug(element.get_name())
+ raise
+ return
+
+ @classmethod
+ def match_model(cls, fdata, fyle):
+ """ Included to prevent 'File > New' error """
+ return False
+
+
+if HAS_FUTURE: # Only register drivers if environment is PY3 compliant
+ @directory.register
+ class KenwoodTMD710Radio(KenwoodTMx710Radio):
+ """ Kenwood TM-D710 VHF/UHF/APRS Radio model. """
+ VENDOR = "Kenwood"
+ MODEL = "TM-D710_CloneMode"
+ SHORT = "" # Quick model code
+
+ _num_blocks = 3
+ _num_packets = [0x9c, 1, 1]
+
+ MEM_FORMAT = """
+ struct chns { // 16 bytes channel structure
+ ul32 rxfreq;
+ u8 tstep;
+ u8 mode;
+ u8 tmode:4,
+ duplex:4; // 4 = split
+ u8 rtone;
+ u8 ctone;
+ u8 dtcs;
+ ul32 offset; // or Split mode TX freq
+ u8 splitstep;
+ u8 cross; // not used
+ };
+
+ struct pm_grp { // 512 bytes per group
+ u8 unk0200;
+ u8 a_mr;
+ u8 unk0202;
+ u8 unk0203;
+ u8 unk0204;
+ u8 unk0205;
+ u8 unk0206;
+ u8 a_pwr;
+ u8 wxalerta;
+ u8 asmsql;
+ u8 a_chn;
+ u8 unk020b;
+ u8 unk020c;
+ u8 b_mr;
+ u8 unk020e;
+ u8 unk020f;
+ u8 unk0210;
+ u8 unk0211;
+ u8 unk0212;
+ u8 b_pwr;
+ u8 wxalertb;
+ u8 bsmsql;
+ u8 b_chn;
+ u8 unk0217;
+ u8 unk0218;
+ u8 unk0219;
+ u8 unk021a;
+ u8 unk021b;
+ u8 unk021c;
+ u8 unk021d;
+ u8 unk021e;
+ u8 unk021f;
+ u8 unk0220;
+ u8 unk0221;
+ u8 unk0222;
+ u8 unk0223;
+ u8 unk0224;
+ u8 unk0225;
+ u8 unk0226;
+ u8 unk0227;
+ u8 unk0228;
+ u8 unk0229;
+ u8 unk022a;
+ u8 unk022b;
+ u8 unk022c;
+ u8 unk022d;
+ u8 unk022e;
+ u8 unk022f;
+ u8 unk0230;
+ u8 unk0231;
+ u8 sqclogic;
+ u8 txband;
+ u8 single;
+ u8 unk0235;
+ u8 mute;
+ u8 unk0237;
+ u8 unk0238;
+ u8 unk0239;
+ u8 unk0237a;
+ u8 unk023b;
+ u8 unk023c;
+ u8 unk023d;
+ u8 unk023e;
+ u8 unk023f;
+ struct chns vfo[10]; // 0x0240 - 0x02df
+ char pwron[8];
+ u8 unk02e8;
+ u8 unk02e9;
+ u8 unk02ea;
+ u8 unk02eb;
+ u8 unk02ec;
+ u8 unk02ed;
+ u8 unk02ee;
+ u8 unk02ef;
+ char memgrplk[10];
+ u8 unk02fa;
+ u8 unk02fb;
+ u8 unk02fc;
+ u8 unk02fd;
+ u8 unk02fe;
+ u8 unk02ff;
+ struct {
+ ul32 blow;
+ ul32 bhigh;
+ } progvfo[10];
+ u8 beepon;
+ u8 beepvol;
+ u8 extspkr;
+ u8 ance;
+ u8 lang;
+ u8 vcvol;
+ u8 vcspd;
+ u8 pbkrpt;
+ u8 pbkint;
+ u8 cntrec;
+ u8 vhfaip;
+ u8 uhfaip;
+ u8 ssqlhu;
+ u8 mutehu;
+ u8 beatshft;
+ u8 tot;
+ u8 recall;
+ u8 eclnkspd;
+ u8 dtmfhld;
+ u8 dtmfspd;
+ u8 dtmfpau;
+ u8 dtmflck;
+ u8 rptrofst;
+ u8 rptr1750;
+ u8 bright;
+ u8 autobri;
+ u8 bkltclr;
+ u8 pf1key;
+ u8 pf2key;
+ u8 micpf1;
+ u8 micpf2;
+ u8 micpf3;
+ u8 micpf4;
+ u8 miclck;
+ u8 unk0372;
+ u8 scnrsm;
+ u8 apo;
+ u8 extband;
+ u8 extbaud;
+ u8 sqcsrc;
+ u8 autopm;
+ u8 dispbar;
+ u8 unk037a;
+ u8 bkltcont;
+ u8 dsprev;
+ u8 vsmode;
+ u8 intband;
+ u8 wxscntm;
+ u8 scntot;
+ u8 scncot;
+ u8 unk0382;
+ u8 unk0383;
+ u8 unk0384;
+ u8 unk0385;
+ u8 unk0386;
+ u8 unk0387;
+ u8 unk0388;
+ u8 unk0389;
+ u8 unk038a;
+ u8 unk038b;
+ u8 unk038c;
+ u8 unk038d;
+ u8 unk038e;
+ u8 unk038f;
+ u8 abnd118;
+ u8 abnd144;
+ u8 abnd220;
+ u8 abnd300;
+ u8 abnd430;
+ u8 bbnd144;
+ u8 bbnd220;
+ u8 bbnd300;
+ u8 bbnd430;
+ u8 bbnd800;
+ u8 unk039a;
+ u8 unk039b;
+ u8 unk039c;
+ u8 unk039d;
+ u8 unk039e;
+ u8 unk039f;
+ u8 unk03a0[96]; // to 0x03ff
+ }; // end of struct pm
+
+ #seekto 0x0000; // block1: x000 - x023f
+ struct {
+ u8 unk000[16];
+ u8 unk010;
+ u8 unk011;
+ char unk012[3];
+ u8 ansbck;
+ u8 pmrecall; // 0x0016
+ u8 pnlklk;
+ u8 dspmemch;
+ u8 m10mz;
+ u8 micsens;
+ u8 opband;
+ u8 unk01c;
+ u8 rptrmode;
+ u8 rptrhold;
+ u8 rptridx;
+ u8 unk020;
+ u8 pcbaud;
+ u8 unk022;
+ u8 pwdon; // 0x0023
+ u8 unk024;
+ u8 unk025;
+ u8 unk026;
+ u8 unk027;
+ u8 unk028;
+ u8 unk029;
+ char pswd[6]; // 0x023a - 23f
+ } block1;
+
+ #seekto 0x0030;
+ struct {
+ char code[16]; // @ 0x0030
+ } dtmc[10];
+
+ struct {
+ char id[8]; // 0x00d0 - 0x011f
+ } dtmn[10];
+
+ struct { // block1a: 0x0120 - 0x023f
+ u8 unk0120;
+ u8 unk0121;
+ u8 unk0122[78];
+ char rptrid[12]; // 0x0170 - 017b
+ u8 unk017c;
+ u8 unk017d;
+ u8 unk017e;
+ u8 unk017f;
+ u8 unk0180[128]; // 0x0180 - 0x01ff
+ } block1a;
+
+ struct pm_grp pmg[6]; // 0x0200 - 0x0dff
+
+ #seekto 0x0e00;
+ struct {
+ u8 band;
+ u8 skip;
+ } chmap[1030]; // to 0x0160b
+
+ #seekto 0x01700; // 0x01700 - 0x0575f
+ struct chns ch_mem[1030]; // 0-999 MR and 1000 -1029 Specials
+
+ #seekto 0x05760;
+ struct chns call[2];
+
+ #seekto 0x05800;
+ struct {
+ char name[8];
+ } ch_nam[1020]; // ends @ 0x07e0
+
+ #seekto 0x077e0; // 0x077e0 - 0x07830
+ struct {
+ char name[8];
+ } wxnam[10];
+
+ #seekto 0x07da0;
+ struct {
+ char pmname[16];
+ } pm_name[5];
+
+ #seekto 0x07df0;
+ struct {
+ char comnt[32];
+ } mcpcom;
+
+ #seekto 0x08660;
+ struct {
+ char cmdr[10];
+ char tptr[10];
+ u8 skytone; // 0x08674
+ } skycmd;
+ // data stops at 0x09b98
+ """
+
+ def _read_mem(radio):
+ """ Load the memory map """
+ global BAUD
+ status = chirp_common.Status()
+ status.cur = 0
+ val = 0
+ for mx in range(0, radio._num_blocks):
+ val += radio._num_packets[mx]
+ status.max = val
+ status.msg = "Reading %i packets" % val
+ radio.status_fn(status)
+
+ data = ""
+
+ radio.pipe.baudrate = BAUD
+ cmc = "0M PROGRAM" + TERM
+ resp0 = _command(radio.pipe, cmc, 3, W8S)
+ junk = radio.pipe.read(16) # flushit
+ for bkx in range(0, 0x09c):
+ if bkx != 0x07f: # Skip block 7f !!??
+ cmc = "R" + chr(bkx) + chr(0) + chr(0)
+ resp0 = _command(radio.pipe, cmc, 260, W8S)
+ junk = _command(radio.pipe, ACK, 1, W8S)
+ if len(resp0) < 260:
+ junk = _command(radio.pipe, "E", 2, W8S)
+ sx = "Block 0x%x read error: " % bkx
+ sx += "Got %i bytes, expected 260." % len(resp0)
+ LOG.error(sx)
+ sx = "Block read errror! Check debug.log"
+ raise errors.RadioError(sx)
+ if bkx == 0: # 1st packet of 1st block
+ mht = resp0[4:7] # [57 00 00 00] 03 4b 01 ff ff ...
+ data = resp0[5:6] # 2nd byte (4b) replaces 1st
+ data += resp0[5:] # then bytes 2 on (4b 4b 01 ff ...)
+ else:
+ data += resp0[4:] # skip cmd echo
+ _update_status(radio, status) # UI Update
+ cmc = "R" + chr(0x0fe) + chr(0x0f0) + chr(0x010)
+ resp0 = _command(radio.pipe, cmc, 0x014, W8S)
+ data += resp0[4:]
+ junk = _command(radio.pipe, ACK, 1, W8S)
+ _update_status(radio, status)
+ cmc = "R" + chr(0x0ff) + chr(0) + chr(0x090)
+ resp0 = _command(radio.pipe, cmc, 0x094, W8S)
+ data += resp0[4:]
+ junk = _command(radio.pipe, ACK, 1, W8S)
+ _update_status(radio, status)
+ # Exit Prog mode, no TERM
+ resp = _command(radio.pipe, "E", 2, W8S) # Rtns 06 0d
+ radio.pipe.baudrate = BAUD
+ return data
+
+ def _write_mem(radio):
+ """ PROG MCP Blocks Send """
+ global BAUD
+ # UI progress
+ status = chirp_common.Status()
+ status.cur = 0
+ val = 0
+ for mx in range(0, radio._num_blocks):
+ val += radio._num_packets[mx]
+ status.max = val
+ status.msg = "Writing %i packets" % val
+ radio.status_fn(status)
+
+ imgadr = 0
+ radio.pipe.baudrate = BAUD
+ resp0 = _command(radio.pipe, "0M PROGRAM" + TERM, 3, W8S)
+ # Read block 0 magic header thingy, save it
+ cmc = "R" + chr(0) + chr(0) + chr(4)
+ resp0 = _command(radio.pipe, cmc, 8, W8S)
+ mht0 = resp0[4:] # Expecting [57 00 00 04] 03 4b 01 ff
+ junk = _command(radio.pipe, ACK, 1, W8S)
+ cmc = "W" + chr(0) + chr(0) + chr(1) + chr(0x0ff)
+ junk = _command(radio.pipe, cmc, 1, W8S) # responds ACK
+ cmc = "R" + chr(0x080) + chr(0) + chr(3)
+ resp = _command(radio.pipe, cmc, 7, W8S) # [57 80 00 03] 00 33 00
+ mht1 = resp[4:]
+ junk = _command(radio.pipe, ACK, 1, W8S)
+ cmc = "W" + chr(0x080) + chr(0) + chr(1) + chr(0x0ff)
+ junk = _command(radio.pipe, cmc, 1, W8S)
+ imgadr = 4 # After 03 4b 01 ff
+ for bkx in range(0, radio._num_packets[0]):
+ cmc = "W" + chr(bkx) + chr(0) + chr(0)
+ imgstep = 256
+ if bkx == 0:
+ imgstep = 0x0fc
+ cmc = "W" + chr(0) + chr(4) + chr(imgstep)
+ cmc += radio.get_mmap()[imgadr:imgadr + imgstep]
+ else: # after first packet
+ cmc += radio.get_mmap()[imgadr:imgadr + imgstep]
+ if bkx != 0x07f: # don't send 7f !
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ if resp0 != ACK:
+ LOG.error("Packet 0x%x Write error, no ACK." % bkx)
+ sx = "Radio failed to acknowledge upload packet!"
+ raise errors.RadioError(sx)
+ imgadr += imgstep
+ _update_status(radio, status) # UI Update
+ # write fe and ff blocks
+ cmc = "W" + chr(0x0fe) + chr(0x0f0) + chr(16)
+ cmc += radio.get_mmap()[imgadr:imgadr + 16]
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ if resp0 != ACK:
+ LOG.error("Packet 0xfe Write error, no ACK.")
+ sx = "Radio failed to acknowledge upload packet!"
+ raise errors.RadioError(sx)
+ imgadr += 16
+ cmc = "W" + chr(0x0ff) + chr(0) + chr(0x090)
+ cmc += radio.get_mmap()[imgadr:imgadr + 0x090]
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ if resp0 != ACK:
+ LOG.error("Packet 0xff Write error, no ACK.")
+ sx = "Radio failed to acknowledge upload packet!"
+ raise errors.RadioError(sx)
+ # Write mht1
+ cmc = "W" + chr(0x080) + chr(0) + chr(3) + mht1
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ if resp0 != ACK:
+ LOG.error("Mht1 Write error at 0x080 00 03 , no ACK.")
+ sx = "Radio failed to acknowledge upload packet!"
+ raise errors.RadioError(sx)
+ # and mht0
+ cmc = "W" + chr(0) + chr(0) + chr(4) + mht0
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ if resp0 != ACK:
+ LOG.error("Mht0 Write error at 00 00 04 , no ACK.")
+ sx = "Radio failed to acknowledge upload packet!"
+ raise errors.RadioError(sx)
+ # Write E to Exit PROG mode
+ resp = _command(radio.pipe, "E", 2, W8S)
+ return
+
+ @directory.register
+ class KenwoodTMD710GRadio(KenwoodTMx710Radio):
+ """ Kenwood TM-D710G VHF/UHF/GPS/APRS Radio model. """
+ VENDOR = "Kenwood"
+ MODEL = "TM-D710G_CloneMode"
+ SHORT = "G" # Quick model code 1 for G
+
+ _num_blocks = 2 # Only reading first 2, not GPS logs
+ _packet_size = [261, 261, 261]
+ _block_addr = [0, 0x100, 0x200] # starting addr, each block
+ _num_packets = [0x7f, 0x0fe, 0x200] # num packets per block, 0-based
+
+ MEM_FORMAT = """
+ struct chns { // 16 bytes channel structure
+ ul32 rxfreq;
+ u8 tstep;
+ u8 mode;
+ u8 tmode:4,
+ duplex:4; // 4 = split
+ u8 rtone;
+ u8 ctone;
+ u8 dtcs;
+ u8 cross;
+ ul32 offset; // or Split mode TX freq
+ u8 splitstep;
+ };
+
+ struct pm_grp { // 512 bytes per group
+ u8 unk0200;
+ u8 a_mr;
+ u8 unk0202;
+ u8 unk0203;
+ u8 unk0204;
+ u8 unk0205;
+ u8 unk0206;
+ u8 a_pwr;
+ u8 wxalerta;
+ u8 asmsql;
+ u8 a_chn;
+ u8 unk020b;
+ u8 unk020c;
+ u8 b_mr;
+ u8 unk020e;
+ u8 unk020f;
+ u8 unk0210;
+ u8 unk0211;
+ u8 unk0212;
+ u8 b_pwr;
+ u8 wxalertb;
+ u8 bsmsql;
+ u8 b_chn;
+ u8 unk0217;
+ u8 unk0218;
+ u8 unk0219;
+ u8 unk021a;
+ u8 unk021b;
+ u8 unk021c;
+ u8 unk021d;
+ u8 unk021e;
+ u8 unk021f;
+ u8 unk0220;
+ u8 unk0221;
+ u8 unk0222;
+ u8 unk0223;
+ u8 unk0224;
+ u8 unk0225;
+ u8 unk0226;
+ u8 unk0227;
+ u8 unk0228;
+ u8 unk0229;
+ u8 unk022a;
+ u8 unk022b;
+ u8 unk022c;
+ u8 unk022d;
+ u8 unk022e;
+ u8 unk022f;
+ u8 unk0230;
+ u8 unk0231;
+ u8 sqclogic;
+ u8 txband;
+ u8 single;
+ u8 unk0235;
+ u8 mute;
+ u8 unk0237;
+ u8 unk0238;
+ u8 unk0239;
+ u8 unk0237a;
+ u8 unk023b;
+ u8 unk023c;
+ u8 unk023d;
+ u8 unk023e;
+ u8 unk023f;
+ struct chns vfo[10]; // 0x0240 - 0x02df
+ char pwron[8];
+ u8 unk02e8;
+ u8 unk02e9;
+ u8 unk02ea;
+ u8 unk02eb;
+ u8 unk02ec;
+ u8 unk02ed;
+ u8 unk02ee;
+ u8 unk02ef;
+ char memgrplk[10];
+ u8 unk02fa;
+ u8 unk02fb;
+ u8 unk02fc;
+ u8 unk02fd;
+ u8 unk02fe;
+ u8 unk02ff;
+ struct {
+ ul32 blow;
+ ul32 bhigh;
+ } progvfo[10];
+ u8 beepon;
+ u8 beepvol;
+ u8 extspkr;
+ u8 ance;
+ u8 lang;
+ u8 vcvol;
+ u8 vcspd;
+ u8 pbkrpt;
+ u8 pbkint;
+ u8 cntrec;
+ u8 vhfaip;
+ u8 uhfaip;
+ u8 ssqlhu;
+ u8 mutehu;
+ u8 beatshft;
+ u8 tot;
+ u8 recall;
+ u8 eclnkspd;
+ u8 dtmfhld;
+ u8 dtmfspd;
+ u8 dtmfpau;
+ u8 dtmflck;
+ u8 rptrofst;
+ u8 rptr1750;
+ u8 bright;
+ u8 autobri;
+ u8 bkltclr;
+ u8 pf1key;
+ u8 pf2key;
+ u8 micpf1;
+ u8 micpf2;
+ u8 micpf3;
+ u8 micpf4;
+ u8 miclck;
+ u8 unk0372;
+ u8 scnrsm;
+ u8 apo;
+ u8 extband;
+ u8 extbaud;
+ u8 sqcsrc;
+ u8 autopm;
+ u8 dispbar;
+ u8 unk037a;
+ u8 bkltcont;
+ u8 dsprev;
+ u8 vsmode;
+ u8 intband;
+ u8 wxscntm;
+ u8 scntot;
+ u8 scncot;
+ u8 unk0382;
+ u8 unk0383;
+ u8 unk0384;
+ u8 unk0385;
+ u8 unk0386;
+ u8 unk0387;
+ u8 unk0388;
+ u8 unk0389;
+ u8 unk038a;
+ u8 unk038b;
+ u8 unk038c;
+ u8 unk038d;
+ u8 unk038e;
+ u8 unk038f;
+ u8 abnd118;
+ u8 abnd144;
+ u8 abnd220;
+ u8 abnd300;
+ u8 abnd430;
+ u8 bbnd144;
+ u8 bbnd220;
+ u8 bbnd300;
+ u8 bbnd430;
+ u8 bbnd800;
+ u8 unk039a;
+ u8 unk039b;
+ u8 unk039c;
+ u8 unk039d;
+ u8 unk039e;
+ u8 unk039f;
+ u8 unk03a0[96]; // to 0x03ff
+ }; // end of struct pm
+
+ #seekto 0x0000; // block1: x000 - x023f
+ struct {
+ u8 unk000[16];
+ u8 unk010;
+ u8 unk011;
+ char unk012[3];
+ u8 ansbck;
+ u8 pmrecall; // 0x0016
+ u8 pnlklk;
+ u8 dspmemch;
+ u8 m10mz;
+ u8 micsens;
+ u8 opband;
+ u8 unk01c;
+ u8 rptrmode;
+ u8 rptrhold;
+ u8 rptridx;
+ u8 unk020;
+ u8 pcbaud;
+ u8 unk022;
+ u8 pwdon; // 0x0023
+ u8 unk024;
+ u8 unk025;
+ u8 unk026;
+ u8 unk027;
+ u8 unk028;
+ u8 unk029;
+ char pswd[6]; // 0x023a - 23f
+ } block1;
+
+ #seekto 0x0030;
+ struct {
+ char code[16]; // @ 0x0030
+ } dtmc[10];
+
+ struct {
+ char id[8]; // 0x00d0 - 0x011f
+ } dtmn[10];
+
+ struct { // block1a: 0x0120 - 0x023f
+ u8 unk0120;
+ u8 unk0121;
+ u8 unk0122[78];
+ char rptrid[12]; // 0x0170 - 017b
+ u8 unk017c;
+ u8 unk017d;
+ u8 unk017e;
+ u8 unk017f;
+ u8 unk0180[128]; // 0x0180 - 0x01ff
+ } block1a;
+
+ struct pm_grp pmg[6]; // 0x0200 - 0x0dff
+
+ #seekto 0x0e00;
+ struct {
+ u8 band;
+ u8 skip;
+ } chmap[1030]; // to 0x0160b
+
+ #seekto 0x01700; // 0x01700 - 0x0575f
+ struct chns ch_mem[1030]; // 0-999 MR and 1000 -1029 Specials
+
+ #seekto 0x058a0;
+ struct chns call[2];
+
+ #seekto 0x05900;
+ struct {
+ char name[8];
+ } ch_nam[1020]; // ends @ 0x07840
+
+ #seekto 0x078e0; // 0x078e0 - 0x0792f
+ struct {
+ char name[8];
+ } wxnam[10];
+
+ #seekto 0x07da0;
+ struct {
+ char pmname[16];
+ } pm_name[5];
+
+ #seekto 0x07df0;
+ struct {
+ char comnt[32];
+ } mcpcom;
+ // Block 1 ends @ 0x07eff
+ // Block 2 starts @ 0x07f00
+ #seekto 0x08660;
+ struct {
+ char cmdr[10];
+ char tptr[10];
+ u8 skytone; // 0x08674
+ } skycmd;
+
+ #seekto 0x10ef0;
+ struct {
+ u8 bmp[1896];
+ u8 unk11658[8]; // 0x11658
+ char bmpfyl[64]; // 0x11660
+ u8 unk116a0[95];
+ u8 bmpon; // 0x116ff
+ } bitmap;
+
+ // 2nd block ends @ 0x017cff
+ """
+
+ def _read_mem(radio):
+ """ Load the memory map """
+ global BAUD
+ status = chirp_common.Status()
+ status.cur = 0
+ val = 0
+ for mx in range(0, radio._num_blocks):
+ val += radio._num_packets[mx]
+ status.max = val
+ status.msg = "Reading %i packets" % val
+ radio.status_fn(status)
+
+ data = ""
+
+ radio.pipe.baudrate = BAUD
+ resp0 = radio.pipe.read(16) # flush
+ cmc = "0M PROGRAM" + TERM
+ resp0 = _command(radio.pipe, cmc, 3, W8S)
+ if resp0[:1] == "?": # try once more
+ resp0 = _command(radio.pipe, cmc, 3, W8S)
+ radio.pipe.baudrate = 57600 # PROG mode is always 57.6
+ LOG.debug("Switching to 57600 baud download.")
+ junk = radio.pipe.read(1) # trailing byte
+ for blkn in range(0, radio._num_blocks):
+ for bkx in range(0, radio._num_packets[blkn]):
+ cmc = "R" + _make_address(radio._block_addr[blkn], bkx,
+ 0, 0)
+ resp0 = _command(radio.pipe, cmc,
+ radio._packet_size[blkn], W8S)
+ if len(resp0) < radio._packet_size[blkn]:
+ junk = _command(radio.pipe, "E", 0, W8S)
+ lb = len(resp0)
+ xb = radio._packet_size[blkn]
+ sx = "Block 0x%x, 0x%x read error: " % (blkn, bkx)
+ sx += "Got %i bytes, expected %i." % (lb, xb)
+ LOG.error(sx)
+ sx = "Block read errror! Check debug.log"
+ raise errors.RadioError(sx)
+ if blkn == 0 and bkx == 0: # 1st packet of 1st block
+ mht = resp0[5:9] # Magic Header Thingy after cmd echo
+ data += mht[0:1]
+ data += chr(255) + chr(255) + chr(255)
+ data += resp0[9:]
+ else:
+ data += resp0[5:] # skip cmd echo
+ _update_status(radio, status) # UI Update
+ # Exit Prog mode, no TERM
+ resp = _command(radio.pipe, "E", 0, W8S)
+ radio.pipe.baudrate = BAUD
+ return data
+
+ def _write_mem(radio):
+ """ PROG MCP Blocks Send """
+ global BAUD
+ # UI progress
+ status = chirp_common.Status()
+ status.cur = 0
+ val = 0
+ for mx in range(0, radio._num_blocks):
+ val += radio._num_packets[mx]
+ status.max = val
+ status.msg = "Writing %i packets" % val
+ radio.status_fn(status)
+
+ imgadr = 0
+ radio.pipe.baudrate = BAUD
+ resp0 = _command(radio.pipe, "0M PROGRAM" + TERM, 3, W8S)
+ radio.pipe.baudrate = 57600
+ LOG.debug("Switching to 57600 baud upload.")
+ junk = radio.pipe.read(1)
+ # Read block 0 magic header thingy, save it
+ cmc = "R" + _make_address(radio._block_addr[0], 0, 0, 4)
+ resp0 = _command(radio.pipe, cmc, 16, W8S)
+ mht0 = resp0[5:]
+ # Now get block 1 mht
+ cmc = "R" + _make_address(radio._block_addr[1], 0, 0, 5)
+ resp0 = _command(radio.pipe, cmc, 16, W8S)
+ mht1 = resp0[5:]
+ for blkn in range(0, radio._num_blocks):
+ for bkx in range(0, radio._num_packets[blkn]):
+ cmc = "W" + _make_address(radio._block_addr[blkn], bkx,
+ 0, 0)
+ if bkx == 0: # First packet of the block includes mht
+ if blkn == 0:
+ cmc += chr(255) + chr(0x04b) + chr(1) + \
+ chr(0x032) + radio.get_mmap()[4:imgadr + 256]
+ elif blkn == 1:
+ cmc += mht1 + radio.get_mmap()[imgadr + 5:imgadr
+ + 256]
+ else: # after first packet
+ cmc += radio.get_mmap()[imgadr:imgadr + 256]
+ resp0 = _command(radio.pipe, cmc, 6, W8S)
+ if bkx > 0 and resp0 != ACK:
+ LOG.error("Packet 0x%x Write error, no ACK!" % bkx)
+ sx = "Radio failed to acknowledge upload. "
+ sx += "See debug.log"
+ raise errors.RadioError(sx)
+ imgadr += 256
+ _update_status(radio, status) # UI Update
+ # Re-write magic headers
+ cmc = "W" + _make_address(radio._block_addr[0], 0, 1, 3)
+ cmc += mht0[1:3] + chr(0x032)
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ cmc = "W" + _make_address(radio._block_addr[1], 0, 0, 5)
+ cmc += mht1
+ resp0 = _command(radio.pipe, cmc, 1, W8S)
+ cmc = "Z" + _make_address(radio._block_addr[0], 0, 0, 1) \
+ + mht0[0:1]
+ resp0 = _command(radio.pipe, cmc, 16, W8S)
+ # Write E to Exit PROG mode
+ resp = _command(radio.pipe, "E", 0, W8S)
+ radio.pipe.baudrate = BAUD
+ return
diff -r e0dbf1a6f190 -r 7afcb68bd934 tools/cpep8.manifest
--- a/tools/cpep8.manifest Wed Apr 08 16:10:56 2020 -0700
+++ b/tools/cpep8.manifest Tue Apr 28 10:58:00 2020 -0700
@@ -82,6 +82,7 @@
./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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Kenwood_TM-D710G_CloneMode.img
Type: application/octet-stream
Size: 97721 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20200526/b47a5efe/attachment-0002.img
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Kenwood_TM-D710_CloneMode.img
Type: application/octet-stream
Size: 40021 bytes
Desc: not available
Url : http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20200526/b47a5efe/attachment-0003.img
-------------- 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/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
More information about the chirp_devel
mailing list