Project

General

Profile

Bug #10775 » tmd710.py

5ca3c6a4 - Dan Smith, 11/19/2023 02:08 PM

 
1
# Copyright 2011 Dan Smith <dsmith@danplanet.com>
2
# --        2019 Rick DeWitt <aa0rd@yahoo.com>
3
# -- Implementing Kenwood TM-D710G as MCP Clone Mode for Python 2.7
4
# -- Thanks to Herm Halbach, W7HRM, for the 710 model testing.
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17

    
18
import time
19
import struct
20
import logging
21
from chirp import chirp_common, directory, memmap
22
from chirp import bitwise, errors, util
23
from chirp.settings import RadioSettingGroup, RadioSetting, \
24
    RadioSettingValueBoolean, RadioSettingValueList, \
25
    RadioSettingValueString, RadioSettingValueInteger, \
26
    RadioSettingValueFloat, RadioSettings
27
from chirp.drivers import kenwood_live
28

    
29
LOG = logging.getLogger(__name__)
30

    
31
HAS_FUTURE = True
32
try:                         # PY3 compliance
33
    from builtins import bytes
34
except ImportError:
35
    HAS_FUTURE = False
36
    LOG.debug('python-future package is not '
37
              'available; %s requires it' % __name__)
38

    
39
BAUD = 0
40
STIMEOUT = 0.2
41
TERM = b'\x0d'         # Cmd write terminator (CR)
42
ACK = b'\x06'           # Data write acknowledge char
43
W8S = 0.001      # short wait, secs
44
W8L = 0.1       # long wait
45
TMD710_DUPLEX = ["", "+", "-", "n/a", "split"]
46
TMD710_SKIP = ["", "S"]
47
TMD710_MODES = ["FM", "NFM", "AM"]
48
TMD710_BANDS = [(118000000, 135995000),
49
                (136000000, 199995000),
50
                (200000000, 299995000),
51
                (300000000, 399995000),
52
                (400000000, 523995000),
53
                (800000000, 1299995000)]
54
TMD710_STEPS = [5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0,
55
                30.0, 50.0, 100.0]
56
# Need string list of those steps for mem.extra value list
57
STEPS_STR = []
58
for val in TMD710_STEPS:
59
    STEPS_STR.append("%3.2f" % val)
60
TMD710_TONE_MODES = ["", "Tone", "TSQL", "DTCS", "Cross"]
61
TMD710_CROSS = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone"]
62
TMD710_DTSC = list(chirp_common.DTCS_CODES)
63
TMD710_TONES = list(chirp_common.TONES)
64
TMD710_TONES.remove(159.8)
65
TMD710_TONES.remove(165.5)
66
TMD710_TONES.remove(171.3)
67
TMD710_TONES.remove(177.3)
68
TMD710_TONES.remove(183.5)
69
TMD710_TONES.remove(189.9)
70
TMD710_TONES.remove(196.6)
71
TMD710_TONES.remove(199.5)
72
TMD710_CHARS = chirp_common.CHARSET_ASCII
73
TMD710_CHARS += chr(34)     # "
74

    
75

    
76
def _command(ser, cmd, rsplen, w8t=0.01):
77
    """Send cmd to radio via ser port
78
    cmd is output string with possible terminator
79
    rsplen is expected response char count, NOT incl prefix and term
80
    If rsplen = 0 then do not0 read after write """
81
    ser.write(cmd)
82
    LOG.debug(" Out %4i ->: %s" % (len(cmd), util.hexprint(cmd[0: 32])))
83
    time.sleep(w8t)
84
    result = b""
85
    if rsplen > 0:  # read response
86
        result = ser.read(rsplen)
87
        LOG.debug(" In %4i <-: %s" % (len(result),
88
                                      util.hexprint(result[0: 32])))
89
    return result
90

    
91

    
92
def _connect_radio(radio):
93
    """Determine baud rate and verify radio on-line"""
94
    global BAUD
95
    xid = "D710" + radio.SHORT
96
    resp = kenwood_live.get_id(radio.pipe)
97
    BAUD = radio.pipe.baudrate      # As detected by kenwood_live
98
    LOG.debug("Got [%s] at %i Baud." % (resp, BAUD))
99
    resp = resp[3:]     # Strip "ID " prefix
100
    if len(resp) > 2:   # Got something from "ID"
101
        if resp == xid:     # Good comms
102
            return
103
        else:
104
            stx = "Radio responded as %s, not %s." % (resp, xid)
105
            raise errors.RadioError(stx)
106
    raise errors.RadioError("No response from radio")
107

    
108

    
109
def _update_status(self, status, step=1):
110
    """ Increment status bar """
111
    status.cur += step
112
    self.status_fn(status)
113
    return
114

    
115

    
116
def _val_list(setting, opts, obj, atrb, fix=0, ndx=-1):
117
    """Callback:from ValueList. Set the integer index.
118
    This function is here to be available to get_mem and get_set
119
    fix is optional additive offset to the list index
120
    ndx is optional obj[ndx] array index """
121
    value = opts.index(str(setting.value))
122
    value += fix
123
    if ndx >= 0:    # indexed obj
124
        setattr(obj[ndx], atrb, value)
125
    else:
126
        setattr(obj, atrb, value)
127
    return
128

    
129

    
130
class KenwoodTMx710Radio(chirp_common.CloneModeRadio):
131
    """ Base class for TMD-710 """
132
    VENDOR = "Kenwood"
133
    MODEL = "TM-x710"
134
    SHORT = "X"       # Short model ID code
135
    NEEDS_COMPAT_SERIAL = False
136

    
137
    _upper = 999         # Number of normal chans
138

    
139
    # Put Special memory channels after normal ones
140
    SPECIAL_MEMORIES = {"Scan-0Lo": 1000, "Scan-0Hi": 1001,
141
                        "Scan-1Lo": 1002, "Scan-1Hi": 1003,
142
                        "Scan-2Lo": 1004, "Scan-2Hi": 1005,
143
                        "Scan-3Lo": 1006, "Scan-3Hi": 1007,
144
                        "Scan-4Lo": 1008, "Scan-4Hi": 1009,
145
                        "Scan-5Lo": 1010, "Scan-5Hi": 1011,
146
                        "Scan-6Lo": 1012, "Scan-6Hi": 1013,
147
                        "Scan-7Lo": 1014, "Scan-7Hi": 1015,
148
                        "Scan-8Lo": 1016, "Scan-8Hi": 1017,
149
                        "Scan-9Lo": 1018, "Scan-9Hi": 1019,
150
                        "WX-1": 1020, "WX-2": 1021,
151
                        "WX-3": 1022, "WX-4": 1023,
152
                        "WX-5": 1024, "WX-6": 1025,
153
                        "WX-7": 1026, "WX-8": 1027,
154
                        "WX-9": 1028, "WX-10": 1029,
155
                        "Call C0": 1030, "Call C1": 1031
156
                        }
157
    # _REV dict is used to retrieve name given number
158
    SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(),
159
                                    SPECIAL_MEMORIES.keys()))
160

    
161
    def get_features(self):
162
        rf = chirp_common.RadioFeatures()
163
        rf.can_odd_split = True
164
        rf.has_dtcs = True
165
        rf.has_dtcs_polarity = False
166
        if self.SHORT == "G":             # NOT for D710
167
            rf.has_rx_dtcs = True       # Enable DTCS Rx Code column
168
            rf.has_cross = True
169
            rf.valid_cross_modes = TMD710_CROSS
170
        rf.has_bank = False
171
        rf.has_settings = True
172
        rf.has_ctone = True
173
        rf.has_mode = True
174
        rf.has_comment = False
175
        rf.valid_tmodes = TMD710_TONE_MODES
176
        rf.valid_modes = TMD710_MODES
177
        rf.valid_duplexes = TMD710_DUPLEX
178
        rf.valid_tuning_steps = TMD710_STEPS
179
        rf.valid_tones = TMD710_TONES
180
        rf.valid_dtcs_codes = TMD710_DTSC
181
        # Supports upper and lower case text
182
        rf.valid_characters = TMD710_CHARS
183
        rf.valid_name_length = 8
184
        rf.valid_skips = TMD710_SKIP
185
        rf.valid_bands = TMD710_BANDS
186
        rf.memory_bounds = (0, 999)        # including special chans 1000-1029
187
        rf.valid_special_chans = sorted(self.SPECIAL_MEMORIES.keys())
188
        return rf
189

    
190
    @classmethod
191
    def get_prompts(cls):
192
        rp = chirp_common.RadioPrompts()
193
        rp.pre_download = _(
194
            "Connect your interface cable to the PC Port on the\n"
195
            "back of the 'TX/RX' unit. NOT the Com Port on the head.\n")
196
        rp.pre_upload = _(
197
            "Connect your interface cable to the PC Port on the\n"
198
            "back of the 'TX/RX' unit. NOT the Com Port on the head.\n")
199
        return rp
200

    
201
    def sync_in(self):
202
        """Download from radio"""
203
        try:
204
            _connect_radio(self)
205
            data = bytes(self._read_mem())
206
        except errors.RadioError:
207
            # Pass through any real errors we raise
208
            raise
209
        except Exception:
210
            # If anything unexpected happens, make sure we raise
211
            # a RadioError and log the problem
212
            LOG.exception('Unexpected error during download')
213
            raise errors.RadioError('Unexpected error communicating '
214
                                    'with the radio')
215
        self._mmap = memmap.MemoryMapBytes(data)
216
        self.process_mmap()
217

    
218
    def sync_out(self):
219
        """Upload to radio"""
220
        try:
221
            _connect_radio(self)
222
            self._write_mem()
223
        except Exception:
224
            # If anything unexpected happens, make sure we raise
225
            # a RadioError and log the problem
226
            LOG.exception('Unexpected error during upload')
227
            raise errors.RadioError('Unexpected error communicating '
228
                                    'with the radio')
229

    
230
    def process_mmap(self):
231
        """Process the mem map into the mem object"""
232
        self._memobj = bitwise.parse(self.MEM_FORMAT, self._mmap)
233

    
234
    def get_memory(self, number):
235
        """Convert raw channel data (_mem) into UI columns (mem)"""
236
        mem = chirp_common.Memory()
237
        if self.SHORT == "G":
238
            mem.extra = RadioSettingGroup("extra", "Extra")
239
        # If called from 'Properties', spcl chans number is integer
240
        propflg = False
241
        if isinstance(number, int):
242
            if number > 999:
243
                propflg = True
244
        if isinstance(number, str) or propflg:
245
            if propflg:
246
                mem.number = number
247
                mem.name = self.SPECIAL_MEMORIES_REV[number]
248
                mem.extd_number = mem.name
249
            else:
250
                mem.name = number   # Spcl chns 1st var
251
                mem.number = self.SPECIAL_MEMORIES[number]
252
                mem.extd_number = number    # Uses name as LOC
253
                mem.immutable = ["name"]
254
            if mem.number < 1030:       # Scan edges, WX
255
                _mem = self._memobj.ch_mem[mem.number]
256
                _map = self._memobj.chmap[mem.number]
257
            else:                       # Call chans
258
                _mem = self._memobj.call[mem.number - 1030]
259
        else:       # Normal mem chans
260
            _mem = self._memobj.ch_mem[number]
261
            _nam = self._memobj.ch_nam[number]
262
            _map = self._memobj.chmap[number]
263
            mem.number = number
264
            mnx = ""
265
            for char in _nam.name:
266
                if int(char) < 127:
267
                    mnx += chr(int(char))
268
            mem.name = mnx.rstrip()
269
        if _mem.rxfreq == 0x0ffffffff or _mem.rxfreq == 0:
270
            mem.empty = True
271
            return mem
272
        mem.empty = False
273
        if mem.number < 1030 and _map.skip != 0x0ff:      # empty
274
            mem.skip = TMD710_SKIP[_map.skip]
275
        mem.freq = int(_mem.rxfreq)
276
        mem.duplex = TMD710_DUPLEX[_mem.duplex]
