Project

General

Profile

Bug #9687 » btech_vc9204.py

Jim Unroe, 01/08/2022 04:11 PM

 
1
# Copyright 2016-2021:
2
# * Pavel Milanes CO7WT, <pavelmc@gmail.com>
3
# * Jim Unroe KC9HI, <rock.unroe@gmail.com>
4
#
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 2 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

    
22
from time import sleep
23
from chirp import chirp_common, directory, memmap
24
from chirp import bitwise, errors, util
25
from chirp.settings import RadioSettingGroup, RadioSetting, \
26
    RadioSettingValueBoolean, RadioSettingValueList, \
27
    RadioSettingValueString, RadioSettingValueInteger, \
28
    RadioSettingValueFloat, RadioSettings, InvalidValueError
29
from textwrap import dedent
30

    
31
LOG = logging.getLogger(__name__)
32

    
33
# A note about the memmory in these radios
34
#
35
# The real memory of these radios extends to 0x4000
36
# On read the factory software only uses up to 0x3200
37
# On write it just uploads the contents up to 0x3100
38
#
39
# The mem beyond 0x3200 holds the ID data
40

    
41
MEM_SIZE = 0x4000
42
BLOCK_SIZE = 0x40
43
TX_BLOCK_SIZE = 0x10
44
ACK_CMD = "\x06"
45
MODES = ["FM", "NFM"]
46
SKIP_VALUES = ["S", ""]
47
TONES = chirp_common.TONES
48
DTCS = sorted(chirp_common.DTCS_CODES + [645])
49

    
50
# lists related to "extra" settings
51
PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
52
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
53
OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
54
SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
55

    
56
# lists
57
LIST_AB = ["A", "B"]
58
LIST_ABCD = LIST_AB + ["C", "D"]
59
LIST_ANIL = ["3", "4", "5"]
60
LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
61
LIST_COLOR4 = ["Off", "Blue", "Orange", "Purple"]
62
LIST_COLOR8 = ["White", "Red", "Blue", "Green", "Yellow", "Indego",
63
               "Purple", "Gray"]
64
LIST_COLOR9 = ["Black"] + LIST_COLOR8
65
LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
66
LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
67
LIST_EMCTPX = ["Off"] + LIST_EMCTP
68
LIST_LANGUA = ["English", "Chinese"]
69
LIST_MDF = ["Frequency", "Channel", "Name"]
70
LIST_OFF1TO9 = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
71
LIST_OFF1TO10 = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
72
LIST_OFF1TO50 = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
73
LIST_PONMSG = ["Full", "Message", "Battery voltage"]
74
LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
75
LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
76
LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 11)]
77
LIST_SCMODE = ["Off", "PTT-SC", "MEM-SC", "PON-SC"]
78
LIST_SHIFT = ["Off", "+", "-"]
79
LIST_SKIPTX = ["Off", "Skip 1", "Skip 2"]
80
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
81
LIST_STEP = [str(x) for x in STEPS]
82
LIST_SYNC = ["Off", "AB", "CD", "AB+CD"]
83
# the first 12 TMR choices common to all color display mobile radios
84
LIST_TMR12 = ["OFF", "M+A", "M+B", "M+C", "M+D", "M+A+B", "M+A+C", "M+A+D",
85
              "M+B+C", "M+B+D", "M+C+D", "M+A+B+C"]
86
# the 16 choice list for color display mobile radios that correctly implement
87
# the full 16 TMR choices
88
LIST_TMR16 = LIST_TMR12 + ["M+A+B+D", "M+A+C+D", "M+B+C+D", "A+B+C+D"]
89
# the 15 choice list for color mobile radios that are missing the M+A+B+D
90
# choice in the TMR menu
91
LIST_TMR15 = LIST_TMR12 + ["M+A+C+D", "M+B+C+D", "A+B+C+D"]
92
LIST_TMRTX = ["Track", "Fixed"]
93
LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
94
LIST_TXDISP = ["Power", "Mic Volume"]
95
LIST_TXP = ["High", "Low"]
96
LIST_TXP3 = ["High", "Mid", "Low"]
97
LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
98
LIST_VFOMR = ["Frequency", "Channel"]
99
LIST_VOICE = ["Off"] + LIST_LANGUA
100
LIST_VOX = ["Off"] + ["%s" % x for x in range(1, 11)]
101
LIST_VOXT = ["%s seconds" % x for x in range(0, 21)]
102
LIST_WIDE = ["Wide", "Narrow"]
103

    
104
# lists related to DTMF, 2TONE and 5TONE settings
105
LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
106
                        "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
107
                        "CCITT", "NATEL", "MODAT", "none"]
108
LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
109
                                     "ZVEI2", "ZVEI3",
110
                                     "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
111
                                     "EURO", "CCITT", "NATEL", "MODAT"]
112
LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
113
                               "100", "110", "120", "130", "140", "150", "160",
114
                               "170", "180", "190", "200"]
115
LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
116
                     "B", "C", "D", "E", "F"]
117
LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
118
LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
119
LIST_5TONE_RESET_COLOR = ["%s ms" % x for x in range(100, 20100, 100)]
120
LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
121
LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
122
                    "C", "D", "#", "*"]
123
LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
124
                    0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B]
125
LIST_DTMF_SPECIAL_DIGITS = ["*", "#", "A", "B", "C", "D"]
126
LIST_DTMF_SPECIAL_VALUES = [0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
127
LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
128
CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
129
LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
130
                  "B-A", "B-C", "B-D",
131
                  "C-A", "C-B", "C-D",
132
                  "D-A", "D-B", "D-C"]
133
LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
134

    
135
# This is a general serial timeout for all serial read functions.
136
# Practice has show that about 0.7 sec will be enough to cover all radios.
137
STIMEOUT = 0.7
138

    
139
# this var controls the verbosity in the debug and by default it's low (False)
140
# make it True and you will to get a very verbose debug.log
141
debug = False
142

    
143
# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
144
VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
145
    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
146

    
147
GMRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
148
               462.6875, 462.7125]
149
GMRS_FREQS2 = [467.5625, 467.5875, 467.6125, 467.6375, 467.6625,
150
               467.6875, 467.7125]
151
GMRS_FREQS3 = [462.5500, 462.5750, 462.6000, 462.6250, 462.6500,
152
               462.6750, 462.7000, 462.7250]
153
GMRS_FREQS = GMRS_FREQS1 + GMRS_FREQS2 + GMRS_FREQS3 * 2
154

    
155

    
156
# #### ID strings #####################################################
157

    
158
# BTECH UV2501 pre-production units
159
UV2501pp_fp = "M2C294"
160
# BTECH UV2501 pre-production units 2 + and 1st Gen radios
161
UV2501pp2_fp = "M29204"
162
# B-TECH UV-2501 second generation (2G) radios
163
UV2501G2_fp = "BTG214"
164
# B-TECH UV-2501 third generation (3G) radios
165
UV2501G3_fp = "BTG324"
166

    
167
# B-TECH UV-2501+220 pre-production units
168
UV2501_220pp_fp = "M3C281"
169
# extra block read for the 2501+220 pre-production units
170
# the same for all of this radios so far
171
UV2501_220pp_id = "      280528"
172
# B-TECH UV-2501+220
173
UV2501_220_fp = "M3G201"
174
# new variant, let's call it Generation 2
175
UV2501_220G2_fp = "BTG211"
176
# B-TECH UV-2501+220 third generation (3G)
177
UV2501_220G3_fp = "BTG311"
178

    
179
# B-TECH UV-5001 pre-production units + 1st Gen radios
180
UV5001pp_fp = "V19204"
181
# B-TECH UV-5001 alpha units
182
UV5001alpha_fp = "V28204"
183
# B-TECH UV-5001 second generation (2G) radios
184
UV5001G2_fp = "BTG214"
185
# B-TECH UV-5001 second generation (2G2)
186
UV5001G22_fp = "V2G204"
187
# B-TECH UV-5001 third generation (3G)
188
UV5001G3_fp = "BTG304"
189

    
190
# B-TECH UV-25X2
191
UV25X2_fp = "UC2012"
192

    
193
# B-TECH UV-25X4
194
UV25X4_fp = "UC4014"
195

    
196
# B-TECH UV-50X2
197
UV50X2_fp = "UC2M12"
198

    
199
# B-TECH GMRS-50X1
200
GMRS50X1_fp = "NC1802"
201
GMRS50X1_fp1 = "NC1932"
202

    
203
# special var to know when we found a BTECH Gen 3
204
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
205

    
206

    
207
# WACCOM Mini-8900
208
MINI8900_fp = "M28854"
209

    
210

    
211
# QYT KT-UV980
212
KTUV980_fp = "H28854"
213

    
214
# QYT KT8900
215
KT8900_fp = "M29154"
216
# New generations KT8900
217
KT8900_fp1 = "M2C234"
218
KT8900_fp2 = "M2G1F4"
219
KT8900_fp3 = "M2G2F4"
220
KT8900_fp4 = "M2G304"
221
KT8900_fp5 = "M2G314"
222
KT8900_fp6 = "M2G424"
223
KT8900_fp7 = "M27184"
224
# this radio has an extra ID
225
KT8900_id = "303688"
226
# another extra ID in sep/2021
227
KT8900_id2 = "\x05\x58\x3d\xf0\x10"
228

    
229
# KT8900R
230
KT8900R_fp = "M3G1F4"
231
# Second Generation
232
KT8900R_fp1 = "M3G214"
233
# another model
234
KT8900R_fp2 = "M3C234"
235
# another model G4?
236
KT8900R_fp3 = "M39164"
237
# another model
238
KT8900R_fp4 = "M3G314"
239
# this radio has an extra ID
240
KT8900R_id = "280528"
241
# another extra ID in dec/2018
242
KT8900R_id2 = "\x05\x58\x3d\xf0\x10"
243

    
244
# KT7900D (quad band)
245
KT7900D_fp = "VC4004"
246
KT7900D_fp1 = "VC4284"
247
KT7900D_fp2 = "VC4264"
248
KT7900D_fp3 = "VC4114"
249
KT7900D_fp4 = "VC4104"
250
KT7900D_fp5 = "VC4254"
251
KT7900D_fp6 = "VC5264"
252
KT7900D_fp7 = "VC9204"
253

    
254
# QB25 (quad band) - a clone of KT7900D
255
QB25_fp = "QB-25"
256

    
257
# KT8900D (dual band)
258
KT8900D_fp = "VC2002"
259
KT8900D_fp1 = "VC8632"
260
KT8900D_fp2 = "VC3402"
261
KT8900D_fp3 = "VC7062"
262

    
263
# LUITON LT-588UV
264
LT588UV_fp = "V2G1F4"
265
# Added by rstrickoff gen 2 id
266
LT588UV_fp1 = "V2G214"
267

    
268
# QYT KT-8R (quad band ht)
269
KT8R_fp = "MCB264"
270
KT8R_fp1 = "MCB284"
271
KT8R_fp2 = "MC5264"
272

    
273
# QYT KT5800 (dual band)
274
KT5800_fp = "VCB222"
275

    
276
# QYT KT980Plus (dual band)
277
KT980PLUS_fp = "VC2002"
278
KT980PLUS_fp1 = "VC6042"
279

    
280
# Radioddity DB25-G (gmrs)
281
DB25G_fp = "VC6182"
282

    
283

    
284
# ### MAGICS
285
# for the Waccom Mini-8900
286
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
287
# for the B-TECH UV-2501+220 (including pre production ones)
288
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
289
# for the QYT KT8900 & R
290
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
291
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
292
# magic string for all other models
293
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
294
# for the QYT KT7900D & KT8900D
295
MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
296
# for the BTECH UV-25X2 and UV-50X2
297
MSTRING_UV25X2 = "\x55\x20\x16\x12\x28\xFF\xDC\x02"
298
# for the BTECH UV-25X4
299
MSTRING_UV25X4 = "\x55\x20\x16\x11\x18\xFF\xDC\x02"
300
# for the BTECH GMRS-50X1
301
MSTRING_GMRS50X1 = "\x55\x20\x18\x10\x18\xFF\xDC\x02"
302
# for the QYT KT-8R
303
MSTRING_KT8R = "\x55\x20\x17\x07\x03\xFF\xDC\x02"
304

    
305

    
306
def _clean_buffer(radio):
307
    """Cleaning the read serial buffer, hard timeout to survive an infinite
308
    data stream"""
309

    
310
    # touching the serial timeout to optimize the flushing
311
    # restored at the end to the default value
312
    radio.pipe.timeout = 0.1
313
    dump = "1"
314
    datacount = 0
315

    
316
    try:
317
        while len(dump) > 0:
318
            dump = radio.pipe.read(100)
319
            datacount += len(dump)
320
            # hard limit to survive a infinite serial data stream
321
            # 5 times bigger than a normal rx block (69 bytes)
322
            if datacount > 345:
323
                seriale = "Please check your serial port selection."
324
                raise errors.RadioError(seriale)
325

    
326
        # restore the default serial timeout
327
        radio.pipe.timeout = STIMEOUT
328

    
329
    except Exception:
330
        raise errors.RadioError("Unknown error cleaning the serial buffer")
331

    
332

    
333
def _rawrecv(radio, amount):
334
    """Raw read from the radio device, less intensive way"""
335

    
336
    data = ""
337

    
338
    try:
339
        data = radio.pipe.read(amount)
340

    
341
        # DEBUG
342
        if debug is True:
343
            LOG.debug("<== (%d) bytes:\n\n%s" %
344
                      (len(data), util.hexprint(data)))
345

    
346
        # fail if no data is received
347
        if len(data) == 0:
348
            raise errors.RadioError("No data received from radio")
349

    
350
        # notice on the logs if short
351
        if len(data) < amount:
352
            LOG.warn("Short reading %d bytes from the %d requested." %
353
                     (len(data), amount))
354

    
355
    except:
356
        raise errors.RadioError("Error reading data from radio")
357

    
358
    return data
359

    
360

    
361
def _send(radio, data):
362
    """Send data to the radio device"""
363

    
364
    try:
365
        for byte in data:
366
            radio.pipe.write(byte)
367
            # Some OS (mainly Linux ones) are too fast on the serial and
368
            # get the MCU inside the radio stuck in the early stages, this
369
            # hits some models more than others.
370
            #
371
            # To cope with that we introduce a delay on the writes.
372
            # Many option have been tested (delaying only after error occures,
373
            # after short reads, only for linux, ...)
374
            # Finally, a static delay was chosen as simplest of all solutions
375
            # (Michael Wagner, OE4AMW)
376
            # (for details, see issue 3993)
377
            sleep(0.002)
378

    
379
        # DEBUG
380
        if debug is True:
381
            LOG.debug("==> (%d) bytes:\n\n%s" %
382
                      (len(data), util.hexprint(data)))
383
    except:
384
        raise errors.RadioError("Error sending data to radio")
385

    
386

    
387
def _make_frame(cmd, addr, length, data=""):
388
    """Pack the info in the headder format"""
389
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
390
    # add the data if set
391
    if len(data) != 0:
392
        frame += data
393

    
394
    return frame
395

    
396

    
397
def _recv(radio, addr):
398
    """Get data from the radio all at once to lower syscalls load"""
399

    
400
    # Get the full 69 bytes at a time to reduce load
401
    # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
402

    
403
    # get the whole block
404
    block = _rawrecv(radio, BLOCK_SIZE + 5)
405

    
406
    # basic check
407
    if len(block) < (BLOCK_SIZE + 5):
408
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
409

    
410
    # checking for the ack
411
    if block[0] != ACK_CMD:
412
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
413

    
414
    # header validation
415
    c, a, l = struct.unpack(">BHB", block[1:5])
416
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
417
        LOG.debug("Invalid header for block 0x%04x" % addr)
418
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
419
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
420

    
421
    # return the data
422
    return block[5:]
423

    
424

    
425
def _start_clone_mode(radio, status):
426
    """Put the radio in clone mode and get the ident string, 3 tries"""
427

    
428
    # cleaning the serial buffer
429
    _clean_buffer(radio)
430

    
431
    # prep the data to show in the UI
432
    status.cur = 0
433
    status.msg = "Identifying the radio..."
434
    status.max = 3
435
    radio.status_fn(status)
436

    
437
    try:
438
        for a in range(0, status.max):
439
            # Update the UI
440
            status.cur = a + 1
441
            radio.status_fn(status)
442

    
443
            # send the magic word
444
            _send(radio, radio._magic)
445

    
446
            # Now you get a x06 of ACK if all goes well
447
            ack = radio.pipe.read(1)
448

    
449
            if ack == "\x06":
450
                # DEBUG
451
                LOG.info("Magic ACK received")
452
                status.cur = status.max
453
                radio.status_fn(status)
454

    
455
                return True
456

    
457
        return False
458

    
459
    except errors.RadioError:
460
        raise
461
    except Exception, e:
462
        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
463

    
464

    
465
def _do_ident(radio, status, upload=False):
466
    """Put the radio in PROGRAM mode & identify it"""
467
    #  set the serial discipline
468
    radio.pipe.baudrate = 9600
469
    radio.pipe.parity = "N"
470

    
471
    # open the radio into program mode
472
    if _start_clone_mode(radio, status) is False:
473
        msg = "Radio did not enter clone mode"
474
        # warning about old versions of QYT KT8900
475
        if radio.MODEL == "KT8900":
476
            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
477
            msg += " known variant of this radios that is a clone of it."
478
        raise errors.RadioError(msg)
479

    
480
    # Ok, get the ident string
481
    ident = _rawrecv(radio, 49)
482

    
483
    # basic check for the ident
484
    if len(ident) != 49:
485
        raise errors.RadioError("Radio send a short ident block.")
486

    
487
    # check if ident is OK
488
    itis = False
489
    for fp in radio._fileid:
490
        if fp in ident:
491
            # got it!
492
            itis = True
493
            # checking if we are dealing with a Gen 3 BTECH
494
            if radio.VENDOR == "BTECH" and fp in BTECH3:
495
                radio.btech3 = True
496

    
497
            break
498

    
499
    if itis is False:
500
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
501
        raise errors.RadioError("Radio identification failed.")
502

    
503
    # some radios needs a extra read and check for a code on it, this ones
504
    # has the check value in the _id2 var, others simply False
505
    if radio._id2 is not False:
506
        # lower the timeout here as this radios are reseting due to timeout
507
        radio.pipe.timeout = 0.05
508

    
509
        # query & receive the extra ID
510
        _send(radio, _make_frame("S", 0x3DF0, 16))
511
        id2 = _rawrecv(radio, 21)
512

    
513
        # WARNING !!!!!!
514
        # different radios send a response with a different amount of data
515
        # it seems that it's padded with \xff, \x20 and some times with \x00
516
        # we just care about the first 16, our magic string is in there
517
        if len(id2) < 16:
518
            raise errors.RadioError("The extra ID is short, aborting.")
519

    
520
        # ok, the correct string must be in the received data
521
        # the radio._id2 var will be always a list
522
        flag2 = False
523
        for _id2 in radio._id2:
524
            if _id2 in id2:
525
                flag2 = True
526

    
527
        if not flag2:
528
            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
529
                      (radio.MODEL, util.hexprint(id2)))
530
            raise errors.RadioError("The extra ID is wrong, aborting.")
531

    
532
        # this radios need a extra request/answer here on the upload
533
        # the amount of data received depends of the radio type
534
        #
535
        # also the first block of TX must no have the ACK at the beginning
536
        # see _upload for this.
537
        if upload is True:
538
            # send an ACK
539
            _send(radio, ACK_CMD)
540

    
541
            # the amount of data depend on the radio, so far we have two radios
542
            # reading two bytes with an ACK at the end and just ONE with just
543
            # one byte (QYT KT8900)
544
            # the JT-6188 appears a clone of the last, but reads TWO bytes.
545
            #
546
            # we will read two bytes with a custom timeout to not penalize the
547
            # users for this.
548
            #
549
            # we just check for a response and last byte being a ACK, that is
550
            # the common stone for all radios (3 so far)
551
            ack = _rawrecv(radio, 2)
552

    
553
            # checking
554
            if len(ack) == 0 or ack[-1:] != ACK_CMD:
555
                raise errors.RadioError("Radio didn't ACK the upload")
556

    
557
            # restore the default serial timeout
558
            radio.pipe.timeout = STIMEOUT
559

    
560
    # DEBUG
561
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
562

    
563
    return True
564

    
565

    
566
def _download(radio):
567
    """Get the memory map"""
568

    
569
    # UI progress
570
    status = chirp_common.Status()
571

    
572
    # put radio in program mode and identify it
573
    _do_ident(radio, status)
574

    
575
    # the models that doesn't have the extra ID have to make a dummy read here
576
    if radio._id2 is False:
577
        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
578
        discard = _rawrecv(radio, BLOCK_SIZE + 5)
579

    
580
        if debug is True:
581
            LOG.info("Dummy first block read done, got this:\n\n %s",
582
                     util.hexprint(discard))
583

    
584
    # reset the progress bar in the UI
585
    status.max = MEM_SIZE / BLOCK_SIZE
586
    status.msg = "Cloning from radio..."
587
    status.cur = 0
588
    radio.status_fn(status)
589

    
590
    # cleaning the serial buffer
591
    _clean_buffer(radio)
592

    
593
    data = ""
594
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
595
        # sending the read request
596
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
597

    
598
        # read
599
        d = _recv(radio, addr)
600

    
601
        # aggregate the data
602
        data += d
603

    
604
        # UI Update
605
        status.cur = addr / BLOCK_SIZE
606
        status.msg = "Cloning from radio..."
607
        radio.status_fn(status)
608

    
609
    return data