277
        mem.offset = int(_mem.offset)
278
        # Duplex = 4 (split); offset contains the TX freq
279
        mem.mode = TMD710_MODES[_mem.mode]
280
        # _mem.tmode is 4-bit pattern, not number
281
        mx = 0      # No tone
282
        mem.cross_mode = TMD710_CROSS[0]
283
        mem.rx_dtcs = TMD710_DTSC[_mem.dtcs]
284
        mem.dtcs = TMD710_DTSC[_mem.dtcs]
285
        if self.SHORT == "G":
286
            if _mem.tmode & 8:     # Tone
287
                mx = 1
288
            if _mem.tmode & 4:     # Tsql
289
                mx = 2
290
            if _mem.tmode & 2:     # Dtcs
291
                mx = 3
292
            if _mem.tmode & 1:     # Cross
293
                mx = 4
294
                if _mem.cross == 1:     # Tone->DTCS
295
                    mem.cross_mode = TMD710_CROSS[1]
296
                if _mem.cross == 2:     # DTCS->Tone
297
                    mem.cross_mode = TMD710_CROSS[2]
298
        else:           # D710; may have bit 8 set
299
            if _mem.tmode & 4:     # Tone
300
                mx = 1
301
            if _mem.tmode & 2:     # Tsql
302
                mx = 2
303
            if _mem.tmode & 1:     # Dtcs
304
                mx = 3
305
                mem.dtcs = TMD710_DTSC[_mem.dtcs]
306
        mem.tmode = TMD710_TONE_MODES[mx]
307
        mem.ctone = TMD710_TONES[_mem.ctone]
308
        mem.rtone = TMD710_TONES[_mem.rtone]
309
        mem.tuning_step = TMD710_STEPS[_mem.tstep]
310

    
311
        if self.SHORT == "G":         # Only the 710G
312
            rx = RadioSettingValueList(STEPS_STR, STEPS_STR[_mem.splitstep])
313
            sx = "Split TX step (kHz)"
314
            rset = RadioSetting("splitstep", sx, rx)
315
            mem.extra.append(rset)
316

    
317
        return mem
318

    
319
    def set_memory(self, mem):
320
        """Convert UI column data (mem) into MEM_FORMAT memory (_mem)"""
321
        if mem.number > 999:      # Special chans
322
            if mem.number < 1030:        # Scan, Wx
323
                _mem = self._memobj.ch_mem[mem.number]
324
                _map = self._memobj.chmap[mem.number]
325
            else:                        # Call chans
326
                _mem = self._memobj.call[mem.number - 1030]
327
            _nam = None
328
        else:
329
            _mem = self._memobj.ch_mem[mem.number]
330
            _nam = self._memobj.ch_nam[mem.number]
331
            _map = self._memobj.chmap[mem.number]
332
            nx = len(mem.name)
333
            for ix in range(8):
334
                if ix < nx:
335
                    _nam.name[ix] = mem.name[ix]
336
                else:
337
                    _nam.name[ix] = chr(0x0ff)    # needs 8 chrs
338
        if mem.empty:
339
            _mem.rxfreq = 0x0ffffffff
340
            _mem.offset = 0x0ffffff
341
            _mem.duplex = 0x0f
342
            _mem.tstep = 0x0ff
343
            _mem.tmode = 0x0f
344
            _mem.mode = 0x0ff
345
            _mem.rtone = 0x0ff
346
            _mem.ctone = 0x0ff
347
            _mem.dtcs = 0x0ff
348
            _map.skip = 0x0ff
349
            _map.band = 0x0ff
350
            if _nam:
351
                for ix in range(8):
352
                    _nam.name[ix] = chr(0x0ff)
353
            return
354
        if _mem.rxfreq == 0x0ffffffff:    # New Channel needs defaults
355
            _mem.rxfreq = 144000000
356
            _map.band = 5
357
            _map.skip = 0
358
            _mem.mode = 0
359
            _mem.duplex = 0
360
            _mem.offset = 0
361
            _mem.rtone = 8
362
            _mem.ctone = 8
363
            _mem.dtcs = 0
364
            _mem.tstep = 0
365
            _mem.splitstep = 0
366
        # Now use the UI values entered so far
367
        _mem.rxfreq = mem.freq
368
        _mem.mode = TMD710_MODES.index(mem.mode)
369
        try:
370
            _tone = mem.rtone
371
            _mem.rtone = TMD710_TONES.index(mem.rtone)
372
            _tone = mem.ctone
373
            _mem.ctone = TMD710_TONES.index(mem.ctone)
374
        except ValueError:
375
            raise errors.UnsupportedToneError("This radio does not support " +
376
                                              "tone %.1fHz" % _tone)
377
        _mem.dtcs = TMD710_DTSC.index(mem.dtcs)
378
        _mem.tmode = 0      # None
379
        _mem.cross = 0
380
        if self.SHORT == "G":
381
            if mem.tmode == "Tone":
382
                _mem.tmode = 8
383
            if mem.tmode == "TSQL":
384
                _mem.tmode = 4
385
            if mem.tmode == "DTCS":
386
                _mem.tmode = 2
387
            if mem.tmode == "Cross":
388
                _mem.tmode = 1
389
                mx = TMD710_CROSS.index(mem.cross_mode)
390
                _mem.cross = 3          # t -t
391
                if mx == 1:
392
                    _mem.cross = 1      # t-d
393
                    _mem.dtcs = TMD710_DTSC.index(mem.rx_dtcs)
394
                if mx == 2:
395
                    _mem.cross = 2      # d-t
396
                    _mem.dtcs = TMD710_DTSC.index(mem.dtcs)
397
        else:
398
            _mem.tmode = 0x80       # None
399
            if mem.tmode == "Tone":
400
                _mem.tmode = 0x0c
401
            if mem.tmode == "TSQL":
402
                _mem.tmode = 0x0a
403
            if mem.tmode == "DTCS":
404
                _mem.tmode = 0x09
405
        if mem.duplex == "n/a":     # Not valid
406
            mem.duplex = ""
407
        _mem.duplex = TMD710_DUPLEX.index(mem.duplex)
408
        _mem.offset = mem.offset
409
        _mem.tstep = TMD710_STEPS.index(mem.tuning_step)
410
        # Set _map.band for this bank. Not Calls!
411
        if mem.number < 1030:
412
            _map.band = 5
413
            val = mem.freq
414
            for mx in range(6):     # Band codes are 0, 5, 6, 7, 8, 9
415
                if val >= TMD710_BANDS[mx][0] and \
416
                        val <= TMD710_BANDS[mx][1]:
417
                    _map.band = mx
418
                    if mx > 0:
419
                        _map.band = mx + 4
420
            _map.skip = TMD710_SKIP.index(mem.skip)
421
        # Only 1 mem.extra entry now
422
        for ext in mem.extra:
423
            if ext.get_name() == "splitstep":
424
                val = STEPS_STR.index(str(ext.value))
425
                setattr(_mem, "splitstep", val)
426
            else:
427
                setattr(_mem, ext.get_name(), ext.value)
428
        return
429

    
430
    def get_settings(self):
431
        """Translate the MEM_FORMAT structs into settings in the UI"""
432
        # Define mem struct write-back shortcuts
433
        if self.SHORT == "G":
434
            _bmp = self._memobj.bitmap
435
        _blk1 = self._memobj.block1
436
        _blk1a = self._memobj.block1a
437
        _pmg = self._memobj.pmg     # array[6] of settings
438
        _dtmc = self._memobj.dtmc
439
        _dtmn = self._memobj.dtmn
440
        _com = self._memobj.mcpcom
441
        _skyc = self._memobj.skycmd
442
        basic = RadioSettingGroup("basic", "Basic")
443
        disp = RadioSettingGroup("disp", "PM0: Display")    # PM[0] settings
444
        aud = RadioSettingGroup("aud", "PM0: Audio")
445
        aux = RadioSettingGroup("aux", "PM0: Aux")
446
        txrx = RadioSettingGroup("txrc", "PM0: Transmit/Receive")
447
        memz = RadioSettingGroup("memz", "PM0: Memory")
448
        pfk = RadioSettingGroup("pfk", "PM0: PF Keys")
449
        pvfo = RadioSettingGroup("pvfo", "PM0: Programmable VFO")
450
        bmsk = RadioSettingGroup("bmsk", "PM0: Band Masks")    # end PM[0]
451
        rptr = RadioSettingGroup("rptr", "Repeater")
452
        dtmf = RadioSettingGroup("dtmf", "DTMF")
453
        skyk = RadioSettingGroup("skyk", "Sky Command")
454
        pmm = RadioSettingGroup("pmm", "PM Groups 1-5(Partial)")
455
        group = RadioSettings(basic, disp, aud, aux, txrx, memz, pvfo, pfk,
456
                              bmsk, rptr, dtmf, skyk, pmm)
457

    
458
        mhz1 = 1000000.   # Raw freq is stored with 0.1 Hz resolution
459

    
460
        def _adjraw(setting, obj, atrb, fix=0, ndx=-1):
461
            """Callback for Integer add or subtract fix from value."""
462
            vx = int(str(setting.value))
463
            value = vx + int(fix)
464
            if value < 0:
465
                value = 0
466
            if ndx < 0:
467
                setattr(obj, atrb, value)
468
            else:
469
                setattr(obj[ndx], atrb, value)
470
            return
471

    
472
        def _mhz_val(setting, obj, atrb, ndx=-1, ndy=-1):
473
            """ Callback to set freq back to Hz """
474
            vx = float(str(setting.value))
475
            vx = int(vx * mhz1)
476
            if ndx < 0:
477
                setattr(obj, atrb, vx)
478
            else:
479
                if atrb[0:7] == "progvfo":      # 2-deep
480
                    stx = atrb.split(".")
481
                    setattr(obj[ndx].progvfo[ndy], stx[1], vx)
482
                else:
483
                    setattr(obj[ndx], atrb, vx)
484
            return
485

    
486
        def _char_to_str(chrx):
487
            """ Remove ff pads from char array """
488
            #  chrx is char array
489
            str1 = ""
490
            for sx in chrx:
491
                if int(sx) > 31 and int(sx) < 127:
492
                    str1 += chr(int(sx))
493
            return str1
494

    
495
        def _pswd_vfy(setting, obj, atrb):
496
            """ Verify password is 1-6 chars, numbers 1-5 """
497
            str1 = str(setting.value).strip()   # initial
498
            str2 = ''.join(filter(lambda c: c in '12345', str1))  # valid chars
499
            if str1 != str2:
500
                # Two lines due to python 73 char limit
501
                sx = "Bad characters in Password"
502
                raise errors.RadioError(sx)
503
            str2 = str1.ljust(6, chr(255))      # pad to 6 with ff's
504
            setattr(obj, atrb, str2)
505
            return
506

    
507
        def _pad_str(setting, lenstr, padchr, obj, atrb, ndx=-1):
508
            """ pad string to lenstr with padchr  """
509
            str1 = str(setting.value).strip()      # initial string
510
            str2 = str1.ljust(lenstr, padchr)
511
            if ndx < 0:
512
                setattr(obj, atrb, str2)
513
            else:
514
                setattr(obj[ndx], atrb, str2)
515
            return
516

    
517
        # ===== BASIC GROUP =====
518
        sx = _char_to_str(_com.comnt)
519
        rx = RadioSettingValueString(0, 32, sx)
520
        sx = "Comment"
521
        rset = RadioSetting("mcpcom.comnt", sx, rx)
522
        basic.append(rset)
523

    
524
        rx = RadioSettingValueInteger(0, 5, _blk1.pmrecall)
525
        sx = "Current PM Select"
526
        rset = RadioSetting("block1.pmrecall", sx, rx)
527
        basic.append(rset)
528

    
529
        rx = RadioSettingValueBoolean(bool(_blk1.pwdon))
530
        sx = "Password"
531
        rset = RadioSetting("block1.pwdon", sx, rx)
532
        basic.append(rset)
533

    
534
        sx = _char_to_str(_blk1.pswd).strip()
535
        rx = RadioSettingValueString(0, 6, sx)
536
        # rx.set_charset("12345")   # Keeps finding `'
537
        sx = "-   Password (numerals 1-5)"
538
        rset = RadioSetting("block1.pswd", sx, rx)
539
        rset.set_apply_callback(_pswd_vfy, _blk1, "pswd")
540
        basic.append(rset)
541

    
542
        # ===== PM0 (off) DISPLAY GROUP =====
543
        rx = RadioSettingValueString(0, 8, _char_to_str(_pmg[0].pwron))
544
        sx = "Power-On message"
545
        rset = RadioSetting("pmg/0.pwron", sx, rx)
546
        disp.append(rset)
547

    
548
        if self.SHORT == "G":         # TMD-710G
549
            rx = RadioSettingValueBoolean(bool(_bmp.bmpon))
550
            sx = "PM0: Custom display bitmap"
551
            rset = RadioSetting("bitmap.bmpon", sx, rx)
552
            disp.append(rset)
553

    
554
            rx = RadioSettingValueString(0, 64, _char_to_str(_bmp.bmpfyl))
555
            rx.set_mutable(False)
556
            sx = "-   Custom bitmap filename"
557
            rset = RadioSetting("bitmap.bmpfyl", sx, rx)
558
            rset.set_doc("Read-only: To modify, use MCP-6 s/w")
559
            disp.append(rset)
560

    
561
        opts = ["VFO", "Mem Recall"]
562
        rx = RadioSettingValueList(opts, opts[_pmg[0].a_mr])
563
        sx = "A: Left Side VFO/MR"
564
        rset = RadioSetting("pmg/0.a_mr", sx, rx)
565
        rset.set_apply_callback(_val_list, opts, _pmg[0], "a_mr")
566
        disp.append(rset)
567

    
568
        rx = RadioSettingValueInteger(0, 999, _pmg[0].a_chn)
569
        sx = "A: Left Side MR Channel"
570
        rset = RadioSetting("pmg/0.a_chn", sx, rx)
571
        disp.append(rset)
572

    
573
        rx = RadioSettingValueList(opts, opts[_pmg[0].b_mr])
574
        sx = "B: Right Side VFO/MR"
575
        rset = RadioSetting("pmg/0.b_mr", sx, rx)
576
        rset.set_apply_callback(_val_list, opts, _pmg[0], "b_mr")
577
        disp.append(rset)
578

    
579
        rx = RadioSettingValueInteger(0, 999, _pmg[0].b_chn)
580
        sx = "B: Right Side MR Channel"
581
        rset = RadioSetting("pmg/0.b_chn", sx, rx)
582
        disp.append(rset)
583

    
584
        rx = RadioSettingValueInteger(0, 8, _pmg[0].bright)
585
        sx = "Brightness level"
586
        rset = RadioSetting("pmg/0.bright", sx, rx)
587
        disp.append(rset)
588

    
589
        opts = ["Amber", "Green"]
590
        rx = RadioSettingValueList(opts, opts[_pmg[0].bkltclr])
591
        sx = "Backlight color"
592
        rset = RadioSetting("pmg/0.bkltclr", sx, rx)
593
        rset.set_apply_callback(_val_list, opts, _pmg[0], "bkltclr")
594
        disp.append(rset)
595

    
596
        val = _pmg[0].bkltcont + 1
597
        rx = RadioSettingValueInteger(1, 16, val)
598
        sx = "Contrast level"
599
        rset = RadioSetting("pmg/0.bkltcont", sx, rx)
600
        rset.set_apply_callback(_adjraw, _pmg[0], "bkltcont", -1)
601
        disp.append(rset)
602

    
603
        opts = ["Positive", "Negative"]
604
        rx = RadioSettingValueList(opts, opts[_pmg[0].dsprev])
605
        sx = "Color mode"
606
        rset = RadioSetting("pmg/0.dsprev", sx, rx)
607
        rset.set_apply_callback(_val_list, opts, _pmg[0], "dsprev")
608
        disp.append(rset)
609

    
610
        rx = RadioSettingValueBoolean(bool(_pmg[0].autobri))
611
        sx = "Auto brightness"
612
        rset = RadioSetting("pmg/0.autobri", sx, rx)
613
        disp.append(rset)
614

    
615
        rx = RadioSettingValueBoolean(bool(_pmg[0].dispbar))
616
        sx = "Display partition bar"
617
        rset = RadioSetting("pmg/0.dispbar", sx, rx)
618
        disp.append(rset)
619

    
620
        rx = RadioSettingValueBoolean(bool(_pmg[0].single))
621
        sx = "Single band display"
622
        rset = RadioSetting("pmg/0.single", sx, rx)
623
        disp.append(rset)
624

    
625
        rx = RadioSettingValueBoolean(bool(_pmg[0].autopm))
626
        sx = "Auto PM Store"
627
        rset = RadioSetting("pmg/0.autopm", sx, rx)
628
        disp.append(rset)
629

    
630
        # ===== AUDIO GROUP =====
631
        rx = RadioSettingValueBoolean(bool(_pmg[0].beepon))
632
        sx = "Beep On"
633
        rset = RadioSetting("pmg/0.beepon", sx, rx)
634
        aud.append(rset)
635

    
636
        val = _pmg[0].beepvol + 1     # 1-7 downloads as 0-6
637
        rx = RadioSettingValueInteger(1, 7, val)
638
        sx = "Beep volume (1 - 7)"
639
        rset = RadioSetting("pmg/0.beepvol", sx, rx)
640
        rset.set_apply_callback(_adjraw, _pmg[0], "beepvol", -1)
641
        aud.append(rset)
642

    
643
        opts = ["Mode1", "Mode2"]
644
        rx = RadioSettingValueList(opts, opts[_pmg[0].extspkr])
645
        sx = "External Speaker"
646
        rset = RadioSetting("pmg/0.extspkr", sx, rx)
647
        rset.set_apply_callback(_val_list, opts, _pmg[0], "extspkr")
648
        aud.append(rset)
649

    
650
        rx = RadioSettingValueBoolean(bool(_pmg[0].pbkrpt))
651
        sx = "VGS Plugin: Playback repeat"
652
        rset = RadioSetting("pmg/0.pbkrpt", sx, rx)
653
        aud.append(rset)
654

    
655
        rx = RadioSettingValueInteger(0, 60, _pmg[0].pbkint)
656
        sx = "     Playback repeat interval (0 - 60 secs)"
657
        rset = RadioSetting("pmg/0.pbkint", sx, rx)
658
        aud.append(rset)
659

    
660
        rx = RadioSettingValueBoolean(bool(_pmg[0].cntrec))
661
        sx = "     Continuous recording"
662
        rset = RadioSetting("pmg/0.cntrec", sx, rx)
663
        aud.append(rset)
664

    
665
        opts = ["Off", "Auto", "Manual"]
666
        rx = RadioSettingValueList(opts, opts[_pmg[0].ance])
667
        sx = "     Announce mode"
668
        rset = RadioSetting("pmg/0.ance", sx, rx)
669
        rset.set_apply_callback(_val_list, opts, _pmg[0], "ance")
670
        aud.append(rset)
671

    
672
        opts = ["English", "Japanese"]
673
        rx = RadioSettingValueList(opts, opts[_pmg[0].lang])
674
        sx = "     Announce language"
675
        rset = RadioSetting("pmg/0.lang", sx, rx)
676
        rset.set_apply_callback(_val_list, opts, _pmg[0], "lang")
677
        aud.append(rset)
678

    
679
        rx = RadioSettingValueInteger(1, 7, _pmg[0].vcvol + 1)
680
        sx = "     Voice volume (1 - 7)"
681
        rset = RadioSetting("pmg/0.vcvol", sx, rx)
682
        rset.set_apply_callback(_adjraw, _pmg[0], "vcvol", -1)
683
        aud.append(rset)
684

    
685
        rx = RadioSettingValueInteger(0, 4, _pmg[0].vcspd)
686
        sx = "     Voice speed (0 - 4)"
687
        rset = RadioSetting("pmg/0.vcspd", sx, rx)
688
        aud.append(rset)
689

    
690
        # ===== AUX GROUP =====
691
        opts = ["9600", "19200", "38400", "57600"]
692
        rx = RadioSettingValueList(opts, opts[_blk1.pcbaud])
693
        sx = "PC port baud rate"
694
        rset = RadioSetting("block1.pcbaud", sx, rx)
695
        rset.set_apply_callback(_val_list, opts, _blk1, "pcbaud")
696
        aux.append(rset)
697

    
698
        opts = ["A-Band", "B-Band", "TX-A / RX-B", "RX-A / TX-B"]
699
        rx = RadioSettingValueList(opts, opts[_pmg[0].intband])
700
        sx = "Internal TNC band"
701
        rset = RadioSetting("pmg/0.intband", sx, rx)
702
        rset.set_apply_callback(_val_list, opts, _pmg[0], "intband")
703
        aux.append(rset)
704

    
705
        opts = ["A-Band", "B-Band", "TX-A / RX-B", "RX-A / TX-B"]
706
        rx = RadioSettingValueList(opts, opts[_pmg[0].extband])
707
        sx = "External TNC band"
708
        rset = RadioSetting("pmg/0.extband", sx, rx)
709
        rset.set_apply_callback(_val_list, opts, _pmg[0], "extband")
710
        aux.append(rset)
711

    
712
        opts = ["1200", "9600"]
713
        rx = RadioSettingValueList(opts, opts[_pmg[0].extbaud])
714
        sx = "External TNC baud"
715
        rset = RadioSetting("pmg/0.extbaud", sx, rx)
716
        rset.set_apply_callback(_val_list, opts, _pmg[0], "extbaud")
717
        aux.append(rset)
718

    
719
        opts = ["Off", "BUSY", "SQL", "TX", "BUSY/TX", "SQL/TX"]
720
        rx = RadioSettingValueList(opts, opts[_pmg[0].sqcsrc])
721
        sx = "SQC output source"
722
        rset = RadioSetting("pmg/0.sqcsrc", sx, rx)
723
        rset.set_apply_callback(_val_list, opts, _pmg[0], "sqcsrc")
724
        aux.append(rset)
725

    
726
        opts = ["Low", "High"]
727
        rx = RadioSettingValueList(opts, opts[_pmg[0].sqclogic])
728
        sx = "SQC logic"
729
        rset = RadioSetting("pmg/0.sqclogic", sx, rx)
730
        rset.set_apply_callback(_val_list, opts, _pmg[0], "sqclogic")
731
        aux.append(rset)
732

    
733
        opts = ["Off", "30", "60", "90", "120", "180"]
734
        rx = RadioSettingValueList(opts, opts[_pmg[0].apo])
735
        sx = "APO: Auto Power Off (Mins)"
736
        rset = RadioSetting("pmg/0.apo", sx, rx)
737
        rset.set_apply_callback(_val_list, opts, _pmg[0], "apo")
738
        aux.append(rset)
739

    
740
        opts = ["Time Operate (TO)", "Carrier Operate (CO)", "Seek"]
741
        rx = RadioSettingValueList(opts, opts[_pmg[0].scnrsm])
742
        sx = "Scan resume mode"
743
        rset = RadioSetting("pmg/0.scnrsm", sx, rx)
744
        rset.set_apply_callback(_val_list, opts, _pmg[0], "scnrsm")
745
        aux.append(rset)
746

    
747
        rx = RadioSettingValueInteger(1, 10, _pmg[0].scntot + 1)
748
        sx = "   Scan TO delay (Secs)"
749
        rset = RadioSetting("pmg/0.scntot", sx, rx)
750
        rset.set_apply_callback(_adjraw, _pmg[0], "scntot", -1)
751
        aux.append(rset)
752

    
753
        rx = RadioSettingValueInteger(1, 10, _pmg[0].scncot + 1)
754
        sx = "   Scan CO delay (Secs)"
755
        rset = RadioSetting("pmg/0.scncot", sx, rx)