610

    
611

    
612
def _upload(radio):
613
    """Upload procedure"""
614

    
615
    # The UPLOAD mem is restricted to lower than 0x3100,
616
    # so we will overide that here localy
617
    MEM_SIZE = radio.UPLOAD_MEM_SIZE
618

    
619
    # UI progress
620
    status = chirp_common.Status()
621

    
622
    # put radio in program mode and identify it
623
    _do_ident(radio, status, True)
624

    
625
    # get the data to upload to radio
626
    data = radio.get_mmap()
627

    
628
    # Reset the UI progress
629
    status.max = MEM_SIZE / TX_BLOCK_SIZE
630
    status.cur = 0
631
    status.msg = "Cloning to radio..."
632
    radio.status_fn(status)
633

    
634
    # the radios that doesn't have the extra ID 'may' do a dummy write, I found
635
    # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
636
    # dummy write is accepted (it actually writes to the mem!) by the radio.
637

    
638
    # cleaning the serial buffer
639
    _clean_buffer(radio)
640

    
641
    # the fun start here
642
    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
643
        # getting the block of data to send
644
        d = data[addr:addr + TX_BLOCK_SIZE]
645

    
646
        # build the frame to send
647
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
648

    
649
        # first block must not send the ACK at the beginning for the
650
        # ones that has the extra id, since this have to do a extra step
651
        if addr == 0 and radio._id2 is not False:
652
            frame = frame[1:]
653

    
654
        # send the frame
655
        _send(radio, frame)
656

    
657
        # receiving the response
658
        ack = _rawrecv(radio, 1)
659

    
660
        # basic check
661
        if len(ack) != 1:
662
            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
663

    
664
        if ack not in "\x06\x05":
665
            raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
666

    
667
        # UI Update
668
        status.cur = addr / TX_BLOCK_SIZE
669
        status.msg = "Cloning to radio..."
670
        radio.status_fn(status)
671

    
672

    
673
def model_match(cls, data):
674
    """Match the opened/downloaded image to the correct version"""
675
    rid = data[0x3f70:0x3f76]
676

    
677
    if rid in cls._fileid:
678
        return True
679

    
680
    return False
681

    
682

    
683
def _decode_ranges(low, high):
684
    """Unpack the data in the ranges zones in the memmap and return
685
    a tuple with the integer corresponding to the Mhz it means"""
686
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
687
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
688
    ilow *= 1000000
689
    ihigh *= 1000000
690

    
691
    return (ilow, ihigh)
692

    
693

    
694
def _split(rf, f1, f2):
695
    """Returns False if the two freqs are in the same band (no split)
696
    or True otherwise"""
697

    
698
    # determine if the two freqs are in the same band
699
    for low, high in rf.valid_bands:
700
        if f1 >= low and f1 <= high and \
701
                f2 >= low and f2 <= high:
702
            # if the two freqs are on the same Band this is not a split
703
            return False
704

    
705
    # if you get here is because the freq pairs are split
706
    return True
707

    
708

    
709
class BTechMobileCommon(chirp_common.CloneModeRadio,
710
                        chirp_common.ExperimentalRadio):
711
    """BTECH's UV-5001 and alike radios"""
712
    VENDOR = "BTECH"
713
    MODEL = ""
714
    IDENT = ""
715
    BANDS = 2
716
    COLOR_LCD = False
717
    COLOR_LCD2 = False
718
    COLOR_LCD3 = False
719
    NAME_LENGTH = 6
720
    UPLOAD_MEM_SIZE = 0X3100
721
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
722
                     chirp_common.PowerLevel("Low", watts=10)]
723
    _vhf_range = (130000000, 180000000)
724
    _220_range = (200000000, 271000000)
725
    _uhf_range = (400000000, 521000000)
726
    _350_range = (350000000, 391000000)
727
    _upper = 199
728
    _magic = MSTRING
729
    _fileid = None
730
    _id2 = False
731
    btech3 = False
732
    _gmrs = False
733

    
734
    @classmethod
735
    def get_prompts(cls):
736
        rp = chirp_common.RadioPrompts()
737
        rp.experimental = \
738
            ('This driver is experimental.\n'
739
             '\n'
740
             'Please keep a copy of your memories with the original software '
741
             'if you treasure them, this driver is new and may contain'
742
             ' bugs.\n'
743
             '\n'
744
             )
745
        rp.pre_download = _(dedent("""\
746
            Follow these instructions to download your info:
747

    
748
            1 - Turn off your radio
749
            2 - Connect your interface cable
750
            3 - Turn on your radio
751
            4 - Do the download of your radio data
752

    
753
            """))
754
        rp.pre_upload = _(dedent("""\
755
            Follow these instructions to upload your info:
756

    
757
            1 - Turn off your radio
758
            2 - Connect your interface cable
759
            3 - Turn on your radio
760
            4 - Do the upload of your radio data
761

    
762
            """))
763
        return rp
764

    
765
    def get_features(self):
766
        """Get the radio's features"""
767

    
768
        # we will use the following var as global
769
        global POWER_LEVELS
770

    
771
        rf = chirp_common.RadioFeatures()
772
        rf.has_settings = True
773
        rf.has_bank = False
774
        rf.has_tuning_step = False
775
        rf.can_odd_split = True
776
        rf.has_name = True
777
        rf.has_offset = True
778
        rf.has_mode = True
779
        rf.has_dtcs = True
780
        rf.has_rx_dtcs = True
781
        rf.has_dtcs_polarity = True
782
        rf.has_ctone = True
783
        rf.has_cross = True
784
        rf.valid_modes = MODES
785
        rf.valid_characters = VALID_CHARS
786
        rf.valid_name_length = self.NAME_LENGTH
787
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
788
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
789
        rf.valid_cross_modes = [
790
            "Tone->Tone",
791
            "DTCS->",
792
            "->DTCS",
793
            "Tone->DTCS",
794
            "DTCS->Tone",
795
            "->Tone",
796
            "DTCS->DTCS"]
797
        rf.valid_skips = SKIP_VALUES
798
        rf.valid_dtcs_codes = DTCS
799
        rf.valid_tuning_steps = STEPS
800
        rf.memory_bounds = (0, self._upper)
801

    
802
        # power levels
803
        POWER_LEVELS = self._power_levels
804
        rf.valid_power_levels = POWER_LEVELS
805

    
806
        # normal dual bands
807
        rf.valid_bands = [self._vhf_range, self._uhf_range]
808

    
809
        # 220 band
810
        if self.BANDS == 3 or self.BANDS == 4:
811
            rf.valid_bands.append(self._220_range)
812

    
813
        # 350 band
814
        if self.BANDS == 4:
815
            rf.valid_bands.append(self._350_range)
816

    
817
        return rf
818

    
819
    def validate_memory(self, mem):
820
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
821

    
822
        _msg_duplex1 = 'Memory location only supports "Low"'
823
        _msg_duplex2 = 'Memory location only supports "off"'
824
        _msg_duplex3 = 'Memory location only supports "(None)", "+" or "off"'
825

    
826
        if self._gmrs:
827
            if mem.number < 1 or mem.number > 30:
828
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
829
                    if mem.duplex not in ['', 'off']:
830
                        # warn user wrong Duplex
831
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
832
                    if mem.power != self._power_levels[2]:
833
                        # warn user wrong Duplex
834
                        msgs.append(chirp_common.ValidationError(_msg_duplex1))
835

    
836
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
837
                    if mem.duplex not in ['off', ]:
838
                        # warn user wrong Duplex
839
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
840

    
841
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
842
                    if mem.duplex not in ['', '+', 'off']:
843
                        # warn user wrong Duplex
844
                        msgs.append(chirp_common.ValidationError(_msg_duplex3))
845

    
846
        return msgs
847

    
848
    def sync_in(self):
849
        """Download from radio"""
850
        data = _download(self)
851
        self._mmap = memmap.MemoryMap(data)
852
        self.process_mmap()
853

    
854
    def sync_out(self):
855
        """Upload to radio"""
856
        try:
857
            _upload(self)
858
        except errors.RadioError:
859
            raise
860
        except Exception, e:
861
            raise errors.RadioError("Error: %s" % e)
862

    
863
    def get_raw_memory(self, number):
864
        return repr(self._memobj.memory[number])
865

    
866
    def _decode_tone(self, val):