756
        rset.set_apply_callback(_adjraw, _pmg[0], "scncot", -1)
757
        aux.append(rset)
758

    
759
        opts = ["Mode 1: 1ch", "Mode 2: 61ch", "Mode 3: 91ch",
760
                "Mode 4: 181ch"]
761
        rx = RadioSettingValueList(opts, opts[_pmg[0].vsmode])
762
        sx = "Visual scan"
763
        rset = RadioSetting("pmg/0.vsmode", sx, rx)
764
        rset.set_apply_callback(_val_list, opts, _pmg[0], "vsmode")
765
        aux.append(rset)
766

    
767
        rx = RadioSettingValueBoolean(bool(_blk1.m10mz))
768
        sx = "10 MHz mode"
769
        rset = RadioSetting("block1.m10mz", sx, rx)
770
        aux.append(rset)
771

    
772
        rx = RadioSettingValueBoolean(bool(_blk1.ansbck))
773
        sx = "Remote control answerback"
774
        rset = RadioSetting("block1.ansbck", sx, rx)
775
        aux.append(rset)
776

    
777
        # ===== TX / RX Group =========
778
        opts = ["A: Left", "B: Right"]
779
        rx = RadioSettingValueList(opts, opts[_pmg[0].txband])
780
        sx = "TX Side (PTT)"
781
        rset = RadioSetting("pmg/0.txband", sx, rx)
782
        rset.set_apply_callback(_val_list, opts, _pmg[0], "txband")
783
        txrx.append(rset)
784

    
785
        opts = ["High (50W)", "Medium (10W)", "Low (5W)"]
786
        rx = RadioSettingValueList(opts, opts[_pmg[0].a_pwr])
787
        sx = "A-Band transmit power"
788
        rset = RadioSetting("pmg/0.a_pwr", sx, rx)
789
        rset.set_apply_callback(_val_list, opts, _pmg[0], "a_pwr")
790
        txrx.append(rset)
791

    
792
        rx = RadioSettingValueList(opts, opts[_pmg[0].b_pwr])
793
        sx = "B-Band transmit power"
794
        rset = RadioSetting("pmg/0.b_pwr", sx, rx)
795
        rset.set_apply_callback(_val_list, opts, _pmg[0], "b_pwr")
796
        txrx.append(rset)
797

    
798
        opts = ["Off", "125", "250", "500", "750", "1000"]
799
        rx = RadioSettingValueList(opts, opts[_pmg[0].mutehu])
800
        sx = "Rx Mute hangup time (ms)"
801
        rset = RadioSetting("pmg/0.mutehu", sx, rx)
802
        rset.set_apply_callback(_val_list, opts, _pmg[0], "mutehu")
803
        txrx.append(rset)
804

    
805
        opts = ["Off", "125", "250", "500"]
806
        rx = RadioSettingValueList(opts, opts[_pmg[0].ssqlhu])
807
        sx = "S-meter SQL hangup time (ms)"
808
        rset = RadioSetting("pmg/0.ssqlhu", sx, rx)
809
        rset.set_apply_callback(_val_list, opts, _pmg[0], "ssqlhu")
810
        txrx.append(rset)
811

    
812
        rx = RadioSettingValueBoolean(bool(_pmg[0].beatshft))
813
        sx = "Beat shift"
814
        rset = RadioSetting("pmg/0.beatshft", sx, rx)
815
        txrx.append(rset)
816

    
817
        rx = RadioSettingValueBoolean(bool(_pmg[0].asmsql))
818
        sx = "A-Band S-meter SQL"
819
        rset = RadioSetting("pmg/0.asmsql", sx, rx)
820
        txrx.append(rset)
821

    
822
        rx = RadioSettingValueBoolean(bool(_pmg[0].bsmsql))
823
        sx = "B-Band S-meter SQL"
824
        rset = RadioSetting("pmg/0.bsmsql", sx, rx)
825
        txrx.append(rset)
826

    
827
        rx = RadioSettingValueBoolean(bool(_pmg[0].vhfaip))
828
        sx = "VHF band AIP"
829
        rset = RadioSetting("pmg/0.vhfaip", sx, rx)
830
        txrx.append(rset)
831

    
832
        rx = RadioSettingValueBoolean(bool(_pmg[0].uhfaip))
833
        sx = "UHF band AIP"
834
        rset = RadioSetting("pmg/0.uhfaip", sx, rx)
835
        txrx.append(rset)
836

    
837
        opts = ["High", "Medium", "Low"]
838
        rx = RadioSettingValueList(opts, opts[_blk1.micsens])
839
        sx = "Microphone sensitivity (gain)"
840
        rset = RadioSetting("block1.micsens", sx, rx)
841
        txrx.append(rset)
842

    
843
        opts = ["3", "5", "10"]
844
        rx = RadioSettingValueList(opts, opts[_pmg[0].tot])
845
        sx = "Time-Out timer (Mins)"
846
        rset = RadioSetting("pmg/0.tot", sx, rx)
847
        #  rset.set_apply_callback(_val_list, opts, _pmg[0], "tot")
848
        txrx.append(rset)
849

    
850
        rx = RadioSettingValueBoolean(bool(_pmg[0].wxalerta))
851
        sx = "WX Alert A-band"
852
        rset = RadioSetting("pmg/0.wxalerta", sx, rx)
853
        txrx.append(rset)
854

    
855
        rx = RadioSettingValueBoolean(bool(_pmg[0].wxalertb))
856
        sx = "WX Alert B-band"
857
        rset = RadioSetting("pmg/0.wxalertb", sx, rx)
858
        txrx.append(rset)
859

    
860
        opts = ["Off", "15", "30", "60"]
861
        rx = RadioSettingValueList(opts, opts[_pmg[0].wxscntm])
862
        sx = "WX alert scan memory time (Mins)"
863
        rset = RadioSetting("pmg/0.wxscntm", sx, rx)
864
        rset.set_apply_callback(_val_list, opts, _pmg[0], "wxscntm")
865
        txrx.append(rset)
866

    
867
        # ===== DTMF GROUP =====
868
        rx = RadioSettingValueBoolean(bool(_pmg[0].dtmfhld))
869
        sx = "DTMF hold"
870
        rset = RadioSetting("pmg/0.dtmfhld", sx, rx)
871
        dtmf.append(rset)
872

    
873
        opts = ["100", "250", "500", "750", "1000", "1500", "2000"]
874
        rx = RadioSettingValueList(opts, opts[_pmg[0].dtmfpau])
875
        sx = "DTMF pause duration (mS)"
876
        rset = RadioSetting("pmg/0.dtmfpau", sx, rx)
877
        rset.set_apply_callback(_val_list, opts, _pmg[0], "dtmfpau")
878
        dtmf.append(rset)
879

    
880
        opts = ["Fast", "Slow"]
881
        rx = RadioSettingValueList(opts, opts[_pmg[0].dtmfspd])
882
        sx = "DTMF speed"
883
        rset = RadioSetting("pmg/0.dtmfspd", sx, rx)
884
        rset.set_apply_callback(_val_list, opts, _pmg[0], "dtmfspd")
885
        dtmf.append(rset)
886

    
887
        for mx in range(0, 10):
888
            csx = _char_to_str(_dtmn[mx].id).strip()
889
            rx = RadioSettingValueString(0, 8, csx)
890
            sx = "DTMF %i Name (8 chars)" % mx
891
            rset = RadioSetting("dtmn.id/%d" % mx, sx, rx)
892
            rset.set_apply_callback(_pad_str, 8, chr(255), _dtmn, "id", mx)
893
            dtmf.append(rset)
894

    
895
            csx = _char_to_str(_dtmc[mx].code).strip()
896
            rx = RadioSettingValueString(0, 16, csx)
897
            sx = "    Code %i (16 chars)" % mx
898
            rset = RadioSetting("dtmc.code/%d" % mx, sx, rx)
899
            rset.set_apply_callback(_pad_str, 16, chr(255), _dtmc, "code", mx)
900
            dtmf.append(rset)
901

    
902
        # ===== MEMORY GROUP =====
903
        opts = ["All Bands", "Current Band"]
904
        rx = RadioSettingValueList(opts, opts[_pmg[0].recall])
905
        sx = "Memory recall method"
906
        rset = RadioSetting("pmg/0.recall", sx, rx)
907
        rset.set_apply_callback(_val_list, opts, _pmg[0], "recall")
908
        memz.append(rset)
909

    
910
        rx = RadioSettingValueString(0, 10, _char_to_str(_pmg[0].memgrplk))
911
        sx = "Group link"
912
        rset = RadioSetting("pmg/0.memgrplk", sx, rx)
913
        memz.append(rset)
914

    
915
        opts = ["Fast", "Slow"]
916
        rx = RadioSettingValueList(opts, opts[_pmg[0].eclnkspd])
917
        sx = "Echolink speed"
918
        rset = RadioSetting("pmg/0.eclnkspd", sx, rx)
919
        rset.set_apply_callback(_val_list, opts, _pmg[0], "eclnkspd")
920
        memz.append(rset)
921

    
922
        rx = RadioSettingValueBoolean(bool(_blk1.dspmemch))
923
        sx = "Display memory channel number"
924
        rset = RadioSetting("block1.dspmemch", sx, rx)
925
        memz.append(rset)
926

    
927
        # ===== REPEATER GROUP =====
928
        rx = RadioSettingValueBoolean(bool(_pmg[0].rptr1750))
929
        sx = "1750 Hz transmit hold"
930
        rset = RadioSetting("pmg/0.rptr1750", sx, rx)
931
        rptr.append(rset)
932

    
933
        rx = RadioSettingValueBoolean(bool(_pmg[0].rptrofst))
934
        sx = "Auto repeater offset"
935
        rset = RadioSetting("pmg/0.rptrofst", sx, rx)
936
        rptr.append(rset)
937

    
938
        opts = ["Cross Band", "TX:A-Band / RX:B-Band", "RX:A-Band / TX:B-Band"]
939
        rx = RadioSettingValueList(opts, opts[_blk1.rptrmode])
940
        sx = "Repeater Mode"
941
        rset = RadioSetting("block1.rptrmode", sx, rx)
942
        rset.set_apply_callback(_val_list, opts, _blk1, "rptrmode")
943
        rptr.append(rset)
944

    
945
        opts = ["Off", "Morse", "Voice"]
946
        rx = RadioSettingValueList(opts, opts[_blk1.rptridx])
947
        sx = "Repeater ID transmit"
948
        rset = RadioSetting("block1.rptridx", sx, rx)
949
        rset.set_apply_callback(_val_list, opts, _blk1, "rptridx")
950
        rptr.append(rset)
951

    
952
        rx = RadioSettingValueString(0, 12, _char_to_str(_blk1a.rptrid))
953
        sx = "Repeater ID"
954
        rset = RadioSetting("block1a.rptrid", sx, rx)
955
        rptr.append(rset)
956

    
957
        rx = RadioSettingValueBoolean(bool(_blk1.rptrhold))
958
        sx = "Repeater transmit hold"
959
        rset = RadioSetting("block1.rptrhold", sx, rx)
960
        rptr.append(rset)
961

    
962
        # ===== Prog VFO Group =============
963
        for mx in range(0, 10):
964
            # Raw freq is 0.1 MHz resolution
965
            vfx = int(_pmg[0].progvfo[mx].blow) / mhz1
966
            if vfx == 0:
967
                vfx = 118
968
            rx = RadioSettingValueFloat(118.0, 1299.9, vfx, 0.005, 3)
969
            sx = "VFO-%i Low Limit (MHz)" % mx
970
            rset = RadioSetting("pmg/0.progvfo/%d.blow" % mx, sx, rx)
971
            rset.set_apply_callback(_mhz_val, _pmg, "progvfo.blow", 0, mx)
972
            pvfo.append(rset)
973

    
974
            vfx = int(_pmg[0].progvfo[mx].bhigh) / mhz1
975
            if vfx == 0:
976
                vfx = 118
977
            rx = RadioSettingValueFloat(118.0, 1300.0, vfx, 0.005, 3)
978
            sx = "   VFO-%i High Limit (MHz)" % mx
979
            rset = RadioSetting("pmg/0.progvfo/%d.bhigh" % mx, sx, rx)
980
            rset.set_apply_callback(_mhz_val, _pmg, "progvfo.bhigh", 0, mx)
981
            pvfo.append(rset)
982

    
983
        # ===== PFK GROUP =====
984
        opts = ["WX CH", "FRQ.BAND", "CTRL", "MONITOR", "VGS", "VOICE",
985
                "GROUP UP", "MENU", "MUTE", "SHIFT", "DUAL", "M>V",
986
                "1750 Tone"]
987
        rx = RadioSettingValueList(opts, opts[_pmg[0].pf1key])
988
        sx = "Front panel PF1 key"
989
        rset = RadioSetting("pmg/0.pf1key", sx, rx)
990
        rset.set_apply_callback(_val_list, opts, _pmg[0], "pf1key")
991
        pfk.append(rset)
992

    
993
        rx = RadioSettingValueList(opts, opts[_pmg[0].pf2key])
994
        sx = "Front panel PF2 key"
995
        rset = RadioSetting("pmg/0.pf2key", sx, rx)
996
        rset.set_apply_callback(_val_list, opts, _pmg[0], "pf2key")
997
        pfk.append(rset)
998

    
999
        opts = ["WX CH", "FRQ.BAND", "CTRL", "MONITOR", "VGS", "VOICE",
1000
                "GROUP UP", "MENU", "MUTE", "SHIFT", "DUAL", "M>V",
1001
                "VFO", "MR", "CALL", "MHz", "TONE", "REV", "LOW",
1002
                "LOCK", "A/B", "ENTER", "1750 Tone", "M.LIST",
1003
                "S.LIST", "MSG.NEW", "REPLY", "POS", "P.MONI",
1004
                "BEACON", "DX", "WX"]
1005
        rx = RadioSettingValueList(opts, opts[_pmg[0].micpf1])
1006
        sx = "Microphone PF1 key"
1007
        rset = RadioSetting("pmg/0.micpf1", sx, rx)
1008
        rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf1")
1009
        pfk.append(rset)
1010

    
1011
        rx = RadioSettingValueList(opts, opts[_pmg[0].micpf2])
1012
        sx = "Microphone PF2 key"
1013
        rset = RadioSetting("pmg/0.micpf2", sx, rx)
1014
        rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf2")
1015
        pfk.append(rset)
1016

    
1017
        rx = RadioSettingValueList(opts, opts[_pmg[0].micpf3])
1018
        sx = "Microphone PF3 key"
1019
        rset = RadioSetting("pmg/0.micpf3", sx, rx)
1020
        rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf3")
1021
        pfk.append(rset)
1022

    
1023
        rx = RadioSettingValueList(opts, opts[_pmg[0].micpf4])
1024
        sx = "Microphone PF4 key"
1025
        rset = RadioSetting("pmg/0.micpf4", sx, rx)
1026
        rset.set_apply_callback(_val_list, opts, _pmg[0], "micpf4")
1027
        pfk.append(rset)
1028

    
1029
        # ===== BMSK GROUP =====
1030
        rx = RadioSettingValueBoolean(bool(_pmg[0].abnd118))
1031
        sx = "A/Left: 118 MHz Band"
1032
        rset = RadioSetting("pmg/0.abnd118", sx, rx)
1033
        bmsk.append(rset)
1034

    
1035
        rx = RadioSettingValueBoolean(bool(_pmg[0].abnd144))
1036
        sx = "A/Left: 144 MHz Band"
1037
        rset = RadioSetting("pmg/0.abnd144", sx, rx)
1038
        bmsk.append(rset)
1039

    
1040
        rx = RadioSettingValueBoolean(bool(_pmg[0].abnd220))
1041
        sx = "A/Left: 220 MHz Band"
1042
        rset = RadioSetting("pmg/0.abnd220", sx, rx)
1043
        bmsk.append(rset)
1044

    
1045
        rx = RadioSettingValueBoolean(bool(_pmg[0].abnd300))
1046
        sx = "A/Left: 300 MHz Band"
1047
        rset = RadioSetting("pmg/0.abnd300", sx, rx)
1048
        bmsk.append(rset)
1049

    
1050
        rx = RadioSettingValueBoolean(bool(_pmg[0].abnd430))
1051
        sx = "A/Left: 430 MHz Band"
1052
        rset = RadioSetting("pmg/0.abnd430", sx, rx)
1053
        bmsk.append(rset)
1054

    
1055
        rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd144))
1056
        sx = "B/Right: 144 MHz Band"
1057
        rset = RadioSetting("pmg/0.bbnd144", sx, rx)
1058
        bmsk.append(rset)
1059

    
1060
        rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd220))
1061
        sx = "B/Right: 220 MHz Band"
1062
        rset = RadioSetting("pmg/0.bbnd220", sx, rx)
1063
        bmsk.append(rset)
1064

    
1065
        rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd300))
1066
        sx = "B/Right: 300 MHz Band"
1067
        rset = RadioSetting("pmg/0.bbnd300", sx, rx)
1068
        bmsk.append(rset)
1069

    
1070
        rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd430))
1071
        sx = "B/Right: 430 MHz Band"
1072
        rset = RadioSetting("pmg/0.bbnd430", sx, rx)
1073
        bmsk.append(rset)
1074

    
1075
        rx = RadioSettingValueBoolean(bool(_pmg[0].bbnd800))
1076
        sx = "B/Right: 800 MHz Band"
1077
        rset = RadioSetting("pmg/0.bbnd800", sx, rx)
1078
        bmsk.append(rset)
1079

    
1080
        # ===== Sky command Group =============
1081
        rx = RadioSettingValueString(0, 10, _char_to_str(_skyc.cmdr))
1082
        sx = "Commandr call sign"
1083
        rset = RadioSetting("skycmd.cmdr", sx, rx)
1084
        rset.set_apply_callback(_pad_str, 10, chr(0), _skyc, "cmdr")
1085
        skyk.append(rset)
1086

    
1087
        rx = RadioSettingValueString(0, 10, _char_to_str(_skyc.tptr))
1088
        sx = "Transporter call sign"
1089
        rset = RadioSetting("skycmd.tptr", sx, rx)
1090
        rset.set_apply_callback(_pad_str, 10, chr(0), _skyc, "tptr")
1091
        skyk.append(rset)
1092

    
1093
        opts = []
1094
        for val in TMD710_TONES:
1095
            opts.append(str(val))
1096
        rx = RadioSettingValueList(opts, opts[_skyc.skytone])
1097
        sx = "Tone frequency"
1098
        rset = RadioSetting("skycmd.skytone", sx, rx)
1099
        rset.set_apply_callback(_val_list, opts, _skyc, "skytone")
1100
        skyk.append(rset)
1101

    
1102
        # ===== PM MEMORY GROUP =====
1103
        """ These 5 blocks of 512 bytes are repeats of the major settings """
1104
        # Only showing limited settings for now...
1105
        _pmn = self._memobj.pm_name
1106
        for ix in range(1, 6):
1107
            nx = ix - 1          # Names are [0-4]
1108
            rx = RadioSettingValueString(0, 16, _char_to_str(_pmn[nx].pmname))
1109
            sx = "PM Group %i Name" % ix
1110
            rset = RadioSetting("pm_name/%i.pmname" % nx, sx, rx)
1111
            rset.set_apply_callback(_pad_str, 16, chr(0xff), _pmn,
1112
                                    "pmname", nx)
1113
            pmm.append(rset)
1114

    
1115
            rx = RadioSettingValueString(0, 8, _char_to_str(_pmg[ix].pwron))
1116
            sx = "-   Power-On Message"
1117
            rset = RadioSetting("pmg/%i.pwron" % ix, sx, rx)
1118
            rset.set_apply_callback(_pad_str, 8, chr(0xff), _pmg, "pwron", ix)
1119
            pmm.append(rset)
1120

    
1121
            opts = ["VFO", "Mem Recall"]
1122
            rx = RadioSettingValueList(opts, opts[_pmg[ix].a_mr])
1123
            sx = "-   A: Left Side VFO/MR"
1124
            rset = RadioSetting("pmg/%i.a_mr" % ix, sx, rx)
1125
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "a_mr")
1126
            pmm.append(rset)
1127

    
1128
            rx = RadioSettingValueInteger(0, 999, _pmg[ix].a_chn)
1129
            sx = "-   A: Left Side MR Channel"
1130
            rset = RadioSetting("pmg/%i.a_chn" % ix, sx, rx)
1131
            pmm.append(rset)
1132

    
1133
            rx = RadioSettingValueList(opts, opts[_pmg[ix].b_mr])
1134
            sx = "-   B: Right Side VFO/MR"
1135
            rset = RadioSetting("pmg/%i.b_mr" % ix, sx, rx)
1136
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "b_mr")
1137
            pmm.append(rset)
1138

    
1139
            rx = RadioSettingValueInteger(0, 999, _pmg[ix].b_chn)
1140
            sx = "-   B: Right Side MR Channel"
1141
            rset = RadioSetting("pmg/%i.b_chn" % ix, sx, rx)
1142
            pmm.append(rset)
1143

    
1144
            rx = RadioSettingValueInteger(0, 8, _pmg[ix].bright)
1145
            sx = "-   Brightness level"
1146
            rset = RadioSetting("pmg/%i.bright" % ix, sx, rx)
1147
            pmm.append(rset)
1148

    
1149
            opts = ["Amber", "Green"]
1150
            rx = RadioSettingValueList(opts, opts[_pmg[ix].bkltclr])
1151
            sx = "-   Backlight color"
1152
            rset = RadioSetting("pmg/%i.bkltclr" % ix, sx, rx)
1153
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "bkltclr")
1154
            pmm.append(rset)
1155

    
1156
            val = _pmg[ix].bkltcont + 1
1157
            rx = RadioSettingValueInteger(1, 16, val)
1158
            sx = "-   Contrast level"
1159
            rset = RadioSetting("pmg/%i.bkltcont" % ix, sx, rx)
1160
            rset.set_apply_callback(_adjraw, _pmg[ix], "bkltcont", -1)
1161
            pmm.append(rset)
1162

    
1163
            opts = ["Positive", "Negative"]
1164
            rx = RadioSettingValueList(opts, opts[_pmg[ix].dsprev])
1165
            sx = "-   Color mode"
1166
            rset = RadioSetting("pmg/%i.dsprev" % ix, sx, rx)
1167
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "dsprev")
1168
            pmm.append(rset)
1169

    
1170
            rx = RadioSettingValueBoolean(bool(_pmg[ix].beepon))
1171
            sx = "-   Beep On"
1172
            rset = RadioSetting("pmg/%i.beepon" % ix, sx, rx)
1173
            pmm.append(rset)
1174

    
1175
            val = _pmg[ix].beepvol + 1     # 1-7 downloads as 0-6
1176
            rx = RadioSettingValueInteger(1, 7, val)
1177
            sx = "-   Beep volume (1 - 7)"
1178
            rset = RadioSetting("pmg/%i.beepvol" % ix, sx, rx)
1179
            rset.set_apply_callback(_adjraw, _pmg[ix], "beepvol", -1)
1180
            pmm.append(rset)
1181

    
1182
            rx = RadioSettingValueBoolean(bool(_pmg[ix].autopm))
1183
            sx = "-   Auto PM Store"
1184
            rset = RadioSetting("pmg/%i.autopm" % ix, sx, rx)
1185
            pmm.append(rset)
1186

    
1187
            opts = ["A: Left", "B: Right"]
1188
            rx = RadioSettingValueList(opts, opts[_pmg[ix].txband])
1189
            sx = "-   X Side (PTT)"
1190
            rset = RadioSetting("pmg/%i.txband" % ix, sx, rx)
1191
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "txband")
1192
            pmm.append(rset)
1193

    
1194
            opts = ["High (50W)", "Medium (10W)", "Low (5W)"]