867
        """Parse the tone data to decode from mem, it returns:
868
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
869
        pol = None
870

    
871
        if val in [0, 65535]:
872
            return '', None, None
873
        elif val > 0x0258:
874
            a = val / 10.0
875
            return 'Tone', a, pol
876
        else:
877
            if val > 0x69:
878
                index = val - 0x6A
879
                pol = "R"
880
            else:
881
                index = val - 1
882
                pol = "N"
883

    
884
            tone = DTCS[index]
885
            return 'DTCS', tone, pol
886

    
887
    def _encode_tone(self, memval, mode, val, pol):
888
        """Parse the tone data to encode from UI to mem"""
889
        if mode == '' or mode is None:
890
            memval.set_raw("\x00\x00")
891
        elif mode == 'Tone':
892
            memval.set_value(val * 10)
893
        elif mode == 'DTCS':
894
            # detect the index in the DTCS list
895
            try:
896
                index = DTCS.index(val)
897
                if pol == "N":
898
                    index += 1
899
                else:
900
                    index += 0x6A
901
                memval.set_value(index)
902
            except:
903
                msg = "Digital Tone '%d' is not supported" % value
904
                LOG.error(msg)
905
                raise errors.RadioError(msg)
906
        else:
907
            msg = "Internal error: invalid mode '%s'" % mode
908
            LOG.error(msg)
909
            raise errors.InvalidDataError(msg)
910

    
911
    def get_memory(self, number):
912
        """Get the mem representation from the radio image"""
913
        _mem = self._memobj.memory[number]
914
        _names = self._memobj.names[number]
915

    
916
        # Create a high-level memory object to return to the UI
917
        mem = chirp_common.Memory()
918

    
919
        # Memory number
920
        mem.number = number
921

    
922
        if _mem.get_raw()[0] == "\xFF":
923
            mem.empty = True
924
            return mem
925

    
926
        # Freq and offset
927
        mem.freq = int(_mem.rxfreq) * 10
928
        # tx freq can be blank
929
        if _mem.get_raw()[4] == "\xFF":
930
            # TX freq not set
931
            mem.offset = 0
932
            mem.duplex = "off"
933
        else:
934
            # TX freq set
935
            offset = (int(_mem.txfreq) * 10) - mem.freq
936
            if offset != 0:
937
                if _split(self.get_features(), mem.freq, int(
938
                          _mem.txfreq) * 10):
939
                    mem.duplex = "split"
940
                    mem.offset = int(_mem.txfreq) * 10
941
                elif offset < 0:
942
                    mem.offset = abs(offset)
943
                    mem.duplex = "-"
944
                elif offset > 0:
945
                    mem.offset = offset
946
                    mem.duplex = "+"
947
            else:
948
                mem.offset = 0
949

    
950
        # name TAG of the channel
951
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
952

    
953
        # power
954
        mem.power = POWER_LEVELS[int(_mem.power)]
955

    
956
        # wide/narrow
957
        mem.mode = MODES[int(_mem.wide)]
958

    
959
        # skip
960
        mem.skip = SKIP_VALUES[_mem.add]
961

    
962
        # tone data
963
        rxtone = txtone = None
964
        txtone = self._decode_tone(_mem.txtone)
965
        rxtone = self._decode_tone(_mem.rxtone)
966
        chirp_common.split_tone_decode(mem, txtone, rxtone)
967

    
968
        # Extra
969
        mem.extra = RadioSettingGroup("extra", "Extra")
970

    
971
        if not self.COLOR_LCD or \
972
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
973
            scramble = RadioSetting("scramble", "Scramble",
974
                                    RadioSettingValueBoolean(bool(
975
                                        _mem.scramble)))
976
            mem.extra.append(scramble)
977

    
978
        bcl = RadioSetting("bcl", "Busy channel lockout",
979
                           RadioSettingValueBoolean(bool(_mem.bcl)))
980
        mem.extra.append(bcl)
981

    
982
        pttid = RadioSetting("pttid", "PTT ID",
983
                             RadioSettingValueList(PTTID_LIST,
984
                                                   PTTID_LIST[_mem.pttid]))
985
        mem.extra.append(pttid)
986

    
987
        # validating scode
988
        scode = _mem.scode if _mem.scode != 15 else 0
989
        pttidcode = RadioSetting("scode", "PTT ID signal code",
990
                                 RadioSettingValueList(
991
                                     PTTIDCODE_LIST,
992
                                     PTTIDCODE_LIST[scode]))
993
        mem.extra.append(pttidcode)
994

    
995
        optsig = RadioSetting("optsig", "Optional signaling",
996
                              RadioSettingValueList(
997
                                  OPTSIG_LIST,
998
                                  OPTSIG_LIST[_mem.optsig]))
999
        mem.extra.append(optsig)
1000

    
1001
        spmute = RadioSetting("spmute", "Speaker mute",
1002
                              RadioSettingValueList(
1003
                                  SPMUTE_LIST,
1004
                                  SPMUTE_LIST[_mem.spmute]))
1005
        mem.extra.append(spmute)
1006

    
1007
        return mem
1008

    
1009
    def set_memory(self, mem):
1010
        """Set the memory data in the eeprom img from the UI"""
1011
        # get the eprom representation of this channel
1012
        _mem = self._memobj.memory[mem.number]
1013
        _names = self._memobj.names[mem.number]
1014

    
1015
        mem_was_empty = False
1016
        # same method as used in get_memory for determining if mem is empty
1017
        # doing this BEFORE overwriting it with new values ...
1018
        if _mem.get_raw()[0] == "\xFF":
1019
            LOG.debug("This mem was empty before")
1020
            mem_was_empty = True
1021

    
1022
        # if empty memmory
1023
        if mem.empty:
1024
            # the channel itself
1025
            _mem.set_raw("\xFF" * 16)
1026
            # the name tag
1027
            _names.set_raw("\xFF" * 16)
1028
            return
1029

    
1030
        if mem_was_empty:
1031
            # Zero the whole memory if we're making it unempty for
1032
            # the first time
1033
            LOG.debug('Zeroing new memory')
1034
            _mem.set_raw('\x00' * 16)
1035

    
1036
        if self._gmrs:
1037
            if mem.number >= 1 and mem.number <= 30:
1038
                GMRS_FREQ = int(GMRS_FREQS[mem.number - 1] * 1000000)
1039
                mem.freq = GMRS_FREQ
1040
                if mem.number <= 22:
1041
                    mem.duplex = ''
1042
                    mem.offset = 0
1043
                    if mem.number <= 7:
1044
                        mem.power = self._power_levels[2]
1045
                    if mem.number >= 8 and mem.number <= 14:
1046
                        mem.duplex = 'off'
1047
                        mem.mode = "NFM"
1048
                        mem.power = self._power_levels[2]
1049
                if mem.number > 22:
1050
                    mem.duplex = '+'
1051
                    mem.offset = 5000000
1052
            elif float(mem.freq) / 1000000 in GMRS_FREQS:
1053
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
1054
                    mem.duplex = ''
1055
                    mem.offset = 0
1056
                    mem.power = self._power_levels[2]
1057
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
1058
                    mem.duplex = 'off'
1059
                    mem.offset = 0
1060
                    mem.mode = "NFM"
1061
                    mem.power = self._power_levels[2]
1062
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
1063
                    if mem.duplex == '+':
1064
                        mem.offset = 5000000
1065
                    else:
1066
                        mem.offset = 0
1067
            else:
1068
                mem.duplex = 'off'
1069
                mem.offset = 0
1070

    
1071
        # frequency
1072
        _mem.rxfreq = mem.freq / 10
1073

    
1074
        # duplex
1075
        if mem.duplex == "+":
1076
            _mem.txfreq = (mem.freq + mem.offset) / 10
1077
        elif mem.duplex == "-":
1078
            _mem.txfreq = (mem.freq - mem.offset) / 10
1079
        elif mem.duplex == "off":
1080
            for i in _mem.txfreq:
1081
                i.set_raw("\xFF")
1082
        elif mem.duplex == "split":
1083
            _mem.txfreq = mem.offset / 10
1084
        else:
1085
            _mem.txfreq = mem.freq / 10
1086

    
1087
        # tone data
1088
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
1089
            chirp_common.split_tone_encode(mem)
1090
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
1091
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
1092

    
1093
        # name TAG of the channel
1094
        if len(mem.name) < self.NAME_LENGTH:
1095
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
1096
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
1097
        _names.name = str(mem.name).replace(" ", "\xFF")
1098

    
1099
        # power, # default power level is high
1100
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
1101

    
1102
        # wide/narrow
1103
        _mem.wide = MODES.index(mem.mode)
1104

    
1105
        # scan add property
1106
        _mem.add = SKIP_VALUES.index(mem.skip)
1107

    
1108
        # reseting unknowns, this have to be set by hand
1109
        _mem.unknown0 = 0
1110
        _mem.unknown1 = 0
1111
        _mem.unknown2 = 0
1112
        _mem.unknown3 = 0
1113
        _mem.unknown4 = 0
1114
        _mem.unknown5 = 0
1115
        _mem.unknown6 = 0
1116

    
1117
        def _zero_settings():
1118
            _mem.spmute = 0
1119
            _mem.optsig = 0
1120
            _mem.scramble = 0
1121
            _mem.bcl = 0
1122
            _mem.pttid = 0
1123
            _mem.scode = 0
1124

    
1125
        if self.COLOR_LCD and _mem.scramble:
1126
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1127
            _mem.scramble = 0
1128

    
1129
        # extra settings
1130
        if len(mem.extra) > 0:
1131
            # there are setting, parse
1132
            LOG.debug("Extra-Setting supplied. Setting them.")
1133
            # Zero them all first so any not provided by model don't
1134
            # stay set
1135
            _zero_settings()
1136
            for setting in mem.extra:
1137
                setattr(_mem, setting.get_name(), setting.value)
1138
        else:
1139
            if mem.empty:
1140
                LOG.debug("New mem is empty.")
1141
            else:
1142
                LOG.debug("New mem is NOT empty")
1143
                # set extra-settings to default ONLY when apreviously empty or
1144
                # deleted memory was edited to prevent errors such as #4121
1145
                if mem_was_empty:
1146
                    LOG.debug("old mem was empty. Setting default for extras.")
1147
                    _zero_settings()
1148

    
1149
        return mem
1150

    
1151
    def get_settings(self):
1152
        """Translate the bit in the mem_struct into settings in the UI"""
1153
        _mem = self._memobj
1154
        basic = RadioSettingGroup("basic", "Basic Settings")
1155
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1156
        other = RadioSettingGroup("other", "Other Settings")
1157
        work = RadioSettingGroup("work", "Work Mode Settings")
1158
        top = RadioSettings(basic, advanced, other, work)
1159

    
1160
        # Basic
1161
        if self.COLOR_LCD:
1162
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1163
                               RadioSettingValueList(
1164
                                   self.LIST_TMR,
1165
                                   self.LIST_TMR[_mem.settings.tmr]))
1166
            basic.append(tmr)
1167
        else:
1168
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1169
                               RadioSettingValueBoolean(_mem.settings.tdr))
1170
            basic.append(tdr)
1171

    
1172
        sql = RadioSetting("settings.sql", "Squelch level",
1173
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1174
        basic.append(sql)
1175

    
1176
        if self.MODEL == "GMRS-50X1":
1177
            autolk = RadioSetting("settings.autolk", "Auto keylock",
1178
                                  RadioSettingValueBoolean(
1179
                                      _mem.settings.autolk))
1180
            basic.append(autolk)
1181

    
1182
        if self.MODEL == "DB25-G":
1183
            rs = RadioSettingValueInteger(0, 127, _mem.settings.mgain2)
1184
            mgain2 = RadioSetting("settings.mgain2", "Mic gain", rs)
1185
            basic.append(mgain2)
1186

    
1187
        tot = RadioSetting("settings.tot", "Time out timer",
1188
                           RadioSettingValueList(
1189
                               LIST_TOT,
1190
                               LIST_TOT[_mem.settings.tot]))
1191
        basic.append(tot)
1192

    
1193
        if self.MODEL == "KT-8R":
1194
                save = RadioSetting("settings.save", "Battery Save",
1195
                                    RadioSettingValueBoolean(
1196
                                        _mem.settings.save))
1197
                basic.append(save)
1198

    
1199
        if not self.MODEL == "KT-8R":
1200
            if self.VENDOR == "BTECH" or self.COLOR_LCD:
1201
                apo = RadioSetting("settings.apo", "Auto power off timer",
1202
                                   RadioSettingValueList(
1203
                                       LIST_APO,
1204
                                       LIST_APO[_mem.settings.apo]))
1205
                basic.append(apo)
1206
            else:
1207
                toa = RadioSetting("settings.apo", "Time out alert timer",
1208
                                   RadioSettingValueList(
1209
                                       LIST_OFF1TO10,
1210
                                       LIST_OFF1TO10[_mem.settings.apo]))
1211
                basic.append(toa)
1212

    
1213
        abr = RadioSetting("settings.abr", "Backlight timer",
1214
                           RadioSettingValueList(
1215
                               LIST_OFF1TO50,
1216
                               LIST_OFF1TO50[_mem.settings.abr]))
1217
        basic.append(abr)
1218

    
1219
        beep = RadioSetting("settings.beep", "Key beep",
1220
                            RadioSettingValueBoolean(_mem.settings.beep))
1221
        basic.append(beep)
1222

    
1223
        if self.MODEL == "KT-8R":
1224
                dsub = RadioSetting("settings.dsub", "CTCSS/DCS code display",
1225
                                    RadioSettingValueBoolean(
1226
                                        _mem.settings.dsub))
1227
                basic.append(dsub)
1228

    
1229
        if self.MODEL == "KT-8R":
1230
                dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1231
                                      RadioSettingValueBoolean(
1232
                                          _mem.settings.dtmfst))
1233
                basic.append(dtmfst)
1234
        else:
1235
            dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1236
                                  RadioSettingValueList(
1237
                                      LIST_DTMFST,
1238
                                      LIST_DTMFST[_mem.settings.dtmfst]))
1239
            basic.append(dtmfst)
1240

    
1241
        if not self.COLOR_LCD:
1242
            prisc = RadioSetting("settings.prisc", "Priority scan",
1243
                                 RadioSettingValueBoolean(
1244
                                     _mem.settings.prisc))
1245
            basic.append(prisc)
1246

    
1247
            prich = RadioSetting("settings.prich", "Priority channel",
1248
                                 RadioSettingValueInteger(0, self._upper,
1249
                                                          _mem.settings.prich))
1250
            basic.append(prich)
1251

    
1252
        screv = RadioSetting("settings.screv", "Scan resume method",
1253
                             RadioSettingValueList(
1254
                                 LIST_SCREV,
1255
                                 LIST_SCREV[_mem.settings.screv]))
1256
        basic.append(screv)
1257

    
1258
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1259
                             RadioSettingValueInteger(0, 30,
1260
                                                      _mem.settings.pttlt))
1261
        basic.append(pttlt)
1262

    
1263
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1264
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1265
                                 RadioSettingValueList(
1266
                                     LIST_EMCTPX,
1267
                                     LIST_EMCTPX[_mem.settings.emctp]))
1268
            basic.append(emctp)
1269
        else:
1270
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1271
                                 RadioSettingValueList(
1272
                                     LIST_EMCTP,
1273
                                     LIST_EMCTP[_mem.settings.emctp]))
1274
            basic.append(emctp)
1275

    
1276
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1277
                             RadioSettingValueInteger(0, self._upper,
1278
                                                      _mem.settings.emcch))
1279
        basic.append(emcch)
1280

    
1281
        if self.COLOR_LCD:
1282
            if _mem.settings.sigbp > 0x01:
1283
                val = 0x00
1284
            else:
1285
                val = _mem.settings.sigbp
1286
            sigbp = RadioSetting("settings.sigbp", "Signal beep",
1287
                                 RadioSettingValueBoolean(val))
1288
            basic.append(sigbp)
1289
        else:
1290
            ringt = RadioSetting("settings.ringt", "Ring time",
1291
                                 RadioSettingValueList(
1292
                                     LIST_OFF1TO9,
1293
                                     LIST_OFF1TO9[_mem.settings.ringt]))
1294
            basic.append(ringt)
1295

    
1296
        camdf = RadioSetting("settings.camdf", "Display mode A",
1297
                             RadioSettingValueList(
1298
                                 LIST_MDF,
1299
                                 LIST_MDF[_mem.settings.camdf]))
1300
        basic.append(camdf)
1301

    
1302
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1303
                             RadioSettingValueList(
1304
                                 LIST_MDF,
1305
                                 LIST_MDF[_mem.settings.cbmdf]))
1306
        basic.append(cbmdf)
1307

    
1308
        if self.COLOR_LCD:
1309
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1310
                                 RadioSettingValueList(
1311
                                     LIST_MDF,
1312
                                     LIST_MDF[_mem.settings.ccmdf]))
1313
            basic.append(ccmdf)
1314

    
1315
            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1316
                                 RadioSettingValueList(
1317
                                     LIST_MDF,
1318
                                     LIST_MDF[_mem.settings.cdmdf]))
1319
            basic.append(cdmdf)
1320

    
1321
            langua = RadioSetting("settings.langua", "Language",
1322
                                  RadioSettingValueList(
1323
                                      LIST_LANGUA,
1324
                                      LIST_LANGUA[_mem.settings.langua]))
1325
            basic.append(langua)
1326

    
1327
        if self.MODEL == "KT-8R":
1328
            voice = RadioSetting("settings.voice", "Voice prompt",
1329
                                 RadioSettingValueList(
1330
                                     LIST_VOICE,
1331
                                     LIST_VOICE[_mem.settings.voice]))
1332
            basic.append(voice)
1333

    
1334
            vox = RadioSetting("settings.vox", "VOX",
1335
                               RadioSettingValueList(
1336
                                   LIST_VOX,
1337
                                   LIST_VOX[_mem.settings.vox]))
1338
            basic.append(vox)
1339

    
1340
            voxt = RadioSetting("settings.voxt", "VOX delay time",
1341
                                RadioSettingValueList(
1342
                                    LIST_VOXT,
1343
                                    LIST_VOXT[_mem.settings.voxt]))
1344
            basic.append(voxt)
1345

    
1346
        if self.VENDOR == "BTECH":
1347
            if self.COLOR_LCD:
1348
                sync = RadioSetting("settings.sync", "Channel display sync",
1349
                                    RadioSettingValueList(
1350
                                        LIST_SYNC,
1351
                                        LIST_SYNC[_mem.settings.sync]))
1352
                basic.append(sync)
1353
            else:
1354
                sync = RadioSetting("settings.sync", "A/B channel sync",
1355
                                    RadioSettingValueBoolean(
1356
                                        _mem.settings.sync))
1357
                basic.append(sync)
1358
        else:
1359
            autolk = RadioSetting("settings.sync", "Auto keylock",
1360
                                  RadioSettingValueBoolean(
1361
                                      _mem.settings.sync))
1362
            basic.append(autolk)
1363

    
1364
        if not self.COLOR_LCD:
1365
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1366
                                  RadioSettingValueList(
1367
                                      LIST_PONMSG,
1368
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1369
            basic.append(ponmsg)
1370

    
1371
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3):
1372
            mainfc = RadioSetting("settings.mainfc",
1373
                                  "Main LCD foreground color",
1374
                                  RadioSettingValueList(
1375
                                      LIST_COLOR9,
1376
                                      LIST_COLOR9[_mem.settings.mainfc]))
1377
            basic.append(mainfc)
1378

    
1379
            mainbc = RadioSetting("settings.mainbc",
1380
                                  "Main LCD background color",
1381
                                  RadioSettingValueList(
1382
                                      LIST_COLOR9,
1383
                                      LIST_COLOR9[_mem.settings.mainbc]))
1384
            basic.append(mainbc)
1385

    
1386
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1387
                                  RadioSettingValueList(
1388
                                      LIST_COLOR9,
1389
                                      LIST_COLOR9[_mem.settings.menufc]))
1390
            basic.append(menufc)
1391

    
1392
            menubc = RadioSetting("settings.menubc", "Menu background color",
1393
                                  RadioSettingValueList(
1394
                                      LIST_COLOR9,
1395
                                      LIST_COLOR9[_mem.settings.menubc]))
1396
            basic.append(menubc)
1397

    
1398
            stafc = RadioSetting("settings.stafc",
1399
                                 "Top status foreground color",
1400
                                 RadioSettingValueList(
1401
                                     LIST_COLOR9,
1402
                                     LIST_COLOR9[_mem.settings.stafc]))
1403
            basic.append(stafc)
1404

    
1405
            stabc = RadioSetting("settings.stabc",
1406
                                 "Top status background color",
1407
                                 RadioSettingValueList(
1408
                                     LIST_COLOR9,
1409
                                     LIST_COLOR9[_mem.settings.stabc]))
1410
            basic.append(stabc)
1411

    
1412
            sigfc = RadioSetting("settings.sigfc",
1413
                                 "Bottom status foreground color",
1414
                                 RadioSettingValueList(
1415
                                     LIST_COLOR9,
1416
                                     LIST_COLOR9[_mem.settings.sigfc]))
1417
            basic.append(sigfc)
1418

    
1419
            sigbc = RadioSetting("settings.sigbc",
1420
                                 "Bottom status background color",
1421
                                 RadioSettingValueList(
1422
                                     LIST_COLOR9,
1423
                                     LIST_COLOR9[_mem.settings.sigbc]))
1424
            basic.append(sigbc)
1425

    
1426
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1427
                                RadioSettingValueList(
1428
                                    LIST_COLOR9,
1429
                                    LIST_COLOR9[_mem.settings.rxfc]))
1430
            basic.append(rxfc)
1431

    
1432
            txfc = RadioSetting("settings.txfc",
1433
                                "Transmitting character color",
1434
                                RadioSettingValueList(
1435
                                    LIST_COLOR9,
1436
                                    LIST_COLOR9[_mem.settings.txfc]))
1437
            basic.append(txfc)
1438

    
1439
            txdisp = RadioSetting("settings.txdisp",
1440
                                  "Transmitting status display",
1441
                                  RadioSettingValueList(
1442
                                      LIST_TXDISP,
1443
                                      LIST_TXDISP[_mem.settings.txdisp]))
1444
            basic.append(txdisp)
1445
        elif self.COLOR_LCD2 or self.COLOR_LCD3:
1446
            stfc = RadioSetting("settings.stfc",
1447
                                "ST-FC",
1448
                                RadioSettingValueList(
1449
                                    LIST_COLOR8,
1450
                                    LIST_COLOR8[_mem.settings.stfc]))
1451
            basic.append(stfc)
1452

    
1453
            mffc = RadioSetting("settings.mffc",
1454
                                "MF-FC",
1455
                                RadioSettingValueList(
1456
                                    LIST_COLOR8,
1457
                                    LIST_COLOR8[_mem.settings.mffc]))
1458
            basic.append(mffc)
1459

    
1460
            sfafc = RadioSetting("settings.sfafc",
1461
                                 "SFA-FC",
1462
                                 RadioSettingValueList(
1463
                                     LIST_COLOR8,
1464
                                     LIST_COLOR8[_mem.settings.sfafc]))
1465
            basic.append(sfafc)
1466

    
1467
            sfbfc = RadioSetting("settings.sfbfc",
1468
                                 "SFB-FC",
1469
                                 RadioSettingValueList(
1470
                                     LIST_COLOR8,
1471
                                     LIST_COLOR8[_mem.settings.sfbfc]))
1472
            basic.append(sfbfc)
1473

    
1474
            sfcfc = RadioSetting("settings.sfcfc",
1475
                                 "SFC-FC",
1476
                                 RadioSettingValueList(
1477
                                     LIST_COLOR8,
1478
                                     LIST_COLOR8[_mem.settings.sfcfc]))
1479
            basic.append(sfcfc)
1480

    
1481
            sfdfc = RadioSetting("settings.sfdfc",
1482
                                 "SFD-FC",
1483
                                 RadioSettingValueList(
1484
                                     LIST_COLOR8,
1485
                                     LIST_COLOR8[_mem.settings.sfdfc]))
1486
            basic.append(sfdfc)
1487

    
1488
            subfc = RadioSetting("settings.subfc",
1489
                                 "SUB-FC",
1490
                                 RadioSettingValueList(
1491
                                     LIST_COLOR8,
1492
                                     LIST_COLOR8[_mem.settings.subfc]))
1493
            basic.append(subfc)
1494

    
1495
            fmfc = RadioSetting("settings.fmfc",
1496
                                "FM-FC",
1497
                                RadioSettingValueList(
1498
                                    LIST_COLOR8,
1499
                                    LIST_COLOR8[_mem.settings.fmfc]))
1500
            basic.append(fmfc)
1501

    
1502
            sigfc = RadioSetting("settings.sigfc",
1503
                                 "SIG-FC",
1504
                                 RadioSettingValueList(
1505
                                     LIST_COLOR8,
1506
                                     LIST_COLOR8[_mem.settings.sigfc]))
1507
            basic.append(sigfc)
1508

    
1509
            if not self.MODEL == "KT-8R":
1510
                modfc = RadioSetting("settings.modfc",
1511
                                     "MOD-FC",
1512
                                     RadioSettingValueList(
1513
                                         LIST_COLOR8,
1514
                                         LIST_COLOR8[_mem.settings.modfc]))
1515
                basic.append(modfc)
1516

    
1517
            menufc = RadioSetting("settings.menufc",
1518
                                  "MENUFC",
1519
                                  RadioSettingValueList(
1520
                                      LIST_COLOR8,
1521
                                      LIST_COLOR8[_mem.settings.menufc]))
1522
            basic.append(menufc)
1523

    
1524
            txfc = RadioSetting("settings.txfc",
1525
                                "TX-FC",
1526
                                RadioSettingValueList(
1527
                                    LIST_COLOR8,
1528
                                    LIST_COLOR8[_mem.settings.txfc]))
1529
            basic.append(txfc)
1530

    
1531
            if self.MODEL == "KT-8R":
1532
                rxfc = RadioSetting("settings.rxfc",
1533
                                    "RX-FC",
1534
                                    RadioSettingValueList(
1535
                                        LIST_COLOR8,
1536
                                        LIST_COLOR8[_mem.settings.rxfc]))
1537
                basic.append(rxfc)
1538

    
1539
            if not self.MODEL == "KT-8R":
1540
                txdisp = RadioSetting("settings.txdisp",
1541
                                      "Transmitting status display",
1542
                                      RadioSettingValueList(
1543
                                          LIST_TXDISP,
1544
                                          LIST_TXDISP[_mem.settings.txdisp]))
1545
                basic.append(txdisp)
1546
        else:
1547
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1548
                                 RadioSettingValueList(
1549
                                     LIST_COLOR4,
1550
                                     LIST_COLOR4[_mem.settings.wtled]))
1551
            basic.append(wtled)
1552

    
1553
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1554
                                 RadioSettingValueList(
1555
                                     LIST_COLOR4,
1556
                                     LIST_COLOR4[_mem.settings.rxled]))
1557
            basic.append(rxled)
1558

    
1559
            txled = RadioSetting("settings.txled", "TX backlight Color",
1560
                                 RadioSettingValueList(
1561
                                     LIST_COLOR4,
1562
                                     LIST_COLOR4[_mem.settings.txled]))
1563
            basic.append(txled)
1564

    
1565
        anil = RadioSetting("settings.anil", "ANI length",
1566
                            RadioSettingValueList(
1567
                                LIST_ANIL,
1568
                                LIST_ANIL[_mem.settings.anil]))
1569
        basic.append(anil)
1570

    
1571
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1572
                            RadioSettingValueList(
1573
                                LIST_REPS,
1574
                                LIST_REPS[_mem.settings.reps]))
1575
        basic.append(reps)
1576

    
1577
        if not self.MODEL == "GMRS-50X1" and not self.MODEL == "KT-8R":
1578
            repm = RadioSetting("settings.repm", "Relay condition",
1579
                                RadioSettingValueList(
1580
                                    LIST_REPM,
1581
                                    LIST_REPM[_mem.settings.repm]))
1582
            basic.append(repm)
1583

    
1584
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1585
            if self.COLOR_LCD:
1586
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1587
                                     RadioSettingValueList(
1588
                                         LIST_OFF1TO50,
1589
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1590
                basic.append(tmrmr)
1591
            else:
1592
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1593
                                     RadioSettingValueList(
1594
                                         LIST_OFF1TO50,
1595
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1596
                basic.append(tdrab)
1597

    
1598
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1599
                               RadioSettingValueBoolean(_mem.settings.ste))
1600
            basic.append(ste)
1601

    
1602
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1603
                                 RadioSettingValueList(
1604
                                     LIST_OFF1TO9,
1605
                                     LIST_OFF1TO9[_mem.settings.rpste]))
1606
            basic.append(rpste)
1607

    
1608
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1609
                                 RadioSettingValueList(
1610
                                     LIST_RPTDL,
1611
                                     LIST_RPTDL[_mem.settings.rptdl]))
1612
            basic.append(rptdl)
1613

    
1614
        if self.MODEL == "DB25-G":
1615
            mgain = RadioSetting("settings.mgain", "Auto power-on",
1616
                                 RadioSettingValueBoolean(_mem.settings.mgain))
1617
            basic.append(mgain)
1618

    
1619
        if str(_mem.fingerprint.fp) in BTECH3:
1620
            mgain = RadioSetting("settings.mgain", "Mic gain",
1621
                                 RadioSettingValueInteger(0, 120,
1622
                                                          _mem.settings.mgain))
1623
            basic.append(mgain)
1624

    
1625
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1626
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1627
                                 RadioSettingValueInteger(0, 60,
1628
                                                          _mem.settings.dtmfg))
1629
            basic.append(dtmfg)
1630

    
1631
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1632
            mgain = RadioSetting("settings.mgain", "Mic gain",
1633
                                 RadioSettingValueInteger(0, 120,
1634
                                                          _mem.settings.mgain))
1635
            basic.append(mgain)
1636

    
1637
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1638
                                  RadioSettingValueList(
1639
                                      LIST_SKIPTX,
1640
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1641
            basic.append(skiptx)
1642

    
1643
            scmode = RadioSetting("settings.scmode", "Scan mode",
1644
                                  RadioSettingValueList(
1645
                                      LIST_SCMODE,
1646
                                      LIST_SCMODE[_mem.settings.scmode]))
1647
            basic.append(scmode)
1648

    
1649
        if self.MODEL == "KT-8R":
1650
            tmrtx = RadioSetting("settings.tmrtx", "TX in multi-standby",
1651
                                 RadioSettingValueList(
1652
                                     LIST_TMRTX,
1653
                                     LIST_TMRTX[_mem.settings.tmrtx]))
1654
            basic.append(tmrtx)
1655

    
1656
        # Advanced
1657
        def _filter(name):
1658
            filtered = ""
1659
            for char in str(name):
1660
                if char in VALID_CHARS:
1661
                    filtered += char
1662
                else:
1663
                    filtered += " "
1664
            return filtered
1665

    
1666
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3):
1667
            _msg = self._memobj.poweron_msg
1668
            line1 = RadioSetting("poweron_msg.line1",
1669
                                 "Power-on message line 1",
1670
                                 RadioSettingValueString(0, 8, _filter(
1671
                                                         _msg.line1)))
1672
            advanced.append(line1)
1673
            line2 = RadioSetting("poweron_msg.line2",
1674
                                 "Power-on message line 2",
1675
                                 RadioSettingValueString(0, 8, _filter(
1676
                                                         _msg.line2)))
1677
            advanced.append(line2)
1678
            line3 = RadioSetting("poweron_msg.line3",
1679
                                 "Power-on message line 3",
1680
                                 RadioSettingValueString(0, 8, _filter(
1681
                                                         _msg.line3)))
1682
            advanced.append(line3)
1683
            line4 = RadioSetting("poweron_msg.line4",
1684
                                 "Power-on message line 4",
1685
                                 RadioSettingValueString(0, 8, _filter(
1686
                                                         _msg.line4)))
1687
            advanced.append(line4)
1688
            line5 = RadioSetting("poweron_msg.line5",
1689
                                 "Power-on message line 5",
1690
                                 RadioSettingValueString(0, 8, _filter(
1691
                                                         _msg.line5)))
1692
            advanced.append(line5)
1693
            line6 = RadioSetting("poweron_msg.line6",
1694
                                 "Power-on message line 6",
1695
                                 RadioSettingValueString(0, 8, _filter(
1696
                                                         _msg.line6)))
1697
            advanced.append(line6)
1698
            line7 = RadioSetting("poweron_msg.line7",
1699
                                 "Power-on message line 7",
1700
                                 RadioSettingValueString(0, 8, _filter(
1701
                                                         _msg.line7)))
1702
            advanced.append(line7)
1703
            line8 = RadioSetting("poweron_msg.line8", "Static message",
1704
                                 RadioSettingValueString(0, 8, _filter(
1705
                                                         _msg.line8)))
1706
            advanced.append(line8)
1707
        elif self.COLOR_LCD2 or self.COLOR_LCD3:
1708
            _msg = self._memobj.static_msg
1709
            line = RadioSetting("static_msg.line", "Static message",
1710
                                RadioSettingValueString(0, 16, _filter(
1711
                                    _msg.line)))
1712
            advanced.append(line)
1713
        else:
1714
            _msg = self._memobj.poweron_msg
1715
            line1 = RadioSetting("poweron_msg.line1",
1716
                                 "Power-on message line 1",
1717
                                 RadioSettingValueString(0, 6, _filter(
1718
                                                         _msg.line1)))
1719
            advanced.append(line1)
1720
            line2 = RadioSetting("poweron_msg.line2",
1721
                                 "Power-on message line 2",
1722
                                 RadioSettingValueString(0, 6, _filter(
1723
                                                         _msg.line2)))
1724
            advanced.append(line2)
1725

    
1726
        if self.MODEL in ("UV-2501", "UV-5001"):
1727
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1728
                                   RadioSettingValueBoolean(
1729
                                       _mem.settings2.vfomren))
1730
            advanced.append(vfomren)
1731

    
1732
            reseten = RadioSetting("settings2.reseten", "RESET",
1733
                                   RadioSettingValueBoolean(
1734
                                       _mem.settings2.reseten))
1735
            advanced.append(reseten)
1736

    
1737
            menuen = RadioSetting("settings2.menuen", "Menu",
1738
                                  RadioSettingValueBoolean(
1739
                                      _mem.settings2.menuen))
1740
            advanced.append(menuen)
1741

    
1742
        # Other
1743
        def convert_bytes_to_limit(bytes):
1744
            limit = ""
1745
            for byte in bytes:
1746
                if byte < 10:
1747
                    limit += chr(byte + 0x30)
1748
                else:
1749
                    break
1750
            return limit
1751

    
1752
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1753
            _ranges = self._memobj.ranges220
1754
            ranges = "ranges220"
1755
        else:
1756
            _ranges = self._memobj.ranges
1757
            ranges = "ranges"
1758

    
1759
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1760
        val = RadioSettingValueString(0, 3, _limit)
1761
        val.set_mutable(False)
1762
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1763
        other.append(vhf_low)
1764

    
1765
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1766
        val = RadioSettingValueString(0, 3, _limit)
1767
        val.set_mutable(False)
1768
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1769
        other.append(vhf_high)
1770

    
1771
        if self.BANDS == 3 or self.BANDS == 4:
1772
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1773
            val = RadioSettingValueString(0, 3, _limit)
1774
            val.set_mutable(False)
1775
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1776
            other.append(vhf2_low)
1777

    
1778
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1779
            val = RadioSettingValueString(0, 3, _limit)
1780
            val.set_mutable(False)
1781
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1782
            other.append(vhf2_high)
1783

    
1784
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1785
        val = RadioSettingValueString(0, 3, _limit)
1786
        val.set_mutable(False)
1787
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1788
        other.append(uhf_low)
1789

    
1790
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1791
        val = RadioSettingValueString(0, 3, _limit)
1792
        val.set_mutable(False)
1793
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1794
        other.append(uhf_high)
1795

    
1796
        if self.BANDS == 4:
1797
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1798
            val = RadioSettingValueString(0, 3, _limit)
1799
            val.set_mutable(False)
1800
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1801
            other.append(uhf2_low)
1802

    
1803
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1804
            val = RadioSettingValueString(0, 3, _limit)
1805
            val.set_mutable(False)
1806
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1807
            other.append(uhf2_high)
1808

    
1809
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1810
        val.set_mutable(False)
1811
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1812
        other.append(fp)
1813

    
1814
        # Work
1815
        if self.COLOR_LCD:
1816
            dispab = RadioSetting("settings2.dispab", "Display",
1817
                                  RadioSettingValueList(
1818
                                      LIST_ABCD,
1819
                                      LIST_ABCD[_mem.settings2.dispab]))
1820
            work.append(dispab)
1821
        else:
1822
            dispab = RadioSetting("settings2.dispab", "Display",
1823
                                  RadioSettingValueList(
1824
                                      LIST_AB,
1825
                                      LIST_AB[_mem.settings2.dispab]))
1826
            work.append(dispab)
1827

    
1828
        if self.COLOR_LCD:
1829
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1830
                                  RadioSettingValueList(
1831
                                      LIST_VFOMR,
1832
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1833
            work.append(vfomra)
1834

    
1835
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1836
                                  RadioSettingValueList(
1837
                                      LIST_VFOMR,
1838
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1839
            work.append(vfomrb)
1840

    
1841
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1842
                                  RadioSettingValueList(
1843
                                      LIST_VFOMR,
1844
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1845
            work.append(vfomrc)
1846

    
1847
            vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1848
                                  RadioSettingValueList(
1849
                                      LIST_VFOMR,
1850
                                      LIST_VFOMR[_mem.settings2.vfomrd]))
1851
            work.append(vfomrd)
1852
        else:
1853
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1854
                                 RadioSettingValueList(
1855
                                     LIST_VFOMR,
1856
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1857
            work.append(vfomr)
1858

    
1859
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1860
                               RadioSettingValueBoolean(
1861
                                   _mem.settings2.keylock))
1862
        work.append(keylock)
1863

    
1864
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1865
                             RadioSettingValueInteger(0, self._upper,
1866
                                                      _mem.settings2.mrcha))
1867
        work.append(mrcha)
1868

    
1869
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1870
                             RadioSettingValueInteger(0, self._upper,
1871
                                                      _mem.settings2.mrchb))
1872
        work.append(mrchb)
1873

    
1874
        if self.COLOR_LCD:
1875
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1876
                                 RadioSettingValueInteger(
1877
                                     0, self._upper, _mem.settings2.mrchc))
1878
            work.append(mrchc)
1879

    
1880
            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1881
                                 RadioSettingValueInteger(
1882
                                     0, self._upper, _mem.settings2.mrchd))
1883
            work.append(mrchd)
1884

    
1885
        def convert_bytes_to_freq(bytes):
1886
            real_freq = 0
1887
            for byte in bytes:
1888
                real_freq = (real_freq * 10) + byte
1889
            return chirp_common.format_freq(real_freq * 10)
1890

    
1891
        def my_validate(value):
1892
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1893
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1894
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1895
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1896
            if self.BANDS == 3 or self.BANDS == 4:
1897
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1898
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1899
            if self.BANDS == 4:
1900
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1901
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1902

    
1903
            value = chirp_common.parse_freq(value)
1904
            msg = ("Can't be less then %i.0000")
1905
            if value > 99000000 and value < _vhf_lower * 1000000:
1906
                raise InvalidValueError(msg % (_vhf_lower))
1907
            msg = ("Can't be betweeb %i.9975-%i.0000")
1908
            if self.BANDS == 2:
1909
                if (_vhf_upper + 1) * 1000000 <= value and \
1910
                        value < _uhf_lower * 1000000:
1911
                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
1912
            if self.BANDS == 3:
1913
                if (_vhf_upper + 1) * 1000000 <= value and \
1914
                        value < _vhf2_lower * 1000000:
1915
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1916
                if (_vhf2_upper + 1) * 1000000 <= value and \
1917
                        value < _uhf_lower * 1000000:
1918
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
1919
            if self.BANDS == 4:
1920
                if (_vhf_upper + 1) * 1000000 <= value and \
1921
                        value < _vhf2_lower * 1000000:
1922
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1923
                if (_vhf2_upper + 1) * 1000000 <= value and \
1924
                        value < _uhf2_lower * 1000000:
1925
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
1926
                if (_uhf2_upper + 1) * 1000000 <= value and \
1927
                        value < _uhf_lower * 1000000:
1928
                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
1929
            msg = ("Can't be greater then %i.9975")
1930
            if value > 99000000 and value >= _uhf_upper * 1000000:
1931
                raise InvalidValueError(msg % (_uhf_upper))
1932
            return chirp_common.format_freq(value)
1933

    
1934
        def apply_freq(setting, obj):
1935
            value = chirp_common.parse_freq(str(setting.value)) / 10
1936
            for i in range(7, -1, -1):
1937
                obj.freq[i] = value % 10
1938
                value /= 10
1939

    
1940
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1941
                                        _mem.vfo.a.freq))
1942
        val1a.set_validate_callback(my_validate)
1943
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1944
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1945
        work.append(vfoafreq)
1946

    
1947
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1948
                                        _mem.vfo.b.freq))
1949
        val1b.set_validate_callback(my_validate)
1950
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1951
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1952
        work.append(vfobfreq)
1953

    
1954
        if self.COLOR_LCD:
1955
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1956
                                            _mem.vfo.c.freq))
1957
            val1c.set_validate_callback(my_validate)
1958
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
1959
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
1960
            work.append(vfocfreq)
1961

    
1962
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1963
                                            _mem.vfo.d.freq))
1964
            val1d.set_validate_callback(my_validate)
1965
            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
1966
            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
1967
            work.append(vfodfreq)
1968

    
1969
        if not self.MODEL == "GMRS-50X1":
1970
            vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1971
                                      RadioSettingValueList(
1972
                                          LIST_SHIFT,
1973
                                          LIST_SHIFT[_mem.vfo.a.shiftd]))
1974
            work.append(vfoashiftd)
1975

    
1976
            vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1977
                                      RadioSettingValueList(
1978
                                          LIST_SHIFT,
1979
                                          LIST_SHIFT[_mem.vfo.b.shiftd]))
1980
            work.append(vfobshiftd)
1981

    
1982
            if self.COLOR_LCD:
1983
                vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
1984
                                          RadioSettingValueList(
1985
                                              LIST_SHIFT,
1986
                                              LIST_SHIFT[_mem.vfo.c.shiftd]))
1987
                work.append(vfocshiftd)
1988

    
1989
                vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
1990
                                          RadioSettingValueList(
1991
                                              LIST_SHIFT,
1992
                                              LIST_SHIFT[_mem.vfo.d.shiftd]))
1993
                work.append(vfodshiftd)
1994

    
1995
        def convert_bytes_to_offset(bytes):
1996
            real_offset = 0
1997
            for byte in bytes:
1998
                real_offset = (real_offset * 10) + byte
1999
            return chirp_common.format_freq(real_offset * 1000)
2000

    
2001
        def apply_offset(setting, obj):
2002
            value = chirp_common.parse_freq(str(setting.value)) / 1000
2003
            for i in range(5, -1, -1):
2004
                obj.offset[i] = value % 10
2005
                value /= 10
2006

    
2007
        if not self.MODEL == "GMRS-50X1":
2008
            if self.COLOR_LCD:
2009
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2010
                                                _mem.vfo.a.offset))
2011
                vfoaoffset = RadioSetting("vfo.a.offset",
2012
                                          "VFO A offset (0.000-999.999)",
2013
                                          val1a)
2014
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2015
                work.append(vfoaoffset)
2016

    
2017
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2018
                                                _mem.vfo.b.offset))
2019
                vfoboffset = RadioSetting("vfo.b.offset",
2020
                                          "VFO B offset (0.000-999.999)",
2021
                                          val1b)
2022
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2023
                work.append(vfoboffset)
2024

    
2025
                val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2026
                                                _mem.vfo.c.offset))
2027
                vfocoffset = RadioSetting("vfo.c.offset",
2028
                                          "VFO C offset (0.000-999.999)",
2029
                                          val1c)
2030
                vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
2031
                work.append(vfocoffset)
2032

    
2033
                val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2034
                                                _mem.vfo.d.offset))
2035
                vfodoffset = RadioSetting("vfo.d.offset",
2036
                                          "VFO D offset (0.000-999.999)",
2037
                                          val1d)
2038
                vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
2039
                work.append(vfodoffset)
2040
            else:
2041
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2042
                                                _mem.vfo.a.offset))
2043
                vfoaoffset = RadioSetting("vfo.a.offset",
2044
                                          "VFO A offset (0.000-99.999)", val1a)
2045
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2046
                work.append(vfoaoffset)
2047

    
2048
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2049
                                                _mem.vfo.b.offset))
2050
                vfoboffset = RadioSetting("vfo.b.offset",
2051
                                          "VFO B offset (0.000-99.999)", val1b)
2052
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2053
                work.append(vfoboffset)
2054

    
2055
        if not self.MODEL == "GMRS-50X1":
2056
            vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
2057
                                   RadioSettingValueList(
2058
                                       LIST_TXP,
2059
                                       LIST_TXP[_mem.vfo.a.power]))
2060
            work.append(vfoatxp)
2061

    
2062
            vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
2063
                                   RadioSettingValueList(
2064
                                       LIST_TXP,
2065
                                       LIST_TXP[_mem.vfo.b.power]))
2066
            work.append(vfobtxp)
2067

    
2068
            if self.COLOR_LCD:
2069
                vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
2070
                                       RadioSettingValueList(
2071
                                           LIST_TXP,
2072
                                           LIST_TXP[_mem.vfo.c.power]))
2073
                work.append(vfoctxp)
2074

    
2075
                vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
2076
                                       RadioSettingValueList(
2077
                                           LIST_TXP,
2078
                                           LIST_TXP[_mem.vfo.d.power]))
2079
                work.append(vfodtxp)
2080

    
2081
        if not self.MODEL == "GMRS-50X1":
2082
            vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
2083
                                    RadioSettingValueList(
2084
                                        LIST_WIDE,
2085
                                        LIST_WIDE[_mem.vfo.a.wide]))
2086
            work.append(vfoawide)
2087

    
2088
            vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
2089
                                    RadioSettingValueList(
2090
                                        LIST_WIDE,
2091
                                        LIST_WIDE[_mem.vfo.b.wide]))
2092
            work.append(vfobwide)
2093

    
2094
            if self.COLOR_LCD:
2095
                vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
2096
                                        RadioSettingValueList(
2097
                                            LIST_WIDE,
2098
                                            LIST_WIDE[_mem.vfo.c.wide]))
2099
                work.append(vfocwide)
2100

    
2101
                vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
2102
                                        RadioSettingValueList(
2103
                                            LIST_WIDE,
2104
                                            LIST_WIDE[_mem.vfo.d.wide]))
2105
                work.append(vfodwide)
2106

    
2107
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
2108
                                RadioSettingValueList(
2109
                                    LIST_STEP,
2110
                                    LIST_STEP[_mem.vfo.a.step]))
2111
        work.append(vfoastep)
2112

    
2113
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
2114
                                RadioSettingValueList(
2115
                                    LIST_STEP,
2116
                                    LIST_STEP[_mem.vfo.b.step]))
2117
        work.append(vfobstep)
2118

    
2119
        if self.COLOR_LCD:
2120
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
2121
                                    RadioSettingValueList(
2122
                                        LIST_STEP,
2123
                                        LIST_STEP[_mem.vfo.c.step]))
2124
            work.append(vfocstep)
2125

    
2126
            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
2127
                                    RadioSettingValueList(
2128
                                        LIST_STEP,
2129
                                        LIST_STEP[_mem.vfo.d.step]))
2130
            work.append(vfodstep)
2131

    
2132
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
2133
                                  RadioSettingValueList(
2134
                                      OPTSIG_LIST,
2135
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
2136
        work.append(vfoaoptsig)
2137

    
2138
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
2139
                                  RadioSettingValueList(
2140
                                      OPTSIG_LIST,
2141
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
2142
        work.append(vfoboptsig)
2143

    
2144
        if self.COLOR_LCD:
2145
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
2146
                                      RadioSettingValueList(
2147
                                          OPTSIG_LIST,
2148
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
2149
            work.append(vfocoptsig)
2150

    
2151
            vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
2152
                                      RadioSettingValueList(
2153
                                          OPTSIG_LIST,
2154
                                          OPTSIG_LIST[_mem.vfo.d.optsig]))
2155
            work.append(vfodoptsig)
2156

    
2157
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
2158
                                  RadioSettingValueList(
2159
                                      SPMUTE_LIST,
2160
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
2161
        work.append(vfoaspmute)
2162

    
2163
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
2164
                                  RadioSettingValueList(
2165
                                      SPMUTE_LIST,
2166
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
2167
        work.append(vfobspmute)
2168

    
2169
        if self.COLOR_LCD:
2170
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
2171
                                      RadioSettingValueList(
2172
                                          SPMUTE_LIST,
2173
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
2174
            work.append(vfocspmute)
2175

    
2176
            vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
2177
                                      RadioSettingValueList(
2178
                                          SPMUTE_LIST,
2179
                                          SPMUTE_LIST[_mem.vfo.d.spmute]))
2180
            work.append(vfodspmute)
2181

    
2182
        if not self.COLOR_LCD or \
2183
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
2184
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
2185
                                   RadioSettingValueBoolean(
2186
                                       _mem.vfo.a.scramble))
2187
            work.append(vfoascr)
2188

    
2189
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
2190
                                   RadioSettingValueBoolean(
2191
                                       _mem.vfo.b.scramble))
2192
            work.append(vfobscr)
2193

    
2194
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
2195
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
2196
                                   RadioSettingValueBoolean(
2197
                                       _mem.vfo.c.scramble))
2198
            work.append(vfocscr)
2199

    
2200
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
2201
                                   RadioSettingValueBoolean(
2202
                                       _mem.vfo.d.scramble))
2203
            work.append(vfodscr)
2204

    
2205
        if not self.MODEL == "GMRS-50X1":
2206
            vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
2207
                                     RadioSettingValueList(
2208
                                         PTTIDCODE_LIST,
2209
                                         PTTIDCODE_LIST[_mem.vfo.a.scode]))
2210
            work.append(vfoascode)
2211

    
2212
            vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
2213
                                     RadioSettingValueList(
2214
                                         PTTIDCODE_LIST,
2215
                                         PTTIDCODE_LIST[_mem.vfo.b.scode]))
2216
            work.append(vfobscode)
2217

    
2218
            if self.COLOR_LCD:
2219
                vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
2220
                                         RadioSettingValueList(
2221
                                             PTTIDCODE_LIST,
2222
                                             PTTIDCODE_LIST[_mem.vfo.c.scode]))
2223
                work.append(vfocscode)
2224

    
2225
                vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
2226
                                         RadioSettingValueList(
2227
                                             PTTIDCODE_LIST,
2228
                                             PTTIDCODE_LIST[_mem.vfo.d.scode]))
2229
                work.append(vfodscode)
2230

    
2231
        if not self.MODEL == "GMRS-50X1":
2232
            pttid = RadioSetting("settings.pttid", "PTT ID",
2233
                                 RadioSettingValueList(
2234
                                     PTTID_LIST,
2235
                                     PTTID_LIST[_mem.settings.pttid]))
2236
            work.append(pttid)
2237

    
2238
        if not self.COLOR_LCD:
2239
            # FM presets
2240
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
2241
            top.append(fm_presets)
2242

    
2243
            def fm_validate(value):
2244
                if value == 0:
2245
                    return chirp_common.format_freq(value)
2246
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
2247
                    msg = ("FM-Preset-Frequency: " +
2248
                           "Must be between 87.5 and 108 MHz")
2249
                    raise InvalidValueError(msg)
2250
                return value
2251

    
2252
            def apply_fm_preset_name(setting, obj):
2253
                valstring = str(setting.value)
2254
                for i in range(0, 6):
2255
                    if valstring[i] in VALID_CHARS:
2256
                        obj[i] = valstring[i]
2257
                    else:
2258
                        obj[i] = '0xff'
2259

    
2260
            def apply_fm_freq(setting, obj):
2261
                value = chirp_common.parse_freq(str(setting.value)) / 10
2262
                for i in range(7, -1, -1):
2263
                    obj.freq[i] = value % 10
2264
                    value /= 10
2265

    
2266
            _presets = self._memobj.fm_radio_preset
2267
            i = 1
2268
            for preset in _presets:
2269
                line = RadioSetting("fm_presets_" + str(i),
2270
                                    "Station name " + str(i),
2271
                                    RadioSettingValueString(0, 6, _filter(
2272
                                        preset.broadcast_station_name)))
2273
                line.set_apply_callback(apply_fm_preset_name,
2274
                                        preset.broadcast_station_name)
2275

    
2276
                val = RadioSettingValueFloat(0, 108,
2277
                                             convert_bytes_to_freq(
2278
                                                 preset.freq))
2279
                fmfreq = RadioSetting("fm_presets_" + str(i) + "_freq",
2280
                                      "Frequency " + str(i), val)
2281
                val.set_validate_callback(fm_validate)
2282
                fmfreq.set_apply_callback(apply_fm_freq, preset)
2283
                fm_presets.append(line)
2284
                fm_presets.append(fmfreq)
2285

    
2286
                i = i + 1
2287

    
2288
        # DTMF-Setting
2289
        dtmf_enc_settings = RadioSettingGroup("dtmf_enc_settings",
2290
                                              "DTMF Encoding Settings")
2291
        dtmf_dec_settings = RadioSettingGroup("dtmf_dec_settings",
2292
                                              "DTMF Decoding Settings")
2293
        top.append(dtmf_enc_settings)
2294
        top.append(dtmf_dec_settings)
2295
        txdisable = RadioSetting("dtmf_settings.txdisable",
2296
                                 "TX-Disable",
2297
                                 RadioSettingValueBoolean(
2298
                                     _mem.dtmf_settings.txdisable))
2299
        dtmf_enc_settings.append(txdisable)
2300

    
2301
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
2302
                                 "RX-Disable",
2303
                                 RadioSettingValueBoolean(
2304
                                     _mem.dtmf_settings.rxdisable))
2305
        dtmf_enc_settings.append(rxdisable)
2306

    
2307
        if _mem.dtmf_settings.dtmfspeed_on > 0x0F:
2308
            val = 0x03
2309
        else:
2310
            val = _mem.dtmf_settings.dtmfspeed_on
2311
        dtmfspeed_on = RadioSetting(
2312
            "dtmf_settings.dtmfspeed_on",
2313
            "DTMF Speed (On Time)",
2314
            RadioSettingValueList(LIST_DTMF_SPEED,
2315
                                  LIST_DTMF_SPEED[
2316
                                      val]))
2317
        dtmf_enc_settings.append(dtmfspeed_on)
2318

    
2319
        if _mem.dtmf_settings.dtmfspeed_off > 0x0F:
2320
            val = 0x03
2321
        else:
2322
            val = _mem.dtmf_settings.dtmfspeed_off
2323
        dtmfspeed_off = RadioSetting(
2324
            "dtmf_settings.dtmfspeed_off",
2325
            "DTMF Speed (Off Time)",
2326
            RadioSettingValueList(LIST_DTMF_SPEED,
2327
                                  LIST_DTMF_SPEED[
2328
                                      val]))
2329
        dtmf_enc_settings.append(dtmfspeed_off)
2330

    
2331
        def memory2string(dmtf_mem):
2332
            dtmf_string = ""
2333
            for digit in dmtf_mem:
2334
                if digit != 255:
2335
                    index = LIST_DTMF_VALUES.index(digit)
2336
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2337
            return dtmf_string
2338

    
2339
        def apply_dmtf_frame(setting, obj):
2340
            LOG.debug("Setting DTMF-Code: " + str(setting.value))
2341
            val_string = str(setting.value)
2342
            for i in range(0, 16):
2343
                obj[i] = 255
2344
            i = 0
2345
            for current_char in val_string:
2346
                current_char = current_char.upper()
2347
                index = LIST_DTMF_DIGITS.index(current_char)
2348
                obj[i] = LIST_DTMF_VALUES[index]
2349
                i = i + 1
2350

    
2351
        codes = self._memobj.dtmf_codes
2352
        i = 1
2353
        for dtmfcode in codes:
2354
            val = RadioSettingValueString(0, 16, memory2string(
2355
                                              dtmfcode.code),
2356
                                          False, CHARSET_DTMF_DIGITS)
2357
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
2358
                                "DMTF Code " + str(i), val)
2359
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
2360
            dtmf_enc_settings.append(line)
2361
            i = i + 1
2362

    
2363
        line = RadioSetting("dtmf_settings.mastervice",
2364
                            "Master and Vice ID",
2365
                            RadioSettingValueBoolean(
2366
                                _mem.dtmf_settings.mastervice))
2367
        dtmf_dec_settings.append(line)
2368

    
2369
        val = RadioSettingValueString(0, 16, memory2string(
2370
                                          _mem.dtmf_settings.masterid),
2371
                                      False, CHARSET_DTMF_DIGITS)
2372
        line = RadioSetting("dtmf_settings.masterid",
2373
                            "Master Control ID ", val)
2374
        line.set_apply_callback(apply_dmtf_frame,
2375
                                _mem.dtmf_settings.masterid)
2376
        dtmf_dec_settings.append(line)
2377

    
2378
        line = RadioSetting("dtmf_settings.minspection",
2379
                            "Master Inspection",
2380
                            RadioSettingValueBoolean(
2381
                                _mem.dtmf_settings.minspection))
2382
        dtmf_dec_settings.append(line)
2383

    
2384
        line = RadioSetting("dtmf_settings.mmonitor",
2385
                            "Master Monitor",
2386
                            RadioSettingValueBoolean(
2387
                                _mem.dtmf_settings.mmonitor))
2388
        dtmf_dec_settings.append(line)
2389

    
2390
        line = RadioSetting("dtmf_settings.mstun",
2391
                            "Master Stun",
2392
                            RadioSettingValueBoolean(
2393
                                _mem.dtmf_settings.mstun))
2394
        dtmf_dec_settings.append(line)
2395

    
2396
        line = RadioSetting("dtmf_settings.mkill",
2397
                            "Master Kill",
2398
                            RadioSettingValueBoolean(
2399
                                _mem.dtmf_settings.mkill))
2400
        dtmf_dec_settings.append(line)
2401

    
2402
        line = RadioSetting("dtmf_settings.mrevive",
2403
                            "Master Revive",
2404
                            RadioSettingValueBoolean(
2405
                                _mem.dtmf_settings.mrevive))
2406
        dtmf_dec_settings.append(line)
2407

    
2408
        val = RadioSettingValueString(0, 16, memory2string(
2409
                                          _mem.dtmf_settings.viceid),
2410
                                      False, CHARSET_DTMF_DIGITS)
2411
        line = RadioSetting("dtmf_settings.viceid",
2412
                            "Vice Control ID ", val)
2413
        line.set_apply_callback(apply_dmtf_frame,
2414
                                _mem.dtmf_settings.viceid)
2415
        dtmf_dec_settings.append(line)
2416

    
2417
        line = RadioSetting("dtmf_settings.vinspection",
2418
                            "Vice Inspection",
2419
                            RadioSettingValueBoolean(
2420
                                _mem.dtmf_settings.vinspection))
2421
        dtmf_dec_settings.append(line)
2422

    
2423
        line = RadioSetting("dtmf_settings.vmonitor",
2424
                            "Vice Monitor",
2425
                            RadioSettingValueBoolean(
2426
                                _mem.dtmf_settings.vmonitor))
2427
        dtmf_dec_settings.append(line)
2428

    
2429
        line = RadioSetting("dtmf_settings.vstun",
2430
                            "Vice Stun",
2431
                            RadioSettingValueBoolean(
2432
                                _mem.dtmf_settings.vstun))
2433
        dtmf_dec_settings.append(line)
2434

    
2435
        line = RadioSetting("dtmf_settings.vkill",
2436
                            "Vice Kill",
2437
                            RadioSettingValueBoolean(
2438
                                _mem.dtmf_settings.vkill))
2439
        dtmf_dec_settings.append(line)
2440

    
2441
        line = RadioSetting("dtmf_settings.vrevive",
2442
                            "Vice Revive",
2443
                            RadioSettingValueBoolean(
2444
                                _mem.dtmf_settings.vrevive))
2445
        dtmf_dec_settings.append(line)
2446

    
2447
        val = RadioSettingValueString(0, 16, memory2string(
2448
                                          _mem.dtmf_settings.inspection),
2449
                                      False, CHARSET_DTMF_DIGITS)
2450
        line = RadioSetting("dtmf_settings.inspection",
2451
                            "Inspection", val)
2452
        line.set_apply_callback(apply_dmtf_frame,
2453
                                _mem.dtmf_settings.inspection)
2454
        dtmf_dec_settings.append(line)
2455

    
2456
        val = RadioSettingValueString(0, 16, memory2string(
2457
                                          _mem.dtmf_settings.alarmcode),
2458
                                      False, CHARSET_DTMF_DIGITS)
2459
        line = RadioSetting("dtmf_settings.alarmcode",
2460
                            "Alarm", val)
2461
        line.set_apply_callback(apply_dmtf_frame,
2462
                                _mem.dtmf_settings.alarmcode)
2463
        dtmf_dec_settings.append(line)
2464

    
2465
        val = RadioSettingValueString(0, 16, memory2string(
2466
                                          _mem.dtmf_settings.kill),
2467
                                      False, CHARSET_DTMF_DIGITS)
2468
        line = RadioSetting("dtmf_settings.kill",
2469
                            "Kill", val)
2470
        line.set_apply_callback(apply_dmtf_frame,
2471
                                _mem.dtmf_settings.kill)
2472
        dtmf_dec_settings.append(line)
2473

    
2474
        val = RadioSettingValueString(0, 16, memory2string(
2475
                                          _mem.dtmf_settings.monitor),
2476
                                      False, CHARSET_DTMF_DIGITS)
2477
        line = RadioSetting("dtmf_settings.monitor",
2478
                            "Monitor", val)
2479
        line.set_apply_callback(apply_dmtf_frame,
2480
                                _mem.dtmf_settings.monitor)
2481
        dtmf_dec_settings.append(line)
2482

    
2483
        val = RadioSettingValueString(0, 16, memory2string(
2484
                                          _mem.dtmf_settings.stun),
2485
                                      False, CHARSET_DTMF_DIGITS)
2486
        line = RadioSetting("dtmf_settings.stun",
2487
                            "Stun", val)
2488
        line.set_apply_callback(apply_dmtf_frame,
2489
                                _mem.dtmf_settings.stun)
2490
        dtmf_dec_settings.append(line)
2491

    
2492
        val = RadioSettingValueString(0, 16, memory2string(
2493
                                          _mem.dtmf_settings.revive),
2494
                                      False, CHARSET_DTMF_DIGITS)
2495
        line = RadioSetting("dtmf_settings.revive",
2496
                            "Revive", val)
2497
        line.set_apply_callback(apply_dmtf_frame,
2498
                                _mem.dtmf_settings.revive)
2499
        dtmf_dec_settings.append(line)
2500

    
2501
        def apply_dmtf_listvalue(setting, obj):
2502
            LOG.debug("Setting value: " + str(setting.value) + " from list")
2503
            val = str(setting.value)
2504
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2505
            val = LIST_DTMF_SPECIAL_VALUES[index]
2506
            obj.set_value(val)
2507

    
2508
        if _mem.dtmf_settings.groupcode not in LIST_DTMF_SPECIAL_VALUES:
2509
            val = 0x0B
2510
        else:
2511
            val = _mem.dtmf_settings.groupcode
2512
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2513
        line = RadioSetting(
2514
            "dtmf_settings.groupcode",
2515
            "Group Code",
2516
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2517
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2518
        line.set_apply_callback(apply_dmtf_listvalue,
2519
                                _mem.dtmf_settings.groupcode)
2520
        dtmf_dec_settings.append(line)
2521

    
2522
        if _mem.dtmf_settings.spacecode not in LIST_DTMF_SPECIAL_VALUES:
2523
            val = 0x0C
2524
        else:
2525
            val = _mem.dtmf_settings.spacecode
2526
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2527
        line = RadioSetting(
2528
            "dtmf_settings.spacecode",
2529
            "Space Code",
2530
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2531
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2532
        line.set_apply_callback(apply_dmtf_listvalue,
2533
                                _mem.dtmf_settings.spacecode)
2534
        dtmf_dec_settings.append(line)
2535

    
2536
        if self.COLOR_LCD:
2537
            if _mem.dtmf_settings.resettime > 0x63:
2538
                val = 0x4F
2539
            else:
2540
                val = _mem.dtmf_settings.resettime
2541
            line = RadioSetting(
2542
                "dtmf_settings.resettime",
2543
                "Reset time",
2544
                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
2545
                                      LIST_5TONE_RESET_COLOR[
2546
                                          val]))
2547
            dtmf_dec_settings.append(line)
2548
        else:
2549
            line = RadioSetting(
2550
                "dtmf_settings.resettime",
2551
                "Reset time",
2552
                RadioSettingValueList(LIST_5TONE_RESET,
2553
                                      LIST_5TONE_RESET[
2554
                                          _mem.dtmf_settings.resettime]))
2555
            dtmf_dec_settings.append(line)
2556

    
2557
        if _mem.dtmf_settings.delayproctime > 0x27:
2558
            val = 0x04
2559
        else:
2560
            val = _mem.dtmf_settings.delayproctime
2561
        line = RadioSetting(
2562
            "dtmf_settings.delayproctime",
2563
            "Delay processing time",
2564
            RadioSettingValueList(LIST_DTMF_DELAY,
2565
                                  LIST_DTMF_DELAY[
2566
                                      val]))
2567
        dtmf_dec_settings.append(line)
2568

    
2569
        # 5 Tone Settings
2570
        stds_5tone = RadioSettingGroup("stds_5tone", "Standards")
2571
        codes_5tone = RadioSettingGroup("codes_5tone", "Codes")
2572

    
2573
        group_5tone = RadioSettingGroup("group_5tone", "5 Tone Settings")
2574
        group_5tone.append(stds_5tone)
2575
        group_5tone.append(codes_5tone)
2576

    
2577
        top.append(group_5tone)
2578

    
2579
        def apply_list_value(setting, obj):
2580
            options = setting.value.get_options()
2581
            obj.set_value(options.index(str(setting.value)))
2582

    
2583
        _5tone_standards = self._memobj._5tone_std_settings
2584
        i = 0
2585
        for standard in _5tone_standards:
2586
            std_5tone = RadioSettingGroup("std_5tone_" + str(i),
2587
                                          LIST_5TONE_STANDARDS[i])
2588
            stds_5tone.append(std_5tone)
2589

    
2590
            period = standard.period
2591
            if period == 255:
2592
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2593
                          " is not yet configured. Setting to 70ms.")
2594
                period = 5
2595

    
2596
            if period <= len(LIST_5TONE_STANDARD_PERIODS):
2597
                line = RadioSetting(
2598
                    "_5tone_std_settings_" + str(i) + "_period",
2599
                    "Period (ms)", RadioSettingValueList
2600
                    (LIST_5TONE_STANDARD_PERIODS,
2601
                     LIST_5TONE_STANDARD_PERIODS[period]))
2602
                line.set_apply_callback(apply_list_value, standard.period)
2603
                std_5tone.append(line)
2604
            else:
2605
                LOG.debug("Invalid value for 5tone period! Disabling.")
2606

    
2607
            group_tone = standard.group_tone
2608
            if group_tone == 255:
2609
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2610
                          " is not yet configured. Setting to A.")
2611
                group_tone = 10
2612

    
2613
            if group_tone <= len(LIST_5TONE_DIGITS):
2614
                line = RadioSetting(
2615
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2616
                    "Group Tone",
2617
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2618
                                          LIST_5TONE_DIGITS[
2619
                                              group_tone]))
2620
                line.set_apply_callback(apply_list_value,
2621
                                        standard.group_tone)
2622
                std_5tone.append(line)
2623
            else:
2624
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2625

    
2626
            repeat_tone = standard.repeat_tone
2627
            if repeat_tone == 255:
2628
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2629
                          " is not yet configured. Setting to E.")
2630
                repeat_tone = 14
2631

    
2632
            if repeat_tone <= len(LIST_5TONE_DIGITS):
2633
                line = RadioSetting(
2634
                    "_5tone_std_settings_" + str(i) + "_repttone",
2635
                    "Repeat Tone",
2636
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2637
                                          LIST_5TONE_DIGITS[
2638
                                              repeat_tone]))
2639
                line.set_apply_callback(apply_list_value,
2640
                                        standard.repeat_tone)
2641
                std_5tone.append(line)
2642
            else:
2643
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2644
            i = i + 1
2645

    
2646
        def my_apply_5tonestdlist_value(setting, obj):
2647
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2648
                obj.set_value(0xFF)
2649
            else:
2650
                obj.set_value(LIST_5TONE_STANDARDS.
2651
                              index(str(setting.value)))
2652

    
2653
        def apply_5tone_frame(setting, obj):
2654
            LOG.debug("Setting 5 Tone: " + str(setting.value))
2655
            valstring = str(setting.value)
2656
            if len(valstring) == 0:
2657
                for i in range(0, 5):
2658
                    obj[i] = 255
2659
            else:
2660
                validFrame = True
2661
                for i in range(0, 5):
2662
                    currentChar = valstring[i].upper()
2663
                    if currentChar in LIST_5TONE_DIGITS:
2664
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2665
                    else:
2666
                        validFrame = False
2667
                        LOG.debug("invalid char: " + str(currentChar))
2668
                if not validFrame:
2669
                    LOG.debug("setting whole frame to FF")
2670
                    for i in range(0, 5):
2671
                        obj[i] = 255
2672

    
2673
        def validate_5tone_frame(value):
2674
            if (len(str(value)) != 5) and (len(str(value)) != 0):
2675
                msg = ("5 Tone must have 5 digits or 0 digits")
2676
                raise InvalidValueError(msg)
2677
            for digit in str(value):
2678
                if digit.upper() not in LIST_5TONE_DIGITS:
2679
                    msg = (str(digit) + " is not a valid digit for 5tones")
2680
                    raise InvalidValueError(msg)
2681
            return value
2682

    
2683
        def frame2string(frame):
2684
            frameString = ""
2685
            for digit in frame:
2686
                if digit != 255:
2687
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2688
            return frameString
2689

    
2690
        _5tone_codes = self._memobj._5tone_codes
2691
        i = 1
2692
        for code in _5tone_codes:
2693
            code_5tone = RadioSettingGroup("code_5tone_" + str(i),
2694
                                           "5 Tone code " + str(i))
2695
            codes_5tone.append(code_5tone)
2696
            if (code.standard == 255):
2697
                currentVal = 15
2698
            else:
2699
                currentVal = code.standard
2700
            line = RadioSetting("_5tone_code_" + str(i) + "_std",
2701
                                " Standard",
2702
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2703
                                                      LIST_5TONE_STANDARDS[
2704
                                                          currentVal]))
2705
            line.set_apply_callback(my_apply_5tonestdlist_value,
2706
                                    code.standard)
2707
            code_5tone.append(line)
2708

    
2709
            val = RadioSettingValueString(0, 6,
2710
                                          frame2string(code.frame1), False)
2711
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
2712
                                " Frame 1", val)
2713
            val.set_validate_callback(validate_5tone_frame)
2714
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2715
            code_5tone.append(line)
2716

    
2717
            val = RadioSettingValueString(0, 6,
2718
                                          frame2string(code.frame2), False)
2719
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2720
                                " Frame 2", val)
2721
            val.set_validate_callback(validate_5tone_frame)
2722
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2723
            code_5tone.append(line)
2724

    
2725
            val = RadioSettingValueString(0, 6,
2726
                                          frame2string(code.frame3), False)
2727
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2728
                                " Frame 3", val)
2729
            val.set_validate_callback(validate_5tone_frame)
2730
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2731
            code_5tone.append(line)
2732
            i = i + 1
2733

    
2734
        _5_tone_decode1 = RadioSetting(
2735
            "_5tone_settings._5tone_decode_call_frame1",
2736
            "5 Tone decode call Frame 1",
2737
            RadioSettingValueBoolean(
2738
                _mem._5tone_settings._5tone_decode_call_frame1))
2739
        group_5tone.append(_5_tone_decode1)
2740

    
2741
        _5_tone_decode2 = RadioSetting(
2742
            "_5tone_settings._5tone_decode_call_frame2",
2743
            "5 Tone decode call Frame 2",
2744
            RadioSettingValueBoolean(
2745
                _mem._5tone_settings._5tone_decode_call_frame2))
2746
        group_5tone.append(_5_tone_decode2)
2747

    
2748
        _5_tone_decode3 = RadioSetting(
2749
            "_5tone_settings._5tone_decode_call_frame3",
2750
            "5 Tone decode call Frame 3",
2751
            RadioSettingValueBoolean(
2752
                _mem._5tone_settings._5tone_decode_call_frame3))
2753
        group_5tone.append(_5_tone_decode3)
2754

    
2755
        _5_tone_decode_disp1 = RadioSetting(
2756
            "_5tone_settings._5tone_decode_disp_frame1",
2757
            "5 Tone decode disp Frame 1",
2758
            RadioSettingValueBoolean(
2759
                _mem._5tone_settings._5tone_decode_disp_frame1))
2760
        group_5tone.append(_5_tone_decode_disp1)
2761

    
2762
        _5_tone_decode_disp2 = RadioSetting(
2763
            "_5tone_settings._5tone_decode_disp_frame2",
2764
            "5 Tone decode disp Frame 2",
2765
            RadioSettingValueBoolean(
2766
                _mem._5tone_settings._5tone_decode_disp_frame2))
2767
        group_5tone.append(_5_tone_decode_disp2)
2768

    
2769
        _5_tone_decode_disp3 = RadioSetting(
2770
            "_5tone_settings._5tone_decode_disp_frame3",
2771
            "5 Tone decode disp Frame 3",
2772
            RadioSettingValueBoolean(
2773
                _mem._5tone_settings._5tone_decode_disp_frame3))
2774
        group_5tone.append(_5_tone_decode_disp3)
2775

    
2776
        decode_standard = _mem._5tone_settings.decode_standard
2777
        if decode_standard == 255:
2778
            decode_standard = 0
2779
        if decode_standard <= len(LIST_5TONE_STANDARDS_without_none):
2780
            line = RadioSetting("_5tone_settings.decode_standard",
2781
                                "5 Tone-decode Standard",
2782
                                RadioSettingValueList(
2783
                                    LIST_5TONE_STANDARDS_without_none,
2784
                                    LIST_5TONE_STANDARDS_without_none[
2785
                                        decode_standard]))
2786
            group_5tone.append(line)
2787
        else:
2788
            LOG.debug("Invalid decode std...")
2789

    
2790
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2791
        if _5tone_delay1 == 255:
2792
            _5tone_delay1 = 20
2793

    
2794
        if _5tone_delay1 <= len(LIST_5TONE_DELAY):
2795
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2796
                                         LIST_5TONE_DELAY[
2797
                                             _5tone_delay1])
2798
            line = RadioSetting("_5tone_settings._5tone_delay1",
2799
                                "5 Tone Delay Frame 1", list)
2800
            group_5tone.append(line)
2801
        else:
2802
            LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
2803

    
2804
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2805
        if _5tone_delay2 == 255:
2806
            _5tone_delay2 = 20
2807
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2808

    
2809
        if _5tone_delay2 <= len(LIST_5TONE_DELAY):
2810
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2811
                                         LIST_5TONE_DELAY[
2812
                                             _5tone_delay2])
2813
            line = RadioSetting("_5tone_settings._5tone_delay2",
2814
                                "5 Tone Delay Frame 2", list)
2815
            group_5tone.append(line)
2816
        else:
2817
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2818

    
2819
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2820
        if _5tone_delay3 == 255:
2821
            _5tone_delay3 = 20
2822
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2823

    
2824
        if _5tone_delay3 <= len(LIST_5TONE_DELAY):
2825
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2826
                                         LIST_5TONE_DELAY[
2827
                                             _5tone_delay3])
2828
            line = RadioSetting("_5tone_settings._5tone_delay3",
2829
                                "5 Tone Delay Frame 3", list)
2830
            group_5tone.append(line)
2831
        else:
2832
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2833

    
2834
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2835
        if ext_length == 255:
2836
            ext_length = 0
2837
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2838

    
2839
        if ext_length <= len(LIST_5TONE_DELAY):
2840
            list = RadioSettingValueList(
2841
                LIST_5TONE_DELAY,
2842
                LIST_5TONE_DELAY[
2843
                    ext_length])
2844
            line = RadioSetting(
2845
                "_5tone_settings._5tone_first_digit_ext_length",
2846
                "First digit extend length", list)
2847
            group_5tone.append(line)
2848
        else:
2849
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2850

    
2851
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2852
        if decode_reset_time == 255:
2853
            decode_reset_time = 59
2854
            LOG.debug("Decode reset time unconfigured. resetting.")
2855
        if decode_reset_time <= len(LIST_5TONE_RESET):
2856
            list = RadioSettingValueList(
2857
                LIST_5TONE_RESET,
2858
                LIST_5TONE_RESET[
2859
                    decode_reset_time])
2860
            line = RadioSetting("_5tone_settings.decode_reset_time",
2861
                                "Decode reset time", list)
2862
            group_5tone.append(line)
2863
        else:
2864
            LOG.debug("Invalid value decode reset time! Disabling.")
2865

    
2866
        # 2 Tone
2867
        encode_2tone = RadioSettingGroup("encode_2tone", "2 Tone Encode")
2868
        decode_2tone = RadioSettingGroup("decode_2tone", "2 Code Decode")
2869

    
2870
        top.append(encode_2tone)
2871
        top.append(decode_2tone)
2872

    
2873
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2874
        if duration_1st_tone == 255:
2875
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2876
                      "configured. Setting to 600ms")
2877
            duration_1st_tone = 60
2878

    
2879
        if duration_1st_tone <= len(LIST_5TONE_DELAY):
2880
            line = RadioSetting("_2tone.duration_1st_tone",
2881
                                "Duration 1st Tone",
2882
                                RadioSettingValueList(LIST_5TONE_DELAY,
2883
                                                      LIST_5TONE_DELAY[
2884
                                                          duration_1st_tone]))
2885
            encode_2tone.append(line)
2886

    
2887
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2888
        if duration_2nd_tone == 255:
2889
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2890
                      "configured. Setting to 600ms")
2891
            duration_2nd_tone = 60
2892

    
2893
        if duration_2nd_tone <= len(LIST_5TONE_DELAY):
2894
            line = RadioSetting("_2tone.duration_2nd_tone",
2895
                                "Duration 2nd Tone",
2896
                                RadioSettingValueList(LIST_5TONE_DELAY,
2897
                                                      LIST_5TONE_DELAY[
2898
                                                          duration_2nd_tone]))
2899
            encode_2tone.append(line)
2900

    
2901
        duration_gap = self._memobj._2tone.duration_gap
2902
        if duration_gap == 255:
2903
            LOG.debug("Duration of gap is not yet " +
2904
                      "configured. Setting to 300ms")
2905
            duration_gap = 30
2906

    
2907
        if duration_gap <= len(LIST_5TONE_DELAY):
2908
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2909
                                RadioSettingValueList(LIST_5TONE_DELAY,
2910
                                                      LIST_5TONE_DELAY[
2911
                                                          duration_gap]))
2912
            encode_2tone.append(line)
2913

    
2914
        def _2tone_validate(value):
2915
            if value == 0:
2916
                return 65535
2917
            if value == 65535:
2918
                return value
2919
            if not (300 <= value and value <= 3000):
2920
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2921
                raise InvalidValueError(msg)
2922
            return value
2923

    
2924
        def apply_2tone_freq(setting, obj):
2925
            val = int(setting.value)
2926
            if (val == 0) or (val == 65535):
2927
                obj.set_value(65535)
2928
            else:
2929
                obj.set_value(val)
2930

    
2931
        i = 1
2932
        for code in self._memobj._2tone._2tone_encode:
2933
            code_2tone = RadioSettingGroup("code_2tone_" + str(i),
2934
                                           "Encode Code " + str(i))
2935
            encode_2tone.append(code_2tone)
2936

    
2937
            tmp = code.freq1
2938
            if tmp == 65535:
2939
                tmp = 0
2940
            val1 = RadioSettingValueInteger(0, 65535, tmp)
2941
            freq1 = RadioSetting("2tone_code_" + str(i) + "_freq1",
2942
                                 "Frequency 1", val1)
2943
            val1.set_validate_callback(_2tone_validate)
2944
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
2945
            code_2tone.append(freq1)
2946

    
2947
            tmp = code.freq2
2948
            if tmp == 65535:
2949
                tmp = 0
2950
            val2 = RadioSettingValueInteger(0, 65535, tmp)
2951
            freq2 = RadioSetting("2tone_code_" + str(i) + "_freq2",
2952
                                 "Frequency 2", val2)
2953
            val2.set_validate_callback(_2tone_validate)
2954
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
2955
            code_2tone.append(freq2)
2956

    
2957
            i = i + 1
2958

    
2959
        decode_reset_time = _mem._2tone.reset_time
2960
        if decode_reset_time == 255:
2961
            decode_reset_time = 59
2962
            LOG.debug("Decode reset time unconfigured. resetting.")
2963
        if decode_reset_time <= len(LIST_5TONE_RESET):
2964
            list = RadioSettingValueList(
2965
                LIST_5TONE_RESET,
2966
                LIST_5TONE_RESET[
2967
                    decode_reset_time])
2968
            line = RadioSetting("_2tone.reset_time",
2969
                                "Decode reset time", list)
2970
            decode_2tone.append(line)
2971
        else:
2972
            LOG.debug("Invalid value decode reset time! Disabling.")
2973

    
2974
        def apply_2tone_freq_pair(setting, obj):
2975
            val = int(setting.value)
2976
            derived_val = 65535
2977
            frqname = str(setting._name[-5:])
2978
            derivedname = "derived_from_" + frqname
2979

    
2980
            if (val == 0):
2981
                val = 65535
2982
                derived_val = 65535
2983
            else:
2984
                derived_val = int(round(2304000.0/val))
2985

    
2986
            obj[frqname].set_value(val)
2987
            obj[derivedname].set_value(derived_val)
2988

    
2989
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " +
2990
                      derivedname + ": " + str(derived_val))
2991

    
2992
        i = 1
2993
        for decode_code in self._memobj._2tone._2tone_decode:
2994
            _2tone_dec_code = RadioSettingGroup("code_2tone_" + str(i),
2995
                                                "Decode Code " + str(i))
2996
            decode_2tone.append(_2tone_dec_code)
2997

    
2998
            j = 1
2999
            for dec in decode_code.decs:
3000
                val = dec.dec
3001
                if val == 255:
3002
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
3003
                              " is not yet configured. Setting to 0.")
3004
                    val = 0
3005

    
3006
                if val <= len(LIST_2TONE_DEC):
3007
                    line = RadioSetting(
3008
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
3009
                        "Dec " + str(j), RadioSettingValueList
3010
                        (LIST_2TONE_DEC,
3011
                         LIST_2TONE_DEC[val]))
3012
                    line.set_apply_callback(apply_list_value, dec.dec)
3013
                    _2tone_dec_code.append(line)
3014
                else:
3015
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
3016

    
3017
                val = dec.response
3018
                if val == 255:
3019
                    LOG.debug("Response for Code " + str(i) + " Dec " +
3020
                              str(j) + " is not yet configured. Setting to 0.")
3021
                    val = 0
3022

    
3023
                if val <= len(LIST_2TONE_RESPONSE):
3024
                    line = RadioSetting(
3025
                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
3026
                        "Response " + str(j), RadioSettingValueList
3027
                        (LIST_2TONE_RESPONSE,
3028
                         LIST_2TONE_RESPONSE[val]))
3029
                    line.set_apply_callback(apply_list_value, dec.response)
3030
                    _2tone_dec_code.append(line)
3031
                else:
3032
                    LOG.debug("Invalid value for 2tone response! Disabling.")
3033

    
3034
                val = dec.alert
3035
                if val == 255:
3036
                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +
3037
                              " is not yet configured. Setting to 0.")
3038
                    val = 0
3039

    
3040
                if val <= len(PTTIDCODE_LIST):
3041
                    line = RadioSetting(
3042
                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
3043
                        "Alert " + str(j), RadioSettingValueList
3044
                        (PTTIDCODE_LIST,
3045
                         PTTIDCODE_LIST[val]))
3046
                    line.set_apply_callback(apply_list_value, dec.alert)
3047
                    _2tone_dec_code.append(line)
3048
                else:
3049
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
3050
                j = j + 1
3051

    
3052
            freq = self._memobj._2tone.freqs[i-1]
3053
            for char in ['A', 'B', 'C', 'D']:
3054
                setting_name = "freq" + str(char)
3055

    
3056
                tmp = freq[setting_name]
3057
                if tmp == 65535:
3058
                    tmp = 0
3059
                if tmp != 0:
3060
                    expected = int(round(2304000.0/tmp))
3061
                    from_mem = freq["derived_from_" + setting_name]
3062
                    if expected != from_mem:
3063
                        LOG.error("Expected " + str(expected) +
3064
                                  " but read " + str(from_mem) +
3065
                                  ". Disabling 2Tone Decode Freqs!")
3066
                        break
3067
                val = RadioSettingValueInteger(0, 65535, tmp)
3068
                frq = RadioSetting("2tone_dec_" + str(i) + "_freq" + str(char),
3069
                                   ("Decode Frequency " + str(char)), val)
3070
                val.set_validate_callback(_2tone_validate)
3071
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
3072
                _2tone_dec_code.append(frq)
3073

    
3074
            i = i + 1
3075

    
3076
        return top
3077

    
3078
    def set_settings(self, settings):
3079
        _settings = self._memobj.settings
3080
        for element in settings:
3081
            if not isinstance(element, RadioSetting):
3082
                if element.get_name() == "fm_preset":
3083
                    self._set_fm_preset(element)
3084
                else:
3085
                    self.set_settings(element)
3086
                    continue
3087
            else:
3088
                try:
3089
                    name = element.get_name()
3090
                    if "." in name:
3091
                        bits = name.split(".")
3092
                        obj = self._memobj
3093
                        for bit in bits[:-1]:
3094
                            if "/" in bit:
3095
                                bit, index = bit.split("/", 1)
3096
                                index = int(index)
3097
                                obj = getattr(obj, bit)[index]
3098
                            else:
3099
                                obj = getattr(obj, bit)
3100
                        setting = bits[-1]
3101
                    else:
3102
                        obj = _settings
3103
                        setting = element.get_name()
3104

    
3105
                    if element.has_apply_callback():
3106
                        LOG.debug("Using apply callback")
3107
                        element.run_apply_callback()
3108
                    elif element.value.get_mutable():
3109
                        LOG.debug("Setting %s = %s" % (setting, element.value))
3110
                        setattr(obj, setting, element.value)
3111
                except Exception, e:
3112
                    LOG.debug(element.get_name())
3113
                    raise
3114

    
3115
    @classmethod
3116
    def match_model(cls, filedata, filename):
3117
        match_size = False
3118
        match_model = False
3119

    
3120
        # testing the file data size
3121
        if len(filedata) == MEM_SIZE:
3122
            match_size = True
3123

    
3124
        # testing the firmware model fingerprint
3125
        match_model = model_match(cls, filedata)
3126

    
3127
        if match_size and match_model:
3128
            return True
3129
        else:
3130
            return False
3131

    
3132

    
3133
MEM_FORMAT = """
3134
#seekto 0x0000;
3135
struct {
3136
  lbcd rxfreq[4];
3137
  lbcd txfreq[4];
3138
  ul16 rxtone;
3139
  ul16 txtone;
3140
  u8 unknown0:4,
3141
     scode:4;
3142
  u8 unknown1:2,
3143
     spmute:2,
3144
     unknown2:2,
3145
     optsig:2;
3146
  u8 unknown3:3,
3147
     scramble:1,
3148
     unknown4:3,
3149
     power:1;
3150
  u8 unknown5:1,
3151
     wide:1,
3152
     unknown6:2,
3153
     bcl:1,
3154
     add:1,
3155
     pttid:2;
3156
} memory[200];
3157

    
3158
#seekto 0x0E00;
3159
struct {
3160
  u8 tdr;
3161
  u8 unknown1;
3162
  u8 sql;
3163
  u8 unknown2[2];
3164
  u8 tot;
3165
  u8 apo;           // BTech radios use this as the Auto Power Off time
3166
                    // other radios use this as pre-Time Out Alert
3167
  u8 unknown3;
3168
  u8 abr;
3169
  u8 beep;
3170
  u8 unknown4[4];
3171
  u8 dtmfst;
3172
  u8 unknown5[2];
3173
  u8 prisc;
3174
  u8 prich;
3175
  u8 screv;
3176
  u8 unknown6[2];
3177
  u8 pttid;
3178
  u8 pttlt;
3179
  u8 unknown7;
3180
  u8 emctp;
3181
  u8 emcch;
3182
  u8 ringt;
3183
  u8 unknown8;
3184
  u8 camdf;
3185
  u8 cbmdf;
3186
  u8 sync;          // BTech radios use this as the display sync setting
3187
                    // other radios use this as the auto keypad lock setting
3188
  u8 ponmsg;
3189
  u8 wtled;
3190
  u8 rxled;
3191
  u8 txled;
3192
  u8 unknown9[5];
3193
  u8 anil;
3194
  u8 reps;
3195
  u8 repm;
3196
  u8 tdrab;
3197
  u8 ste;
3198
  u8 rpste;
3199
  u8 rptdl;
3200
  u8 mgain;
3201
  u8 dtmfg;
3202
} settings;
3203

    
3204
#seekto 0x0E80;
3205
struct {
3206
  u8 unknown1;
3207
  u8 vfomr;
3208
  u8 keylock;
3209
  u8 unknown2;
3210
  u8 unknown3:4,
3211
     vfomren:1,
3212
     unknown4:1,
3213
     reseten:1,
3214
     menuen:1;
3215
  u8 unknown5[11];
3216
  u8 dispab;
3217
  u8 mrcha;
3218
  u8 mrchb;
3219
  u8 menu;
3220
} settings2;
3221

    
3222
#seekto 0x0EC0;
3223
struct {
3224
  char line1[6];
3225
  char line2[6];
3226
} poweron_msg;
3227

    
3228
struct settings_vfo {
3229
  u8 freq[8];
3230
  u8 offset[6];
3231
  u8 unknown2[2];
3232
  ul16 rxtone;
3233
  ul16 txtone;
3234
  u8 scode;
3235
  u8 spmute;
3236
  u8 optsig;
3237
  u8 scramble;
3238
  u8 wide;
3239
  u8 power;
3240
  u8 shiftd;
3241
  u8 step;
3242
  u8 unknown3[4];
3243
};
3244

    
3245
#seekto 0x0F00;
3246
struct {
3247
  struct settings_vfo a;
3248
  struct settings_vfo b;
3249
} vfo;
3250

    
3251
#seekto 0x1000;
3252
struct {
3253
  char name[6];
3254
  u8 unknown1[10];
3255
} names[200];
3256

    
3257
#seekto 0x2400;
3258
struct {
3259
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3260
  u8 group_tone;
3261
  u8 repeat_tone;
3262
  u8 unused[13];
3263
} _5tone_std_settings[15];
3264

    
3265
#seekto 0x2500;
3266
struct {
3267
  u8 frame1[5];
3268
  u8 frame2[5];
3269
  u8 frame3[5];
3270
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3271
} _5tone_codes[15];
3272

    
3273
#seekto 0x25F0;
3274
struct {
3275
  u8 _5tone_delay1; // * 10ms
3276
  u8 _5tone_delay2; // * 10ms
3277
  u8 _5tone_delay3; // * 10ms
3278
  u8 _5tone_first_digit_ext_length;
3279
  u8 unknown1;
3280
  u8 unknown2;
3281
  u8 unknown3;
3282
  u8 unknown4;
3283
  u8 decode_standard;
3284
  u8 unknown5:5,
3285
     _5tone_decode_call_frame3:1,
3286
     _5tone_decode_call_frame2:1,
3287
     _5tone_decode_call_frame1:1;
3288
  u8 unknown6:5,
3289
     _5tone_decode_disp_frame3:1,
3290
     _5tone_decode_disp_frame2:1,
3291
     _5tone_decode_disp_frame1:1;
3292
  u8 decode_reset_time; // * 100 + 100ms
3293
} _5tone_settings;
3294

    
3295
#seekto 0x2900;
3296
struct {
3297
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3298
} dtmf_codes[15];
3299

    
3300
#seekto 0x29F0;
3301
struct {
3302
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3303
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3304
  u8 unknown0[14];
3305
  u8 inspection[16];
3306
  u8 monitor[16];
3307
  u8 alarmcode[16];
3308
  u8 stun[16];
3309
  u8 kill[16];
3310
  u8 revive[16];
3311
  u8 unknown1[16];
3312
  u8 unknown2[16];
3313
  u8 unknown3[16];
3314
  u8 unknown4[16];
3315
  u8 unknown5[16];
3316
  u8 unknown6[16];
3317
  u8 unknown7[16];
3318
  u8 masterid[16];
3319
  u8 viceid[16];
3320
  u8 unused01:7,
3321
     mastervice:1;
3322
  u8 unused02:3,
3323
     mrevive:1,
3324
     mkill:1,
3325
     mstun:1,
3326
     mmonitor:1,
3327
     minspection:1;
3328
  u8 unused03:3,
3329
     vrevive:1,
3330
     vkill:1,
3331
     vstun:1,
3332
     vmonitor:1,
3333
     vinspection:1;
3334
  u8 unused04:6,
3335
     txdisable:1,
3336
     rxdisable:1;
3337
  u8 groupcode;
3338
  u8 spacecode;
3339
  u8 delayproctime; // * 100 + 100ms
3340
  u8 resettime;     // * 100 + 100ms
3341
} dtmf_settings;
3342

    
3343
#seekto 0x2D00;
3344
struct {
3345
  struct {
3346
    ul16 freq1;
3347
    u8 unused01[6];
3348
    ul16 freq2;
3349
    u8 unused02[6];
3350
  } _2tone_encode[15];
3351
  u8 duration_1st_tone; // *10ms
3352
  u8 duration_2nd_tone; // *10ms
3353
  u8 duration_gap;      // *10ms
3354
  u8 unused03[13];
3355
  struct {
3356
    struct {
3357
      u8 dec;      // one out of LIST_2TONE_DEC
3358
      u8 response; // one out of LIST_2TONE_RESPONSE
3359
      u8 alert;    // 1-16
3360
    } decs[4];
3361
    u8 unused04[4];
3362
  } _2tone_decode[15];
3363
  u8 unused05[16];
3364

    
3365
  struct {
3366
    ul16 freqA;
3367
    ul16 freqB;
3368
    ul16 freqC;
3369
    ul16 freqD;
3370
    // unknown what those values mean, but they are
3371
    // derived from configured frequencies
3372
    ul16 derived_from_freqA; // 2304000/freqA
3373
    ul16 derived_from_freqB; // 2304000/freqB
3374
    ul16 derived_from_freqC; // 2304000/freqC
3375
    ul16 derived_from_freqD; // 2304000/freqD
3376
  }freqs[15];
3377
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3378
} _2tone;
3379

    
3380
#seekto 0x3000;
3381
struct {
3382
  u8 freq[8];
3383
  char broadcast_station_name[6];
3384
  u8 unknown[2];
3385
} fm_radio_preset[16];
3386

    
3387
#seekto 0x3C90;
3388
struct {
3389
  u8 vhf_low[3];
3390
  u8 vhf_high[3];
3391
  u8 uhf_low[3];
3392
  u8 uhf_high[3];
3393
} ranges;
3394

    
3395
// the UV-2501+220 & KT8900R has different zones for storing ranges
3396

    
3397
#seekto 0x3CD0;
3398
struct {
3399
  u8 vhf_low[3];
3400
  u8 vhf_high[3];
3401
  u8 unknown1[4];
3402
  u8 unknown2[6];
3403
  u8 vhf2_low[3];
3404
  u8 vhf2_high[3];
3405
  u8 unknown3[4];
3406
  u8 unknown4[6];
3407
  u8 uhf_low[3];
3408
  u8 uhf_high[3];
3409
} ranges220;
3410

    
3411
#seekto 0x3F70;
3412
struct {
3413
  char fp[6];
3414
} fingerprint;
3415

    
3416
"""
3417

    
3418

    
3419
class BTech(BTechMobileCommon):
3420
    """BTECH's UV-5001 and alike radios"""
3421
    BANDS = 2
3422
    COLOR_LCD = False
3423
    NAME_LENGTH = 6
3424

    
3425
    def set_options(self):
3426
        """This is to read the options from the image and set it in the
3427
        environment, for now just the limits of the freqs in the VHF/UHF
3428
        ranges"""
3429

    
3430
        # setting the correct ranges for each radio type
3431
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3432
            # the model 2501+220 has a segment in 220
3433
            # and a different position in the memmap
3434
            # also the QYT KT8900R
3435
            ranges = self._memobj.ranges220
3436
        else:
3437
            ranges = self._memobj.ranges
3438

    
3439
        # the normal dual bands
3440
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3441
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3442

    
3443
        # DEBUG
3444
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3445
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3446

    
3447
        # 220Mhz radios case
3448
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3449
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3450
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3451
            self._220_range = vhf2
3452

    
3453
        # set the class with the real data
3454
        self._vhf_range = vhf
3455
        self._uhf_range = uhf
3456

    
3457
    def process_mmap(self):
3458
        """Process the mem map into the mem object"""
3459

    
3460
        # Get it
3461
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3462

    
3463
        # load specific parameters from the radio image
3464
        self.set_options()
3465

    
3466

    
3467
# Declaring Aliases (Clones of the real radios)
3468
class JT2705M(chirp_common.Alias):
3469
    VENDOR = "Jetstream"