1195
            rx = RadioSettingValueList(opts, opts[_pmg[ix].a_pwr])
1196
            sx = "-   A-Band transmit power"
1197
            rset = RadioSetting("pmg/%i.a_pwr" % ix, sx, rx)
1198
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "a_pwr")
1199
            pmm.append(rset)
1200

    
1201
            rx = RadioSettingValueList(opts, opts[_pmg[ix].b_pwr])
1202
            sx = "-   B-Band transmit power"
1203
            rset = RadioSetting("pmg/%i.b_pwr" % ix, sx, rx)
1204
            rset.set_apply_callback(_val_list, opts, _pmg[ix], "b_pwr")
1205
            pmm.append(rset)
1206

    
1207
        return group       # END get_settings()
1208

    
1209
    def set_settings(self, settings):
1210
        """ Convert UI modified changes into mem_format values """
1211
        blks = (self._memobj.block1, self._memobj.block1a,
1212
                self._memobj.pmg, self._memobj.pm_name)
1213
        for _settings in blks:
1214
            for element in settings:
1215
                if not isinstance(element, RadioSetting):
1216
                    self.set_settings(element)
1217
                    continue
1218
                else:
1219
                    try:
1220
                        name = element.get_name()
1221
                        if "." in name:
1222
                            bits = name.split(".")
1223
                            obj = self._memobj
1224
                            for bit in bits[:-1]:
1225
                                if "/" in bit:
1226
                                    bit, index = bit.split("/", 1)
1227
                                    index = int(index)
1228
                                    obj = getattr(obj, bit)[index]
1229
                                else:
1230
                                    obj = getattr(obj, bit)
1231
                            setting = bits[-1]
1232
                        else:
1233
                            obj = _settings
1234
                            setting = element.get_name()
1235

    
1236
                        if element.has_apply_callback():
1237
                            LOG.debug("Using apply callback")
1238
                            element.run_apply_callback()
1239
                        elif element.value.get_mutable():
1240
                            LOG.debug("Setting %s = %s"
1241
                                      % (setting, element.value))
1242
                            setattr(obj, setting, element.value)
1243
                    except Exception:
1244
                        LOG.debug(element.get_name())
1245
                        raise
1246
        return
1247

    
1248
    @classmethod
1249
    def match_model(cls, fdata, fyle):
1250
        """ Included to prevent 'File > New' error """
1251
        return False
1252

    
1253

    
1254
if HAS_FUTURE:   # Only register drivers if environment is PY3 compliant
1255
    @directory.register
1256
    class KenwoodTMD710Radio(KenwoodTMx710Radio):
1257
        """ Kenwood TM-D710 VHF/UHF/APRS Radio model. """
1258
        VENDOR = "Kenwood"
1259
        MODEL = "TM-D710_CloneMode"
1260
        SHORT = ""       # Quick model code
1261

    
1262
        _num_blocks = 3
1263
        _num_packets = [0x9c, 1, 1]
1264

    
1265
        MEM_FORMAT = """
1266
        struct chns {            // 16 bytes channel structure
1267
        ul32  rxfreq;
1268
        u8   tstep;
1269
        u8   mode;
1270
        u8   tmode:4,
1271
            duplex:4;         // 4 = split
1272
        u8   rtone;
1273
        u8   ctone;
1274
        u8   dtcs;
1275
        ul32 offset;          // or Split mode TX freq
1276
        u8   splitstep;
1277
        u8   cross;           // not used
1278
        };
1279

    
1280
        struct pm_grp {         // 512 bytes per group
1281
        u8   unk0200;
1282
        u8   a_mr;
1283
        u8   unk0202;
1284
        u8   unk0203;
1285
        u8   unk0204;
1286
        u8   unk0205;
1287
        u8   unk0206;
1288
        u8   a_pwr;
1289
        u8   wxalerta;
1290
        u8   asmsql;
1291
        u8   a_chn;
1292
        u8   unk020b;
1293
        u8   unk020c;
1294
        u8   b_mr;
1295
        u8   unk020e;
1296
        u8   unk020f;
1297
        u8   unk0210;
1298
        u8   unk0211;
1299
        u8   unk0212;
1300
        u8   b_pwr;
1301
        u8   wxalertb;
1302
        u8   bsmsql;
1303
        u8   b_chn;
1304
        u8   unk0217;
1305
        u8   unk0218;
1306
        u8   unk0219;
1307
        u8   unk021a;
1308
        u8   unk021b;
1309
        u8   unk021c;
1310
        u8   unk021d;
1311
        u8   unk021e;
1312
        u8   unk021f;
1313
        u8   unk0220;
1314
        u8   unk0221;
1315
        u8   unk0222;
1316
        u8   unk0223;
1317
        u8   unk0224;
1318
        u8   unk0225;
1319
        u8   unk0226;
1320
        u8   unk0227;
1321
        u8   unk0228;
1322
        u8   unk0229;
1323
        u8   unk022a;
1324
        u8   unk022b;
1325
        u8   unk022c;
1326
        u8   unk022d;
1327
        u8   unk022e;
1328
        u8   unk022f;
1329
        u8   unk0230;
1330
        u8   unk0231;
1331
        u8   sqclogic;
1332
        u8   txband;
1333
        u8   single;
1334
        u8   unk0235;
1335
        u8   mute;
1336
        u8   unk0237;
1337
        u8   unk0238;
1338
        u8   unk0239;
1339
        u8   unk0237a;
1340
        u8   unk023b;
1341
        u8   unk023c;
1342
        u8   unk023d;
1343
        u8   unk023e;
1344
        u8   unk023f;
1345
        struct chns vfo[10];         // 0x0240 - 0x02df
1346
        char pwron[8];
1347
        u8   unk02e8;
1348
        u8   unk02e9;
1349
        u8   unk02ea;
1350
        u8   unk02eb;
1351
        u8   unk02ec;
1352
        u8   unk02ed;
1353
        u8   unk02ee;
1354
        u8   unk02ef;
1355
        char memgrplk[10];
1356
        u8   unk02fa;
1357
        u8   unk02fb;
1358
        u8   unk02fc;
1359
        u8   unk02fd;
1360
        u8   unk02fe;
1361
        u8   unk02ff;
1362
        struct {
1363
            ul32 blow;
1364
            ul32 bhigh;
1365
        } progvfo[10];
1366
        u8   beepon;
1367
        u8   beepvol;
1368
        u8   extspkr;
1369
        u8   ance;
1370
        u8   lang;
1371
        u8   vcvol;
1372
        u8   vcspd;
1373
        u8   pbkrpt;
1374
        u8   pbkint;
1375
        u8   cntrec;
1376
        u8   vhfaip;
1377
        u8   uhfaip;
1378
        u8   ssqlhu;
1379
        u8   mutehu;
1380
        u8   beatshft;
1381
        u8   tot;
1382
        u8   recall;
1383
        u8   eclnkspd;
1384
        u8   dtmfhld;
1385
        u8   dtmfspd;
1386
        u8   dtmfpau;
1387
        u8   dtmflck;
1388
        u8   rptrofst;
1389
        u8   rptr1750;
1390
        u8   bright;
1391
        u8   autobri;
1392
        u8   bkltclr;
1393
        u8   pf1key;
1394
        u8   pf2key;
1395
        u8   micpf1;
1396
        u8   micpf2;
1397
        u8   micpf3;
1398
        u8   micpf4;
1399
        u8   miclck;
1400
        u8   unk0372;
1401
        u8   scnrsm;
1402
        u8   apo;
1403
        u8   extband;
1404
        u8   extbaud;
1405
        u8   sqcsrc;
1406
        u8   autopm;
1407
        u8   dispbar;
1408
        u8   unk037a;
1409
        u8   bkltcont;
1410
        u8   dsprev;
1411
        u8   vsmode;
1412
        u8   intband;
1413
        u8   wxscntm;
1414
        u8   scntot;
1415
        u8   scncot;
1416
        u8   unk0382;
1417
        u8   unk0383;
1418
        u8   unk0384;
1419
        u8   unk0385;
1420
        u8   unk0386;
1421
        u8   unk0387;
1422
        u8   unk0388;
1423
        u8   unk0389;
1424
        u8   unk038a;
1425
        u8   unk038b;
1426
        u8   unk038c;
1427
        u8   unk038d;
1428
        u8   unk038e;
1429
        u8   unk038f;
1430
        u8   abnd118;
1431
        u8   abnd144;
1432
        u8   abnd220;
1433
        u8   abnd300;
1434
        u8   abnd430;
1435
        u8   bbnd144;
1436
        u8   bbnd220;
1437
        u8   bbnd300;
1438
        u8   bbnd430;
1439
        u8   bbnd800;
1440
        u8   unk039a;
1441
        u8   unk039b;
1442
        u8   unk039c;
1443
        u8   unk039d;
1444
        u8   unk039e;
1445
        u8   unk039f;
1446
        u8   unk03a0[96];       // to 0x03ff
1447
        };                        // end of struct pm
1448

    
1449
        #seekto 0x0000;         // block1: x000 - x023f
1450
        struct {
1451
        u8   unk000[16];
1452
        u8   unk010;
1453
        u8   unk011;
1454
        char unk012[3];
1455
        u8   ansbck;
1456
        u8   pmrecall;            // 0x0016
1457
        u8   pnlklk;
1458
        u8   dspmemch;
1459
        u8   m10mz;
1460
        u8   micsens;
1461
        u8   opband;
1462
        u8   unk01c;
1463
        u8   rptrmode;
1464
        u8   rptrhold;
1465
        u8   rptridx;
1466
        u8   unk020;
1467
        u8   pcbaud;
1468
        u8   unk022;
1469
        u8   pwdon;               //  0x0023
1470
        u8   unk024;
1471
        u8   unk025;
1472
        u8   unk026;
1473
        u8   unk027;
1474
        u8   unk028;
1475
        u8   unk029;
1476
        char pswd[6];             // 0x023a - 23f
1477
        } block1;
1478

    
1479
        #seekto 0x0030;
1480
        struct {
1481
        char code[16];            // @ 0x0030
1482
        } dtmc[10];
1483

    
1484
        struct {
1485
        char id[8];               // 0x00d0 - 0x011f
1486
        } dtmn[10];
1487

    
1488
        struct {                    // block1a: 0x0120 - 0x023f
1489
        u8   unk0120;
1490
        u8   unk0121;
1491
        u8   unk0122[78];
1492
        char rptrid[12];          // 0x0170 - 017b
1493
        u8   unk017c;
1494
        u8   unk017d;
1495
        u8   unk017e;
1496
        u8   unk017f;
1497
        u8   unk0180[128];        // 0x0180 - 0x01ff
1498
        } block1a;
1499

    
1500
        struct pm_grp pmg[6];       // 0x0200 - 0x0dff
1501

    
1502
        #seekto 0x0e00;
1503
        struct {
1504
        u8   band;
1505
        u8   skip;
1506
        } chmap[1030];              // to 0x0160b
1507

    
1508
        #seekto 0x01700;            // 0x01700 - 0x0575f
1509
        struct chns ch_mem[1030];   // 0-999 MR and 1000 -1029 Specials
1510

    
1511
        #seekto 0x05760;
1512
        struct chns call[2];
1513

    
1514
        #seekto 0x05800;
1515
        struct {
1516
        char name[8];
1517
        } ch_nam[1020];         // ends @ 0x07e0
1518

    
1519
        #seekto 0x077e0;        // 0x077e0 - 0x07830
1520
        struct {
1521
        char name[8];
1522
        } wxnam[10];
1523

    
1524
        #seekto 0x07da0;
1525
        struct {
1526
        char pmname[16];
1527
        } pm_name[5];
1528

    
1529
        #seekto 0x07df0;
1530
        struct {
1531
        char comnt[32];
1532
        } mcpcom;
1533

    
1534
        #seekto 0x08660;
1535
        struct {
1536
        char cmdr[10];
1537
        char tptr[10];
1538
        u8  skytone;          // 0x08674
1539
        } skycmd;
1540
                            // data stops at 0x09b98
1541
        """