3470
    MODEL = "JT2705M"
3471

    
3472

    
3473
class JT6188Mini(chirp_common.Alias):
3474
    VENDOR = "Juentai"
3475
    MODEL = "JT-6188 Mini"
3476

    
3477

    
3478
class JT6188Plus(chirp_common.Alias):
3479
    VENDOR = "Juentai"
3480
    MODEL = "JT-6188 Plus"
3481

    
3482

    
3483
class SSGT890(chirp_common.Alias):
3484
    VENDOR = "Sainsonic"
3485
    MODEL = "GT-890"
3486

    
3487

    
3488
class ZastoneMP300(chirp_common.Alias):
3489
    VENDOR = "Zastone"
3490
    MODEL = "MP-300"
3491

    
3492

    
3493
# real radios
3494
@directory.register
3495
class UV2501(BTech):
3496
    """Baofeng Tech UV2501"""
3497
    MODEL = "UV-2501"
3498
    _fileid = [UV2501G3_fp,
3499
               UV2501G2_fp,
3500
               UV2501pp2_fp,
3501
               UV2501pp_fp]
3502

    
3503

    
3504
@directory.register
3505
class UV2501_220(BTech):
3506
    """Baofeng Tech UV2501+220"""
3507
    MODEL = "UV-2501+220"
3508
    BANDS = 3
3509
    _magic = MSTRING_220
3510
    _id2 = [UV2501_220pp_id, ]
3511
    _fileid = [UV2501_220G3_fp,
3512
               UV2501_220G2_fp,
3513
               UV2501_220_fp,
3514
               UV2501_220pp_fp]
3515

    
3516

    
3517
@directory.register
3518
class UV5001(BTech):
3519
    """Baofeng Tech UV5001"""
3520
    MODEL = "UV-5001"
3521
    _fileid = [UV5001G3_fp,
3522
               UV5001G22_fp,
3523
               UV5001G2_fp,
3524
               UV5001alpha_fp,
3525
               UV5001pp_fp]
3526
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3527
                     chirp_common.PowerLevel("Low", watts=10)]
3528

    
3529

    
3530
@directory.register
3531
class MINI8900(BTech):
3532
    """WACCOM MINI-8900"""
3533
    VENDOR = "WACCOM"
3534
    MODEL = "MINI-8900"
3535
    _magic = MSTRING_MINI8900
3536
    _fileid = [MINI8900_fp, ]
3537
    # Clones
3538
    ALIASES = [JT6188Plus, ]
3539

    
3540

    
3541
@directory.register
3542
class KTUV980(BTech):
3543
    """QYT KT-UV980"""