1542

    
1543
        def _read_mem(radio):
1544
            """ Load the memory map """
1545
            global BAUD
1546
            status = chirp_common.Status()
1547
            status.cur = 0
1548
            val = 0
1549
            for mx in range(0, radio._num_blocks):
1550
                val += radio._num_packets[mx]
1551
            status.max = val
1552
            status.msg = "Reading %i packets" % val
1553
            radio.status_fn(status)
1554

    
1555
            data = ""
1556

    
1557
            radio.pipe.baudrate = BAUD
1558
            cmc = b"0M PROGRAM" + TERM
1559
            resp0 = _command(radio.pipe, cmc, 3, W8S)
1560
            junk = radio.pipe.read(16)       # flushit
1561
            for bkx in range(0, 0x09c):
1562
                if bkx != 0x07f:            # Skip block 7f !!??
1563
                    cmc = struct.pack('>cHB', b'R', bkx << 8, 0)
1564
                    resp0 = _command(radio.pipe, cmc, 260, W8S)
1565
                    junk = _command(radio.pipe, ACK, 1, W8S)
1566
                    if len(resp0) < 260:
1567
                        junk = _command(radio.pipe, b"E", 2, W8S)
1568
                        sx = "Block 0x%x read error: " % bkx
1569
                        sx += "Got %i bytes, expected 260." % len(resp0)
1570
                        LOG.error(sx)
1571
                        sx = "Block read error! Check debug.log"
1572
                        raise errors.RadioError(sx)
1573
                    if bkx == 0:   # 1st packet of 1st block
1574
                        mht = resp0[4:7]   # [57 00 00 00] 03 4b 01 ff ff ...
1575
                        data = resp0[5:6]  # 2nd byte (4b) replaces 1st
1576
                        data += resp0[5:]  # then bytes 2 on (4b 4b 01 ff ...)
1577
                    else:
1578
                        data += resp0[4:]       # skip cmd echo
1579
                    _update_status(radio, status)        # UI Update
1580
            cmc = struct.pack('>cHB', b'R', 0xFEF0, 0x10)
1581
            resp0 = _command(radio.pipe, cmc, 0x014, W8S)
1582
            data += resp0[4:]
1583
            junk = _command(radio.pipe, ACK, 1, W8S)
1584
            _update_status(radio, status)
1585
            cmc = struct.pack('>cHB', b'R', 0xFF00, 0x90)
1586
            resp0 = _command(radio.pipe, cmc, 0x094, W8S)
1587
            data += resp0[4:]
1588
            junk = _command(radio.pipe, ACK, 1, W8S)
1589
            _update_status(radio, status)
1590
            # Exit Prog mode, no TERM
1591
            resp = _command(radio.pipe, b"E", 2, W8S)     # Rtns 06 0d
1592
            radio.pipe.baudrate = BAUD
1593
            return data
1594

    
1595
        def _write_mem(radio):
1596
            """ PROG MCP Blocks Send """
1597
            global BAUD
1598
            # UI progress
1599
            status = chirp_common.Status()
1600
            status.cur = 0
1601
            val = 0
1602
            for mx in range(0, radio._num_blocks):
1603
                val += radio._num_packets[mx]
1604
            status.max = val
1605
            status.msg = "Writing %i packets" % val
1606
            radio.status_fn(status)
1607

    
1608
            imgadr = 0
1609
            radio.pipe.baudrate = BAUD
1610
            resp0 = _command(radio.pipe, b"0M PROGRAM" + TERM, 3, W8S)
1611
            # Read block 0 magic header thingy, save it
1612
            cmc = b"R" + bytes([0, 0, 4])
1613
            resp0 = _command(radio.pipe, cmc, 8, W8S)
1614
            mht0 = resp0[4:]    # Expecting [57 00 00 04] 03 4b 01 ff
1615
            junk = _command(radio.pipe, ACK, 1, W8S)
1616
            cmc = b"W" + bytes([0, 0, 1, 0x0ff])
1617
            junk = _command(radio.pipe, cmc, 1, W8S)     # responds ACK
1618
            cmc = b"R" + bytes([0x080, 0, 3])
1619
            resp = _command(radio.pipe, cmc, 7, W8S)   # [57 80 00 03] 00 33 00
1620
            mht1 = resp[4:]
1621
            junk = _command(radio.pipe, ACK, 1, W8S)
1622
            cmc = b"W" + bytes([0x80, 0, 1, 0x0ff])
1623
            junk = _command(radio.pipe, cmc, 1, W8S)
1624
            imgadr = 4      # After 03 4b 01 ff
1625
            for bkx in range(0, radio._num_packets[0]):
1626
                cmc = b"W" + bytes([bkx, 0, 0])
1627
                imgstep = 256
1628
                if bkx == 0:
1629
                    imgstep = 0x0fc
1630
                    cmc = b"W" + bytes([0, 4, imgstep])
1631
                    cmc += radio.get_mmap()[imgadr:imgadr + imgstep]
1632
                else:       # after first packet
1633
                    cmc += radio.get_mmap()[imgadr:imgadr + imgstep]
1634
                if bkx != 0x07f:        # don't send 7f !
1635
                    resp0 = _command(radio.pipe, cmc, 1, W8S)
1636
                    if resp0 != ACK:
1637
                        LOG.error("Packet 0x%x Write error, no ACK." % bkx)
1638
                        sx = "Radio failed to acknowledge upload packet!"
1639
                        raise errors.RadioError(sx)
1640
                    imgadr += imgstep
1641
                _update_status(radio, status)        # UI Update
1642
            # write fe and ff blocks
1643
            cmc = b"W" + bytes([0xfe, 0xf0, 16])
1644
            cmc += radio.get_mmap()[imgadr:imgadr + 16]
1645
            resp0 = _command(radio.pipe, cmc, 1, W8S)
1646
            if resp0 != ACK:
1647
                LOG.error("Packet 0xfe Write error, no ACK.")
1648
                sx = "Radio failed to acknowledge upload packet!"
1649
                raise errors.RadioError(sx)
1650
            imgadr += 16
1651
            cmc = b"W" + bytes([0xff, ,0, 0x90])
1652
            cmc += radio.get_mmap()[imgadr:imgadr + 0x090]
1653
            resp0 = _command(radio.pipe, cmc, 1, W8S)
1654
            if resp0 != ACK:
1655
                LOG.error("Packet 0xff Write error, no ACK.")
1656
                sx = "Radio failed to acknowledge upload packet!"
1657
                raise errors.RadioError(sx)
1658
            # Write mht1
1659
            cmc = b"W" + bytes([0x80, 0, 3]) + mht1
1660
            resp0 = _command(radio.pipe, cmc, 1, W8S)
1661
            if resp0 != ACK:
1662
                LOG.error("Mht1 Write error at 0x080 00 03 , no ACK.")
1663
                sx = "Radio failed to acknowledge upload packet!"
1664
                raise errors.RadioError(sx)
1665
            # and mht0
1666
            cmc = b"W" + bytes([0, 0, 4]) + mht0
1667
            resp0 = _command(radio.pipe, cmc, 1, W8S)
1668
            if resp0 != ACK:
1669
                LOG.error("Mht0 Write error at 00 00 04 , no ACK.")
1670
                sx = "Radio failed to acknowledge upload packet!"
1671
                raise errors.RadioError(sx)
1672
            # Write E to Exit PROG mode
1673
            resp = _command(radio.pipe, b"E", 2, W8S)
1674
            return
1675

    
1676
    @directory.register
1677
    class KenwoodTMD710GRadio(KenwoodTMx710Radio):
1678
        """ Kenwood TM-D710G VHF/UHF/GPS/APRS Radio model. """
1679
        VENDOR = "Kenwood"
1680
        MODEL = "TM-D710G_CloneMode"
1681
        SHORT = "G"       # Quick model code 1 for G
1682

    
1683
        _num_blocks = 2                # Only reading first 2, not GPS logs
1684
        _packet_size = [261, 261, 261]
1685
        _block_addr = [0, 0x100, 0x200]       # starting addr, each block
1686
        _num_packets = [0x7f, 0x0fe, 0x200]   # num packets per block, 0-based