3544
    VENDOR = "QYT"
3545
    MODEL = "KT-UV980"
3546
    _vhf_range = (136000000, 175000000)
3547
    _uhf_range = (400000000, 481000000)
3548
    _magic = MSTRING_MINI8900
3549
    _fileid = [KTUV980_fp, ]
3550
    # Clones
3551
    ALIASES = [JT2705M, ]
3552

    
3553
# Please note that there is a version of this radios that is a clone of the
3554
# Waccom Mini8900, maybe an early version?
3555

    
3556

    
3557
class OTGRadioV1(chirp_common.Alias):
3558
    VENDOR = 'OTGSTUFF'
3559
    MODEL = 'OTG Radio v1'
3560

    
3561

    
3562
@directory.register
3563
class KT9800(BTech):
3564
    """QYT KT8900"""
3565
    VENDOR = "QYT"
3566
    MODEL = "KT8900"
3567
    _vhf_range = (136000000, 175000000)
3568
    _uhf_range = (400000000, 481000000)
3569
    _magic = MSTRING_KT8900
3570
    _fileid = [KT8900_fp,
3571
               KT8900_fp1,
3572
               KT8900_fp2,
3573
               KT8900_fp3,
3574
               KT8900_fp4,
3575
               KT8900_fp5,
3576
               KT8900_fp6,
3577
               KT8900_fp7]
3578
    _id2 = [KT8900_id, KT8900_id2]
3579
    # Clones
3580
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3581

    
3582

    
3583
@directory.register
3584
class KT9800R(BTech):
3585
    """QYT KT8900R"""
3586
    VENDOR = "QYT"
3587
    MODEL = "KT8900R"
3588
    BANDS = 3
3589
    _vhf_range = (136000000, 175000000)
3590
    _220_range = (240000000, 271000000)
3591
    _uhf_range = (400000000, 481000000)
3592
    _magic = MSTRING_KT8900R
3593
    _fileid = [KT8900R_fp,
3594
               KT8900R_fp1,
3595
               KT8900R_fp2,
3596
               KT8900R_fp3,
3597
               KT8900R_fp4]
3598
    _id2 = [KT8900R_id, KT8900R_id2]
3599

    
3600

    
3601
@directory.register
3602
class LT588UV(BTech):
3603
    """LUITON LT-588UV"""
3604
    VENDOR = "LUITON"
3605
    MODEL = "LT-588UV"
3606
    _vhf_range = (136000000, 175000000)
3607
    _uhf_range = (400000000, 481000000)
3608
    _magic = MSTRING_KT8900
3609
    _fileid = [LT588UV_fp,
3610
               LT588UV_fp1]
3611
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
3612
                     chirp_common.PowerLevel("Low", watts=10)]
3613

    
3614

    
3615
COLOR_MEM_FORMAT = """
3616
#seekto 0x0000;
3617
struct {
3618
  lbcd rxfreq[4];
3619
  lbcd txfreq[4];
3620
  ul16 rxtone;
3621
  ul16 txtone;
3622
  u8 unknown0:4,
3623
     scode:4;
3624
  u8 unknown1:2,
3625
     spmute:2,
3626
     unknown2:2,
3627
     optsig:2;
3628
  u8 unknown3:3,
3629
     scramble:1,
3630
     unknown4:2,
3631
     power:2;
3632
  u8 unknown5:1,
3633
     wide:1,
3634
     unknown6:2,
3635
     bcl:1,
3636
     add:1,
3637
     pttid:2;
3638
} memory[200];
3639

    
3640
#seekto 0x0E00;
3641
struct {
3642
  u8 tmr;
3643
  u8 unknown1;
3644
  u8 sql;
3645
  u8 unknown2;
3646
  u8 mgain2;
3647
  u8 tot;
3648
  u8 apo;
3649
  u8 unknown3;
3650
  u8 abr;
3651
  u8 beep;
3652
  u8 unknown4[4];
3653
  u8 dtmfst;
3654
  u8 unknown5[2];
3655
  u8 screv;
3656
  u8 unknown6[2];
3657
  u8 pttid;
3658
  u8 pttlt;
3659
  u8 unknown7;
3660
  u8 emctp;
3661
  u8 emcch;
3662
  u8 sigbp;
3663
  u8 unknown8;
3664
  u8 camdf;
3665
  u8 cbmdf;
3666
  u8 ccmdf;
3667
  u8 cdmdf;
3668
  u8 langua;
3669
  u8 sync;          // BTech radios use this as the display sync
3670
                    // setting, other radios use this as the auto
3671
                    // keypad lock setting
3672
  u8 mainfc;
3673
  u8 mainbc;
3674
  u8 menufc;
3675
  u8 menubc;
3676
  u8 stafc;
3677
  u8 stabc;
3678
  u8 sigfc;
3679
  u8 sigbc;
3680
  u8 rxfc;
3681
  u8 txfc;
3682
  u8 txdisp;
3683
  u8 unknown9[5];
3684
  u8 anil;
3685
  u8 reps;
3686
  u8 repm;
3687
  u8 tmrmr;
3688
  u8 ste;
3689
  u8 rpste;
3690
  u8 rptdl;
3691
  u8 dtmfg;
3692
  u8 mgain;         // used by db25-g for ponyey
3693
  u8 skiptx;
3694
  u8 scmode;
3695
} settings;
3696

    
3697
#seekto 0x0E80;
3698
struct {
3699
  u8 unknown1;
3700
  u8 vfomr;
3701
  u8 keylock;
3702
  u8 unknown2;
3703
  u8 unknown3:4,
3704
     vfomren:1,
3705
     unknown4:1,
3706
     reseten:1,
3707
     menuen:1;
3708
  u8 unknown5[11];
3709
  u8 dispab;
3710
  u8 unknown6[2];
3711
  u8 menu;
3712
  u8 unknown7[7];
3713
  u8 vfomra;
3714
  u8 vfomrb;
3715
  u8 vfomrc;
3716
  u8 vfomrd;
3717
  u8 mrcha;
3718
  u8 mrchb;
3719
  u8 mrchc;
3720
  u8 mrchd;
3721
} settings2;
3722

    
3723
struct settings_vfo {
3724
  u8 freq[8];
3725
  u8 offset[6];
3726
  u8 unknown2[2];
3727
  ul16 rxtone;
3728
  ul16 txtone;
3729
  u8 scode;
3730
  u8 spmute;
3731
  u8 optsig;
3732
  u8 scramble;
3733
  u8 wide;
3734
  u8 power;
3735
  u8 shiftd;
3736
  u8 step;
3737
  u8 unknown3[4];
3738
};
3739

    
3740
#seekto 0x0F00;
3741
struct {
3742
  struct settings_vfo a;
3743
  struct settings_vfo b;
3744
  struct settings_vfo c;
3745
  struct settings_vfo d;
3746
} vfo;
3747

    
3748
#seekto 0x0F80;
3749
struct {
3750
  char line1[8];
3751
  char line2[8];
3752
  char line3[8];
3753
  char line4[8];
3754
  char line5[8];
3755
  char line6[8];
3756
  char line7[8];
3757
  char line8[8];
3758
} poweron_msg;
3759

    
3760
#seekto 0x1000;
3761
struct {
3762
  char name[8];
3763
  u8 unknown1[8];
3764
} names[200];
3765

    
3766
#seekto 0x2400;
3767
struct {
3768
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3769
  u8 group_tone;
3770
  u8 repeat_tone;
3771
  u8 unused[13];
3772
} _5tone_std_settings[15];
3773

    
3774
#seekto 0x2500;
3775
struct {
3776
  u8 frame1[5];
3777
  u8 frame2[5];
3778
  u8 frame3[5];
3779
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3780
} _5tone_codes[15];
3781

    
3782
#seekto 0x25F0;
3783
struct {
3784
  u8 _5tone_delay1; // * 10ms
3785
  u8 _5tone_delay2; // * 10ms
3786
  u8 _5tone_delay3; // * 10ms
3787
  u8 _5tone_first_digit_ext_length;
3788
  u8 unknown1;
3789
  u8 unknown2;
3790
  u8 unknown3;
3791
  u8 unknown4;
3792
  u8 decode_standard;
3793
  u8 unknown5:5,
3794
     _5tone_decode_call_frame3:1,
3795
     _5tone_decode_call_frame2:1,
3796
     _5tone_decode_call_frame1:1;
3797
  u8 unknown6:5,
3798
     _5tone_decode_disp_frame3:1,
3799
     _5tone_decode_disp_frame2:1,
3800
     _5tone_decode_disp_frame1:1;
3801
  u8 decode_reset_time; // * 100 + 100ms
3802
} _5tone_settings;
3803

    
3804
#seekto 0x2900;
3805
struct {
3806
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3807
} dtmf_codes[15];
3808

    
3809
#seekto 0x29F0;
3810
struct {
3811
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3812
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3813
  u8 unknown0[14];
3814
  u8 inspection[16];
3815
  u8 monitor[16];
3816
  u8 alarmcode[16];
3817
  u8 stun[16];
3818
  u8 kill[16];
3819
  u8 revive[16];
3820
  u8 unknown1[16];
3821
  u8 unknown2[16];
3822
  u8 unknown3[16];
3823
  u8 unknown4[16];
3824
  u8 unknown5[16];
3825
  u8 unknown6[16];
3826
  u8 unknown7[16];
3827
  u8 masterid[16];
3828
  u8 viceid[16];
3829
  u8 unused01:7,
3830
     mastervice:1;
3831
  u8 unused02:3,
3832
     mrevive:1,
3833
     mkill:1,
3834
     mstun:1,
3835
     mmonitor:1,
3836
     minspection:1;
3837
  u8 unused03:3,
3838
     vrevive:1,
3839
     vkill:1,
3840
     vstun:1,
3841
     vmonitor:1,
3842
     vinspection:1;
3843
  u8 unused04:6,
3844
     txdisable:1,
3845
     rxdisable:1;
3846
  u8 groupcode;
3847
  u8 spacecode;
3848
  u8 delayproctime; // * 100 + 100ms
3849
  u8 resettime;     // * 100 + 100ms
3850
} dtmf_settings;
3851

    
3852
#seekto 0x2D00;
3853
struct {
3854
  struct {
3855
    ul16 freq1;
3856
    u8 unused01[6];
3857
    ul16 freq2;
3858
    u8 unused02[6];
3859
  } _2tone_encode[15];
3860
  u8 duration_1st_tone; // *10ms
3861
  u8 duration_2nd_tone; // *10ms
3862
  u8 duration_gap;      // *10ms
3863
  u8 unused03[13];
3864
  struct {
3865
    struct {
3866
      u8 dec;      // one out of LIST_2TONE_DEC
3867
      u8 response; // one out of LIST_2TONE_RESPONSE
3868
      u8 alert;    // 1-16
3869
    } decs[4];
3870
    u8 unused04[4];
3871
  } _2tone_decode[15];
3872
  u8 unused05[16];
3873

    
3874
  struct {
3875
    ul16 freqA;
3876
    ul16 freqB;
3877
    ul16 freqC;
3878
    ul16 freqD;
3879
    // unknown what those values mean, but they are
3880
    // derived from configured frequencies
3881
    ul16 derived_from_freqA; // 2304000/freqA
3882
    ul16 derived_from_freqB; // 2304000/freqB
3883
    ul16 derived_from_freqC; // 2304000/freqC
3884
    ul16 derived_from_freqD; // 2304000/freqD
3885
  }freqs[15];
3886
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3887
} _2tone;
3888

    
3889
#seekto 0x3D80;
3890
struct {
3891
  u8 vhf_low[3];
3892
  u8 vhf_high[3];
3893
  u8 unknown1[4];
3894
  u8 unknown2[6];
3895
  u8 vhf2_low[3];
3896
  u8 vhf2_high[3];
3897
  u8 unknown3[4];
3898
  u8 unknown4[6];
3899
  u8 uhf_low[3];
3900
  u8 uhf_high[3];
3901
  u8 unknown5[4];
3902
  u8 unknown6[6];
3903
  u8 uhf2_low[3];
3904
  u8 uhf2_high[3];
3905
} ranges;
3906

    
3907
#seekto 0x3F70;
3908
struct {
3909
  char fp[6];
3910
} fingerprint;
3911

    
3912
"""
3913

    
3914

    
3915
class BTechColor(BTechMobileCommon):
3916
    """BTECH's Color LCD Mobile and alike radios"""
3917
    COLOR_LCD = True
3918
    NAME_LENGTH = 8
3919
    LIST_TMR = LIST_TMR16
3920

    
3921
    def process_mmap(self):
3922
        """Process the mem map into the mem object"""
3923

    
3924
        # Get it
3925
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3926

    
3927
        # load specific parameters from the radio image
3928
        self.set_options()
3929

    
3930
    def set_options(self):
3931
        """This is to read the options from the image and set it in the
3932
        environment, for now just the limits of the freqs in the VHF/UHF
3933
        ranges"""
3934

    
3935
        # setting the correct ranges for each radio type
3936
        ranges = self._memobj.ranges
3937

    
3938
        # the normal dual bands
3939
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3940
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3941

    
3942
        # DEBUG
3943
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3944
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3945

    
3946
        # the additional bands
3947
        if self.MODEL in ["UV-25X4", "KT7900D"]:
3948
            # 200Mhz band
3949
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3950
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3951
            self._220_range = vhf2
3952

    
3953
            # 350Mhz band
3954
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
3955
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
3956
            self._350_range = uhf2
3957

    
3958
        # set the class with the real data
3959
        self._vhf_range = vhf
3960
        self._uhf_range = uhf
3961

    
3962

    
3963
# Declaring Aliases (Clones of the real radios)
3964
class SKT8900D(chirp_common.Alias):
3965
    VENDOR = "Surecom"
3966
    MODEL = "S-KT8900D"
3967

    
3968

    
3969
class QB25(chirp_common.Alias):
3970
    VENDOR = "Radioddity"
3971
    MODEL = "QB25"
3972

    
3973

    
3974
# real radios
3975
@directory.register
3976
class UV25X2(BTechColor):
3977
    """Baofeng Tech UV25X2"""
3978
    MODEL = "UV-25X2"
3979
    BANDS = 2
3980
    _vhf_range = (130000000, 180000000)
3981
    _uhf_range = (400000000, 521000000)
3982
    _magic = MSTRING_UV25X2
3983
    _fileid = [UV25X2_fp, ]
3984

    
3985

    
3986
@directory.register
3987
class UV25X4(BTechColor):
3988
    """Baofeng Tech UV25X4"""
3989
    MODEL = "UV-25X4"
3990
    BANDS = 4
3991
    _vhf_range = (130000000, 180000000)
3992
    _220_range = (200000000, 271000000)
3993
    _uhf_range = (400000000, 521000000)
3994
    _350_range = (350000000, 391000000)
3995
    _magic = MSTRING_UV25X4
3996
    _fileid = [UV25X4_fp, ]
3997

    
3998

    
3999
@directory.register
4000
class UV50X2(BTechColor):
4001
    """Baofeng Tech UV50X2"""
4002
    MODEL = "UV-50X2"
4003
    BANDS = 2
4004
    _vhf_range = (130000000, 180000000)
4005
    _uhf_range = (400000000, 521000000)
4006
    _magic = MSTRING_UV25X2
4007
    _fileid = [UV50X2_fp, ]
4008
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4009
                     chirp_common.PowerLevel("Low", watts=10)]
4010

    
4011

    
4012
@directory.register
4013
class KT7900D(BTechColor):
4014
    """QYT KT7900D"""
4015
    VENDOR = "QYT"
4016
    MODEL = "KT7900D"
4017
    BANDS = 4
4018
    LIST_TMR = LIST_TMR15
4019
    _vhf_range = (136000000, 175000000)
4020
    _220_range = (200000000, 271000000)
4021
    _uhf_range = (400000000, 481000000)
4022
    _350_range = (350000000, 371000000)
4023
    _magic = MSTRING_KT8900D
4024
    _fileid = [KT7900D_fp, KT7900D_fp1, KT7900D_fp2, KT7900D_fp3, KT7900D_fp4,
4025
               KT7900D_fp5, KT7900D_fp6, KT7900D_fp7, QB25_fp, ]
4026
    # Clones
4027
    ALIASES = [SKT8900D, QB25, ]
4028

    
4029

    
4030
@directory.register
4031
class KT8900D(BTechColor):
4032
    """QYT KT8900D"""
4033
    VENDOR = "QYT"
4034
    MODEL = "KT8900D"
4035
    BANDS = 2
4036
    LIST_TMR = LIST_TMR15
4037
    _vhf_range = (136000000, 175000000)
4038
    _uhf_range = (400000000, 481000000)
4039
    _magic = MSTRING_KT8900D
4040
    _fileid = [KT8900D_fp3, KT8900D_fp2, KT8900D_fp1, KT8900D_fp]
4041

    
4042
    # Clones
4043
    ALIASES = [OTGRadioV1]
4044

    
4045

    
4046
@directory.register
4047
class KT5800(BTechColor):
4048
    """QYT KT5800"""
4049
    VENDOR = "QYT"
4050
    MODEL = "KT5800"
4051
    BANDS = 2
4052
    LIST_TMR = LIST_TMR15
4053
    _vhf_range = (136000000, 175000000)
4054
    _uhf_range = (400000000, 481000000)
4055
    _magic = MSTRING_KT8900D
4056
    _fileid = [KT5800_fp, ]
4057

    
4058

    
4059
@directory.register
4060
class KT980PLUS(BTechColor):
4061
    """QYT KT980PLUS"""
4062
    VENDOR = "QYT"
4063
    MODEL = "KT980PLUS"
4064
    BANDS = 2
4065
    LIST_TMR = LIST_TMR15
4066
    _vhf_range = (136000000, 175000000)
4067
    _uhf_range = (400000000, 481000000)
4068
    _magic = MSTRING_KT8900D
4069
    _fileid = [KT980PLUS_fp1, KT980PLUS_fp]
4070
    _power_levels = [chirp_common.PowerLevel("High", watts=75),
4071
                     chirp_common.PowerLevel("Low", watts=55)]
4072

    
4073
    @classmethod
4074
    def match_model(cls, filedata, filename):
4075
        # This model is only ever matched via metadata
4076
        return False
4077

    
4078

    
4079
@directory.register
4080
class DB25G(BTechColor):
4081
    """Radioddity DB25-G"""
4082
    VENDOR = "Radioddity"
4083
    MODEL = "DB25-G"
4084
    BANDS = 2
4085
    LIST_TMR = LIST_TMR15
4086
    _vhf_range = (136000000, 175000000)
4087
    _uhf_range = (400000000, 481000000)
4088
    _magic = MSTRING_KT8900D
4089
    _fileid = [DB25G_fp, ]
4090
    _gmrs = True
4091
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
4092
                     chirp_common.PowerLevel("Mid", watts=15),
4093
                     chirp_common.PowerLevel("Low", watts=5)]
4094

    
4095
    @classmethod
4096
    def match_model(cls, filedata, filename):
4097
        # This model is only ever matched via metadata
4098
        return False