1687

    
1688
        MEM_FORMAT = """
1689
        struct chns {            // 16 bytes channel structure
1690
        ul32  rxfreq;
1691
        u8   tstep;
1692
        u8   mode;
1693
        u8   tmode:4,
1694
            duplex:4;         // 4 = split
1695
        u8   rtone;
1696
        u8   ctone;
1697
        u8   dtcs;
1698
        u8   cross;
1699
        ul32 offset;          // or Split mode TX freq
1700
        u8   splitstep;
1701
        };
1702

    
1703
        struct pm_grp {         // 512 bytes per group
1704
        u8   unk0200;
1705
        u8   a_mr;
1706
        u8   unk0202;
1707
        u8   unk0203;
1708
        u8   unk0204;
1709
        u8   unk0205;
1710
        u8   unk0206;
1711
        u8   a_pwr;
1712
        u8   wxalerta;
1713
        u8   asmsql;
1714
        u8   a_chn;
1715
        u8   unk020b;
1716
        u8   unk020c;
1717
        u8   b_mr;
1718
        u8   unk020e;
1719
        u8   unk020f;
1720
        u8   unk0210;
1721
        u8   unk0211;
1722
        u8   unk0212;
1723
        u8   b_pwr;
1724
        u8   wxalertb;
1725
        u8   bsmsql;
1726
        u8   b_chn;
1727
        u8   unk0217;
1728
        u8   unk0218;
1729
        u8   unk0219;
1730
        u8   unk021a;
1731
        u8   unk021b;
1732
        u8   unk021c;
1733
        u8   unk021d;
1734
        u8   unk021e;
1735
        u8   unk021f;
1736
        u8   unk0220;
1737
        u8   unk0221;
1738
        u8   unk0222;
1739
        u8   unk0223;
1740
        u8   unk0224;
1741
        u8   unk0225;
1742
        u8   unk0226;
1743
        u8   unk0227;
1744
        u8   unk0228;
1745
        u8   unk0229;
1746
        u8   unk022a;
1747
        u8   unk022b;
1748
        u8   unk022c;
1749
        u8   unk022d;
1750
        u8   unk022e;
1751
        u8   unk022f;
1752
        u8   unk0230;
1753
        u8   unk0231;
1754
        u8   sqclogic;
1755
        u8   txband;
1756
        u8   single;
1757
        u8   unk0235;
1758
        u8   mute;
1759
        u8   unk0237;
1760
        u8   unk0238;
1761
        u8   unk0239;
1762
        u8   unk0237a;
1763
        u8   unk023b;
1764
        u8   unk023c;
1765
        u8   unk023d;
1766
        u8   unk023e;
1767
        u8   unk023f;
1768
        struct chns vfo[10];         // 0x0240 - 0x02df
1769
        char pwron[8];
1770
        u8   unk02e8;
1771
        u8   unk02e9;
1772
        u8   unk02ea;
1773
        u8   unk02eb;
1774
        u8   unk02ec;
1775
        u8   unk02ed;
1776
        u8   unk02ee;
1777
        u8   unk02ef;
1778
        char memgrplk[10];
1779
        u8   unk02fa;
1780
        u8   unk02fb;
1781
        u8   unk02fc;
1782
        u8   unk02fd;
1783
        u8   unk02fe;
1784
        u8   unk02ff;
1785
        struct {
1786
            ul32 blow;
1787
            ul32 bhigh;
1788
        } progvfo[10];
1789
        u8   beepon;
1790
        u8   beepvol;
1791
        u8   extspkr;
1792
        u8   ance;
1793
        u8   lang;
1794
        u8   vcvol;
1795
        u8   vcspd;
1796
        u8   pbkrpt;
1797
        u8   pbkint;
1798
        u8   cntrec;
1799
        u8   vhfaip;
1800
        u8   uhfaip;
1801
        u8   ssqlhu;
1802
        u8   mutehu;
1803
        u8   beatshft;
1804
        u8   tot;
1805
        u8   recall;
1806
        u8   eclnkspd;
1807
        u8   dtmfhld;
1808
        u8   dtmfspd;
1809
        u8   dtmfpau;
1810
        u8   dtmflck;
1811
        u8   rptrofst;
1812
        u8   rptr1750;
1813
        u8   bright;
1814
        u8   autobri;
1815
        u8   bkltclr;
1816
        u8   pf1key;
1817
        u8   pf2key;
1818
        u8   micpf1;
1819
        u8   micpf2;
1820
        u8   micpf3;
1821
        u8   micpf4;
1822
        u8   miclck;
1823
        u8   unk0372;
1824
        u8   scnrsm;
1825
        u8   apo;
1826
        u8   extband;
1827
        u8   extbaud;
1828
        u8   sqcsrc;
1829
        u8   autopm;
1830
        u8   dispbar;
1831
        u8   unk037a;
1832
        u8   bkltcont;
1833
        u8   dsprev;
1834
        u8   vsmode;
1835
        u8   intband;
1836
        u8   wxscntm;
1837
        u8   scntot;
1838
        u8   scncot;
1839
        u8   unk0382;
1840
        u8   unk0383;
1841
        u8   unk0384;
1842
        u8   unk0385;
1843
        u8   unk0386;
1844
        u8   unk0387;
1845
        u8   unk0388;
1846
        u8   unk0389;
1847
        u8   unk038a;
1848
        u8   unk038b;
1849
        u8   unk038c;
1850
        u8   unk038d;
1851
        u8   unk038e;
1852
        u8   unk038f;
1853
        u8   abnd118;
1854
        u8   abnd144;
1855
        u8   abnd220;
1856
        u8   abnd300;
1857
        u8   abnd430;
1858
        u8   bbnd144;
1859
        u8   bbnd220;
1860
        u8   bbnd300;
1861
        u8   bbnd430;
1862
        u8   bbnd800;
1863
        u8   unk039a;
1864
        u8   unk039b;
1865
        u8   unk039c;
1866
        u8   unk039d;
1867
        u8   unk039e;
1868
        u8   unk039f;
1869
        u8   unk03a0[96];       // to 0x03ff
1870
        };                        // end of struct pm
1871

    
1872
        #seekto 0x0000;         // block1: x000 - x023f
1873
        struct {
1874
        u8   unk000[16];
1875
        u8   unk010;
1876
        u8   unk011;
1877
        char unk012[3];
1878
        u8   ansbck;
1879
        u8   pmrecall;            // 0x0016
1880
        u8   pnlklk;
1881
        u8   dspmemch;
1882
        u8   m10mz;
1883
        u8   micsens;
1884
        u8   opband;
1885
        u8   unk01c;
1886
        u8   rptrmode;
1887
        u8   rptrhold;
1888
        u8   rptridx;
1889
        u8   unk020;
1890
        u8   pcbaud;
1891
        u8   unk022;
1892
        u8   pwdon;               //  0x0023
1893
        u8   unk024;
1894
        u8   unk025;
1895
        u8   unk026;
1896
        u8   unk027;
1897
        u8   unk028;
1898
        u8   unk029;
1899
        char pswd[6];             // 0x023a - 23f
1900
        } block1;
1901

    
1902
        #seekto 0x0030;
1903
        struct {
1904
        char code[16];            // @ 0x0030
1905
        } dtmc[10];
1906

    
1907
        struct {
1908
        char id[8];               // 0x00d0 - 0x011f
1909
        } dtmn[10];
1910

    
1911
        struct {                    // block1a: 0x0120 - 0x023f
1912
        u8   unk0120;
1913
        u8   unk0121;
1914
        u8   unk0122[78];
1915
        char rptrid[12];          // 0x0170 - 017b
1916
        u8   unk017c;
1917
        u8   unk017d;
1918
        u8   unk017e;
1919
        u8   unk017f;
1920
        u8   unk0180[128];        // 0x0180 - 0x01ff
1921
        } block1a;
1922

    
1923
        struct pm_grp pmg[6];       // 0x0200 - 0x0dff
1924

    
1925
        #seekto 0x0e00;
1926
        struct {
1927
        u8   band;
1928
        u8   skip;
1929
        } chmap[1030];              // to 0x0160b
1930

    
1931
        #seekto 0x01700;            // 0x01700 - 0x0575f
1932
        struct chns ch_mem[1030];   // 0-999 MR and 1000 -1029 Specials
1933

    
1934
        #seekto 0x058a0;
1935
        struct chns call[2];
1936

    
1937
        #seekto 0x05900;
1938
        struct {
1939
        char name[8];
1940
        } ch_nam[1020];         // ends @ 0x07840
1941

    
1942
        #seekto 0x078e0;        // 0x078e0 - 0x0792f
1943
        struct {
1944
        char name[8];
1945
        } wxnam[10];
1946

    
1947
        #seekto 0x07da0;
1948
        struct {
1949
        char pmname[16];
1950
        } pm_name[5];
1951

    
1952
        #seekto 0x07df0;
1953
        struct {
1954
        char comnt[32];
1955
        } mcpcom;
1956
                            // Block 1 ends @ 0x07eff
1957
                            // Block 2 starts @ 0x07f00
1958
        #seekto 0x08660;
1959
        struct {
1960
        char cmdr[10];
1961
        char tptr[10];
1962
        u8  skytone;          // 0x08674
1963
        } skycmd;
1964

    
1965
        #seekto 0x10ef0;
1966
        struct {
1967
        u8   bmp[1896];
1968
        u8   unk11658[8];     // 0x11658
1969
        char bmpfyl[64];      // 0x11660
1970
        u8   unk116a0[95];
1971
        u8   bmpon;           // 0x116ff
1972
        } bitmap;
1973

    
1974
                        // 2nd block ends @ 0x017cff
1975
        """
1976

    
1977
        def _make_command(self, cmd, addr, length, data=b''):
1978
            cmc = struct.pack('>IB', addr, length)
1979
            return cmd + cmc[1:] + data
1980

    
1981
        def _read_mem(radio):
1982
            """ Load the memory map """
1983
            global BAUD
1984
            status = chirp_common.Status()
1985
            status.cur = 0
1986
            val = 0
1987
            for mx in range(0, radio._num_blocks):
1988
                val += radio._num_packets[mx]
1989
            status.max = val
1990
            status.msg = "Reading %i packets" % val
1991
            radio.status_fn(status)
1992

    
1993
            data = b""
1994

    
1995
            radio.pipe.baudrate = BAUD
1996
            resp0 = radio.pipe.read(16)     # flush
1997
            cmc = b"0M PROGRAM" + TERM
1998
            resp0 = _command(radio.pipe, cmc, 3, W8S)
1999
            if resp0[:1] == "?":        # try once more
2000
                resp0 = _command(radio.pipe, cmc, 3, W8S)
2001
            radio.pipe.baudrate = 57600     # PROG mode is always 57.6
2002
            LOG.debug("Switching to 57600 baud download.")
2003
            junk = radio.pipe.read(1)       # trailing byte
2004
            for blkn in range(0, radio._num_blocks):
2005
                for bkx in range(0, radio._num_packets[blkn]):
2006
                    addr = (radio._block_addr[blkn] << 8) | (bkx << 8)
2007
                    resp0 = _command(radio.pipe,
2008
                                     radio._make_command(b'R', addr, 0),
2009
                                     radio._packet_size[blkn], W8S)
2010
                    if len(resp0) < radio._packet_size[blkn]:
2011
                        junk = _command(radio.pipe, b"E", 0, W8S)
2012
                        lb = len(resp0)
2013
                        xb = radio._packet_size[blkn]
2014
                        sx = "Block 0x%x, 0x%x read error: " % (blkn, bkx)
2015
                        sx += "Got %i bytes, expected %i." % (lb, xb)
2016
                        LOG.error(sx)
2017
                        sx = "Block read error! Check debug.log"
2018
                        raise errors.RadioError(sx)
2019
                    if blkn == 0 and bkx == 0:   # 1st packet of 1st block
2020
                        mht = resp0[5:9]   # Magic Header Thingy after cmd echo
2021
                        data += mht[0:1]
2022
                        data += b'\xff\xff\xff'
2023
                        data += resp0[9:]
2024
                    else:
2025
                        data += resp0[5:]       # skip cmd echo
2026
                    _update_status(radio, status)        # UI Update
2027
            # Exit Prog mode, no TERM
2028
            resp = _command(radio.pipe, b"E", 0, W8S)
2029
            radio.pipe.baudrate = BAUD
2030
            return data
2031

    
2032
        def _write_mem(radio):
2033
            """ PROG MCP Blocks Send """
2034
            global BAUD
2035
            # UI progress
2036
            status = chirp_common.Status()
2037
            status.cur = 0
2038
            val = 0
2039
            for mx in range(0, radio._num_blocks):
2040
                val += radio._num_packets[mx]
2041
            status.max = val
2042
            status.msg = "Writing %i packets" % val
2043
            radio.status_fn(status)
2044

    
2045
            imgadr = 0
2046
            radio.pipe.baudrate = BAUD
2047
            resp0 = _command(radio.pipe, b"0M PROGRAM" + TERM, 3, W8S)
2048
            radio.pipe.baudrate = 57600
2049
            LOG.debug("Switching to 57600 baud upload.")
2050
            junk = radio.pipe.read(1)
2051
            # Read block 0 magic header thingy, save it
2052
            addr = radio._block_addr[0] << 8
2053
            resp0 = _command(radio.pipe,
2054
                             radio._make_command(b'R', addr, 4),
2055
                             16, W8S)
2056
            mht0 = resp0[5:]
2057
            # Now get block 1 mht
2058
            addr = radio._block_addr[1] << 8
2059
            resp0 = _command(radio.pipe,
2060
                             radio._make_command(b'R', addr, 5),
2061
                             16, W8S)
2062
            mht1 = resp0[5:]
2063
            for blkn in range(0, radio._num_blocks):
2064
                for bkx in range(0, radio._num_packets[blkn]):
2065
                    addr = (radio._block_addr[blkn] << 8) | (bkx << 8)
2066

    
2067
                    if bkx == 0:    # First packet of the block includes mht
2068
                        if blkn == 0:
2069
                            data = (b'\xff\x4b\x01\x32' +
2070
                                    radio.get_mmap()[4:imgadr + 256])
2071
                        elif blkn == 1:
2072
                            data = mht1 + radio.get_mmap()[imgadr + 5:imgadr +
2073
                                                           256]
2074
                    else:       # after first packet
2075
                        data = radio.get_mmap()[imgadr:imgadr + 256]
2076
                    cmc = radio._make_command(b'W', addr, 0, data)
2077

    
2078
                    resp0 = _command(radio.pipe, cmc, 6, W8S)
2079
                    if bkx > 0 and resp0 != ACK:
2080
                        LOG.error("Packet 0x%x Write error, no ACK!" % bkx)
2081
                        sx = "Radio failed to acknowledge upload. "
2082
                        sx += "See debug.log"
2083
                        raise errors.RadioError(sx)
2084
                    imgadr += 256
2085
                    _update_status(radio, status)        # UI Update
2086
            # Re-write magic headers
2087
            cmc = radio._make_command(b'W', (radio._block_addr[0] << 8) | 1, 3,
2088
                                      mht0[1:3] + b'\x32')
2089
            resp0 = _command(radio.pipe, cmc, 1, W8S)
2090
            cmc = radio._make_command(b'W', radio._block_addr[1] << 8, 5, mht1)
2091
            resp0 = _command(radio.pipe, cmc, 1, W8S)
2092
            cmc = radio._make_command(b'Z', radio._block_addr[0], 1, mht0[0:1])
2093
            resp0 = _command(radio.pipe, cmc, 16, W8S)
2094
            # Write E to Exit PROG mode
2095
            resp = _command(radio.pipe, b"E", 0, W8S)
2096
            radio.pipe.baudrate = BAUD
2097
            return
(1-1/2)