4099

    
4100

    
4101
GMRS_MEM_FORMAT = """
4102
#seekto 0x0000;
4103
struct {
4104
  lbcd rxfreq[4];
4105
  lbcd txfreq[4];
4106
  ul16 rxtone;
4107
  ul16 txtone;
4108
  u8 unknown0:4,
4109
     scode:4;
4110
  u8 unknown1:2,
4111
     spmute:2,
4112
     unknown2:2,
4113
     optsig:2;
4114
  u8 unknown3:3,
4115
     scramble:1,
4116
     unknown4:2,
4117
     power:2;
4118
  u8 unknown5:1,
4119
     wide:1,
4120
     unknown6:2,
4121
     bcl:1,
4122
     add:1,
4123
     pttid:2;
4124
} memory[256];
4125

    
4126
#seekto 0x1000;
4127
struct {
4128
  char name[7];
4129
  u8 unknown1[9];
4130
} names[256];
4131

    
4132
#seekto 0x2400;
4133
struct {
4134
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4135
  u8 group_tone;
4136
  u8 repeat_tone;
4137
  u8 unused[13];
4138
} _5tone_std_settings[15];
4139

    
4140
#seekto 0x2500;
4141
struct {
4142
  u8 frame1[5];
4143
  u8 frame2[5];
4144
  u8 frame3[5];
4145
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4146
} _5tone_codes[15];
4147

    
4148
#seekto 0x25F0;
4149
struct {
4150
  u8 _5tone_delay1; // * 10ms
4151
  u8 _5tone_delay2; // * 10ms
4152
  u8 _5tone_delay3; // * 10ms
4153
  u8 _5tone_first_digit_ext_length;
4154
  u8 unknown1;
4155
  u8 unknown2;
4156
  u8 unknown3;
4157
  u8 unknown4;
4158
  u8 decode_standard;
4159
  u8 unknown5:5,
4160
     _5tone_decode_call_frame3:1,
4161
     _5tone_decode_call_frame2:1,
4162
     _5tone_decode_call_frame1:1;
4163
  u8 unknown6:5,
4164
     _5tone_decode_disp_frame3:1,
4165
     _5tone_decode_disp_frame2:1,
4166
     _5tone_decode_disp_frame1:1;
4167
  u8 decode_reset_time; // * 100 + 100ms
4168
} _5tone_settings;
4169

    
4170
#seekto 0x2900;
4171
struct {
4172
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4173
} dtmf_codes[15];
4174

    
4175
#seekto 0x29F0;
4176
struct {
4177
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4178
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4179
  u8 unknown0[14];
4180
  u8 inspection[16];
4181
  u8 monitor[16];
4182
  u8 alarmcode[16];
4183
  u8 stun[16];
4184
  u8 kill[16];
4185
  u8 revive[16];
4186
  u8 unknown1[16];
4187
  u8 unknown2[16];
4188
  u8 unknown3[16];
4189
  u8 unknown4[16];
4190
  u8 unknown5[16];
4191
  u8 unknown6[16];
4192
  u8 unknown7[16];
4193
  u8 masterid[16];
4194
  u8 viceid[16];
4195
  u8 unused01:7,
4196
     mastervice:1;
4197
  u8 unused02:3,
4198
     mrevive:1,
4199
     mkill:1,
4200
     mstun:1,
4201
     mmonitor:1,
4202
     minspection:1;
4203
  u8 unused03:3,
4204
     vrevive:1,
4205
     vkill:1,
4206
     vstun:1,
4207
     vmonitor:1,
4208
     vinspection:1;
4209
  u8 unused04:6,
4210
     txdisable:1,
4211
     rxdisable:1;
4212
  u8 groupcode;
4213
  u8 spacecode;
4214
  u8 delayproctime; // * 100 + 100ms
4215
  u8 resettime;     // * 100 + 100ms
4216
} dtmf_settings;
4217

    
4218
#seekto 0x2D00;
4219
struct {
4220
  struct {
4221
    ul16 freq1;
4222
    u8 unused01[6];
4223
    ul16 freq2;
4224
    u8 unused02[6];
4225
  } _2tone_encode[15];
4226
  u8 duration_1st_tone; // *10ms
4227
  u8 duration_2nd_tone; // *10ms
4228
  u8 duration_gap;      // *10ms
4229
  u8 unused03[13];
4230
  struct {
4231
    struct {
4232
      u8 dec;      // one out of LIST_2TONE_DEC
4233
      u8 response; // one out of LIST_2TONE_RESPONSE
4234
      u8 alert;    // 1-16
4235
    } decs[4];
4236
    u8 unused04[4];
4237
  } _2tone_decode[15];
4238
  u8 unused05[16];
4239

    
4240
  struct {
4241
    ul16 freqA;
4242
    ul16 freqB;
4243
    ul16 freqC;
4244
    ul16 freqD;
4245
    // unknown what those values mean, but they are
4246
    // derived from configured frequencies
4247
    ul16 derived_from_freqA; // 2304000/freqA
4248
    ul16 derived_from_freqB; // 2304000/freqB
4249
    ul16 derived_from_freqC; // 2304000/freqC
4250
    ul16 derived_from_freqD; // 2304000/freqD
4251
  }freqs[15];
4252
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4253
} _2tone;
4254

    
4255
#seekto 0x3000;
4256
struct {
4257
  u8 freq[8];
4258
  char broadcast_station_name[6];
4259
  u8 unknown[2];
4260
} fm_radio_preset[16];
4261

    
4262
#seekto 0x3200;
4263
struct {
4264
  u8 tmr;
4265
  u8 unknown1;
4266
  u8 sql;
4267
  u8 unknown2;
4268
  u8 autolk;
4269
  u8 tot;
4270
  u8 apo;
4271
  u8 unknown3;
4272
  u8 abr;
4273
  u8 beep;
4274
  u8 unknown4[4];
4275
  u8 dtmfst;
4276
  u8 unknown5[2];
4277
  u8 screv;
4278
  u8 unknown6[2];
4279
  u8 pttid;
4280
  u8 pttlt;
4281
  u8 unknown7;
4282
  u8 emctp;
4283
  u8 emcch;
4284
  u8 sigbp;
4285
  u8 unknown8;
4286
  u8 camdf;
4287
  u8 cbmdf;
4288
  u8 ccmdf;
4289
  u8 cdmdf;
4290
  u8 langua;
4291
  u8 sync;
4292

    
4293

    
4294
  u8 stfc;
4295
  u8 mffc;
4296
  u8 sfafc;
4297
  u8 sfbfc;
4298
  u8 sfcfc;
4299
  u8 sfdfc;
4300
  u8 subfc;
4301
  u8 fmfc;
4302
  u8 sigfc;
4303
  u8 modfc;
4304
  u8 menufc;
4305
  u8 txfc;
4306
  u8 txdisp;
4307
  u8 unknown9[5];
4308
  u8 anil;
4309
  u8 reps;
4310
  u8 repm;
4311
  u8 tmrmr;
4312
  u8 ste;
4313
  u8 rpste;
4314
  u8 rptdl;
4315
  u8 dtmfg;
4316
  u8 mgain;
4317
  u8 skiptx;
4318
  u8 scmode;
4319
} settings;
4320

    
4321
#seekto 0x3280;
4322
struct {
4323
  u8 unknown1;
4324
  u8 vfomr;
4325
  u8 keylock;
4326
  u8 unknown2;
4327
  u8 unknown3:4,
4328
     vfomren:1,
4329
     unknown4:1,
4330
     reseten:1,
4331
     menuen:1;
4332
  u8 unknown5[11];
4333
  u8 dispab;
4334
  u8 unknown6[2];
4335
  u8 smenu;
4336
  u8 unknown7[7];
4337
  u8 vfomra;
4338
  u8 vfomrb;
4339
  u8 vfomrc;
4340
  u8 vfomrd;
4341
  u8 mrcha;
4342
  u8 mrchb;
4343
  u8 mrchc;
4344
  u8 mrchd;
4345
} settings2;
4346

    
4347
struct settings_vfo {
4348
  u8 freq[8];
4349
  u8 offset[6];
4350
  u8 unknown2[2];
4351
  ul16 rxtone;
4352
  ul16 txtone;
4353
  u8 scode;
4354
  u8 spmute;
4355
  u8 optsig;
4356
  u8 scramble;
4357
  u8 wide;
4358
  u8 power;
4359
  u8 shiftd;
4360
  u8 step;
4361
  u8 unknown3[4];
4362
};
4363

    
4364
#seekto 0x3300;
4365
struct {
4366
  struct settings_vfo a;
4367
  struct settings_vfo b;
4368
  struct settings_vfo c;
4369
  struct settings_vfo d;
4370
} vfo;
4371

    
4372
#seekto 0x3D80;
4373
struct {
4374
  u8 vhf_low[3];
4375
  u8 vhf_high[3];
4376
  u8 unknown1[4];
4377
  u8 unknown2[6];
4378
  u8 vhf2_low[3];
4379
  u8 vhf2_high[3];
4380
  u8 unknown3[4];
4381
  u8 unknown4[6];
4382
  u8 uhf_low[3];
4383
  u8 uhf_high[3];
4384
  u8 unknown5[4];
4385
  u8 unknown6[6];
4386
  u8 uhf2_low[3];
4387
  u8 uhf2_high[3];
4388
} ranges;
4389

    
4390
#seekto 0x33B0;
4391
struct {
4392
  char line[16];
4393
} static_msg;
4394

    
4395
#seekto 0x3F70;
4396
struct {
4397
  char fp[6];
4398
} fingerprint;
4399

    
4400
"""
4401

    
4402

    
4403
class BTechGMRS(BTechMobileCommon):
4404
    """BTECH's GMRS Mobile"""
4405
    COLOR_LCD = True
4406
    COLOR_LCD2 = True
4407
    NAME_LENGTH = 7
4408
    UPLOAD_MEM_SIZE = 0X3400
4409

    
4410
    def process_mmap(self):
4411
        """Process the mem map into the mem object"""
4412

    
4413
        # Get it
4414
        self._memobj = bitwise.parse(GMRS_MEM_FORMAT, self._mmap)
4415

    
4416
        # load specific parameters from the radio image
4417
        self.set_options()
4418

    
4419
    def set_options(self):
4420
        """This is to read the options from the image and set it in the
4421
        environment, for now just the limits of the freqs in the VHF/UHF
4422
        ranges"""
4423

    
4424
        # setting the correct ranges for each radio type
4425
        ranges = self._memobj.ranges
4426

    
4427
        # the normal dual bands
4428
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4429
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4430

    
4431
        # DEBUG
4432
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4433
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4434

    
4435
        # set the class with the real data
4436
        self._vhf_range = vhf
4437
        self._uhf_range = uhf
4438

    
4439

    
4440
# real radios
4441
@directory.register
4442
class GMRS50X1(BTechGMRS):
4443
    """Baofeng Tech GMRS50X1"""
4444
    MODEL = "GMRS-50X1"
4445
    BANDS = 2
4446
    LIST_TMR = LIST_TMR16
4447
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4448
                     chirp_common.PowerLevel("Mid", watts=10),
4449
                     chirp_common.PowerLevel("Low", watts=5)]
4450
    _vhf_range = (136000000, 175000000)
4451
    _uhf_range = (400000000, 521000000)
4452
    _upper = 255
4453
    _magic = MSTRING_GMRS50X1
4454
    _fileid = [GMRS50X1_fp1, GMRS50X1_fp, ]
4455

    
4456

    
4457
COLORHT_MEM_FORMAT = """
4458
#seekto 0x0000;
4459
struct {
4460
  lbcd rxfreq[4];
4461
  lbcd txfreq[4];
4462
  ul16 rxtone;
4463
  ul16 txtone;
4464
  u8 unknown0:4,
4465
     scode:4;
4466
  u8 unknown1:2,
4467
     spmute:2,
4468
     unknown2:2,
4469
     optsig:2;
4470
  u8 unknown3:3,
4471
     scramble:1,
4472
     unknown4:3,
4473
     power:1;
4474
  u8 unknown5:1,
4475
     wide:1,
4476
     unknown6:2,
4477
     bcl:1,
4478
     add:1,
4479
     pttid:2;
4480
} memory[200];
4481

    
4482
#seekto 0x0E00;
4483
struct {
4484
  u8 tmr;
4485
  u8 unknownE01;
4486
  u8 sql;
4487
  u8 unknownE03[2];
4488
  u8 tot;
4489
  u8 save;
4490
  u8 unknownE07;
4491
  u8 abr;
4492
  u8 beep;
4493
  u8 unknownE0A[4];
4494
  u8 dsub;
4495
  u8 dtmfst;
4496
  u8 screv;
4497
  u8 unknownE11[3];
4498
  u8 pttid;
4499
  u8 unknownE15;
4500
  u8 pttlt;
4501
  u8 unknownE17;
4502
  u8 emctp;
4503
  u8 emcch;
4504
  u8 sigbp;
4505
  u8 unknownE1B;
4506
  u8 camdf;
4507
  u8 cbmdf;
4508
  u8 ccmdf;
4509
  u8 cdmdf;
4510
  u8 langua;
4511
  u8 voice;
4512
  u8 vox;
4513
  u8 voxt;
4514
  u8 sync;          // BTech radios use this as the display sync setting
4515
                    // other radios use this as the auto keypad lock setting
4516
  u8 stfc;
4517
  u8 mffc;
4518
  u8 sfafc;
4519
  u8 sfbfc;
4520
  u8 sfcfc;
4521
  u8 sfdfc;
4522
  u8 subfc;
4523
  u8 fmfc;
4524
  u8 sigfc;
4525
  u8 menufc;
4526
  u8 txfc;
4527
  u8 rxfc;
4528
  u8 unknownE31[5];
4529
  u8 anil;
4530
  u8 reps;
4531
  u8 tmrmr;
4532
  u8 ste;
4533
  u8 rpste;
4534
  u8 rptdl;
4535
  u8 dtmfg;
4536
  u8 tmrtx;
4537
} settings;
4538

    
4539
#seekto 0x0E80;
4540
struct {
4541
  u8 unknown1;
4542
  u8 vfomr;
4543
  u8 keylock;
4544
  u8 unknown2;
4545
  u8 unknown3:4,
4546
     vfomren:1,
4547
     unknown4:1,
4548
     reseten:1,
4549
     menuen:1;
4550
  u8 unknown5[11];
4551
  u8 dispab;
4552
  u8 unknown6[2];
4553
  u8 menu;
4554
  u8 unknown7[7];
4555
  u8 vfomra;
4556
  u8 vfomrb;
4557
  u8 vfomrc;
4558
  u8 vfomrd;
4559
  u8 mrcha;
4560
  u8 mrchb;
4561
  u8 mrchc;
4562
  u8 mrchd;
4563
} settings2;
4564

    
4565
struct settings_vfo {
4566
  u8 freq[8];
4567
  u8 offset[6];
4568
  u8 unknown2[2];
4569
  ul16 rxtone;
4570
  ul16 txtone;
4571
  u8 scode;
4572
  u8 spmute;
4573
  u8 optsig;
4574
  u8 scramble;
4575
  u8 wide;
4576
  u8 power;
4577
  u8 shiftd;
4578
  u8 step;
4579
  u8 unknown3[4];
4580
};
4581

    
4582
#seekto 0x0F00;
4583
struct {
4584
  struct settings_vfo a;
4585
  struct settings_vfo b;
4586
  struct settings_vfo c;
4587
  struct settings_vfo d;
4588
} vfo;
4589

    
4590
#seekto 0x0FE0;
4591
struct {
4592
  char line[16];
4593
} static_msg;
4594

    
4595
#seekto 0x1000;
4596
struct {
4597
  char name[8];
4598
  u8 unknown1[8];
4599
} names[200];
4600

    
4601
#seekto 0x2400;
4602
struct {
4603
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4604
  u8 group_tone;
4605
  u8 repeat_tone;
4606
  u8 unused[13];
4607
} _5tone_std_settings[15];
4608

    
4609
#seekto 0x2500;
4610
struct {
4611
  u8 frame1[5];
4612
  u8 frame2[5];
4613
  u8 frame3[5];
4614
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4615
} _5tone_codes[15];
4616

    
4617
#seekto 0x25F0;
4618
struct {
4619
  u8 _5tone_delay1; // * 10ms
4620
  u8 _5tone_delay2; // * 10ms
4621
  u8 _5tone_delay3; // * 10ms
4622
  u8 _5tone_first_digit_ext_length;
4623
  u8 unknown1;
4624
  u8 unknown2;
4625
  u8 unknown3;
4626
  u8 unknown4;
4627
  u8 decode_standard;
4628
  u8 unknown5:5,
4629
     _5tone_decode_call_frame3:1,
4630
     _5tone_decode_call_frame2:1,
4631
     _5tone_decode_call_frame1:1;
4632
  u8 unknown6:5,
4633
     _5tone_decode_disp_frame3:1,
4634
     _5tone_decode_disp_frame2:1,
4635
     _5tone_decode_disp_frame1:1;
4636
  u8 decode_reset_time; // * 100 + 100ms
4637
} _5tone_settings;
4638

    
4639
#seekto 0x2900;
4640
struct {
4641
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4642
} dtmf_codes[15];
4643

    
4644
#seekto 0x29F0;
4645
struct {
4646
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4647
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4648
  u8 unknown0[14];
4649
  u8 inspection[16];
4650
  u8 monitor[16];
4651
  u8 alarmcode[16];
4652
  u8 stun[16];
4653
  u8 kill[16];
4654
  u8 revive[16];
4655
  u8 unknown1[16];
4656
  u8 unknown2[16];
4657
  u8 unknown3[16];
4658
  u8 unknown4[16];
4659
  u8 unknown5[16];
4660
  u8 unknown6[16];
4661
  u8 unknown7[16];
4662
  u8 masterid[16];
4663
  u8 viceid[16];
4664
  u8 unused01:7,
4665
     mastervice:1;
4666
  u8 unused02:3,
4667
     mrevive:1,
4668
     mkill:1,
4669
     mstun:1,
4670
     mmonitor:1,
4671
     minspection:1;
4672
  u8 unused03:3,
4673
     vrevive:1,
4674
     vkill:1,
4675
     vstun:1,
4676
     vmonitor:1,
4677
     vinspection:1;
4678
  u8 unused04:6,
4679
     txdisable:1,
4680
     rxdisable:1;
4681
  u8 groupcode;
4682
  u8 spacecode;
4683
  u8 delayproctime; // * 100 + 100ms
4684
  u8 resettime;     // * 100 + 100ms
4685
} dtmf_settings;
4686

    
4687
#seekto 0x2D00;
4688
struct {
4689
  struct {
4690
    ul16 freq1;
4691
    u8 unused01[6];
4692
    ul16 freq2;
4693
    u8 unused02[6];
4694
  } _2tone_encode[15];
4695
  u8 duration_1st_tone; // *10ms
4696
  u8 duration_2nd_tone; // *10ms
4697
  u8 duration_gap;      // *10ms
4698
  u8 unused03[13];
4699
  struct {
4700
    struct {
4701
      u8 dec;      // one out of LIST_2TONE_DEC
4702
      u8 response; // one out of LIST_2TONE_RESPONSE
4703
      u8 alert;    // 1-16
4704
    } decs[4];
4705
    u8 unused04[4];
4706
  } _2tone_decode[15];
4707
  u8 unused05[16];
4708

    
4709
  struct {
4710
    ul16 freqA;
4711
    ul16 freqB;
4712
    ul16 freqC;
4713
    ul16 freqD;
4714
    // unknown what those values mean, but they are
4715
    // derived from configured frequencies
4716
    ul16 derived_from_freqA; // 2304000/freqA
4717
    ul16 derived_from_freqB; // 2304000/freqB
4718
    ul16 derived_from_freqC; // 2304000/freqC
4719
    ul16 derived_from_freqD; // 2304000/freqD
4720
  }freqs[15];
4721
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4722
} _2tone;
4723

    
4724
#seekto 0x3D80;
4725
struct {
4726
  u8 vhf_low[3];
4727
  u8 vhf_high[3];
4728
  u8 unknown1[4];
4729
  u8 unknown2[6];
4730
  u8 vhf2_low[3];
4731
  u8 vhf2_high[3];
4732
  u8 unknown3[4];
4733
  u8 unknown4[6];
4734
  u8 uhf_low[3];
4735
  u8 uhf_high[3];
4736
  u8 unknown5[4];
4737
  u8 unknown6[6];
4738
  u8 uhf2_low[3];
4739
  u8 uhf2_high[3];
4740
} ranges;
4741

    
4742
#seekto 0x3F70;
4743
struct {
4744
  char fp[6];
4745
} fingerprint;
4746

    
4747
"""
4748

    
4749

    
4750
class QYTColorHT(BTechMobileCommon):
4751
    """QTY's Color LCD Handheld and alike radios"""
4752
    COLOR_LCD = True
4753
    COLOR_LCD3 = True
4754
    NAME_LENGTH = 8
4755
    LIST_TMR = LIST_TMR15
4756

    
4757
    def process_mmap(self):
4758
        """Process the mem map into the mem object"""
4759

    
4760
        # Get it
4761
        self._memobj = bitwise.parse(COLORHT_MEM_FORMAT, self._mmap)
4762

    
4763
        # load specific parameters from the radio image
4764
        self.set_options()
4765

    
4766
    def set_options(self):
4767
        """This is to read the options from the image and set it in the
4768
        environment, for now just the limits of the freqs in the VHF/UHF
4769
        ranges"""
4770

    
4771
        # setting the correct ranges for each radio type
4772
        ranges = self._memobj.ranges
4773

    
4774
        # the normal dual bands
4775
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4776
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4777

    
4778
        # DEBUG
4779
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4780
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4781

    
4782
        # the additional bands
4783
        if self.MODEL in ["KT-8R"]:
4784
            # 200Mhz band
4785
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4786
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4787
            self._220_range = vhf2
4788

    
4789
            # 350Mhz band
4790
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4791
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4792
            self._350_range = uhf2
4793

    
4794
        # set the class with the real data
4795
        self._vhf_range = vhf
4796
        self._uhf_range = uhf
4797

    
4798

    
4799
# real radios
4800
@directory.register
4801
class KT8R(QYTColorHT):
4802
    """QYT KT8R"""
4803
    VENDOR = "QYT"
4804
    MODEL = "KT-8R"
4805
    BANDS = 4
4806
    LIST_TMR = LIST_TMR16
4807
    _vhf_range = (136000000, 175000000)
4808
    _220_range = (200000000, 261000000)
4809
    _uhf_range = (400000000, 481000000)
4810
    _350_range = (350000000, 391000000)
4811
    _magic = MSTRING_KT8R
4812
    _fileid = [KT8R_fp2, KT8R_fp1, KT8R_fp, ]
4813
    _power_levels = [chirp_common.PowerLevel("High", watts=5),
4814
                     chirp_common.PowerLevel("Low", watts=1)]
(2-2/2)