Project

General

Profile

Bug #5999 ยป btech_support_radios_with _missing_tmr_choice.py

Jim Unroe, 04/26/2019 11:48 AM

 
1
# Copyright 2016-2017:
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 = ["Black", "White", "Red", "Blue", "Green", "Yellow", "Indego",
63
               "Purple", "Gray"]
64
LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
65
LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
66
LIST_EMCTPX = ["Off"] + LIST_EMCTP
67
LIST_LANGUA = ["English", "Chinese"]
68
LIST_MDF = ["Frequency", "Channel", "Name"]
69
LIST_OFF1TO9 = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
70
LIST_OFF1TO10 = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
71
LIST_OFF1TO50 = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
72
LIST_PONMSG = ["Full", "Message", "Battery voltage"]
73
LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
74
LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
75
LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
76
LIST_SCMODE = ["Off", "PTT-SC", "MEM-SC", "PON-SC"]
77
LIST_SHIFT = ["Off", "+", "-"]
78
LIST_SKIPTX = ["Off", "Skip 1", "Skip 2"]
79
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
80
LIST_STEP = [str(x) for x in STEPS]
81
LIST_SYNC = ["Off", "AB", "CD", "AB+CD"]
82
LIST_TMR = ["OFF", "M+A", "M+B", "M+C", "M+D", "M+A+B", "M+A+C", "M+A+D",
83
            "M+B+C", "M+B+D", "M+C+D", "M+A+B+C", "M+A+B+D", "M+A+C+D",
84
            "M+B+C+D", "A+B+C+D"]
85
LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
86
LIST_TXDISP = ["Power", "Mic Volume"]
87
LIST_TXP = ["High", "Low"]
88
LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
89
LIST_VFOMR = ["Frequency", "Channel"]
90
LIST_WIDE = ["Wide", "Narrow"]
91

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

    
123
# This is a general serial timeout for all serial read functions.
124
# Practice has show that about 0.7 sec will be enough to cover all radios.
125
STIMEOUT = 0.7
126

    
127
# this var controls the verbosity in the debug and by default it's low (False)
128
# make it True and you will to get a very verbose debug.log
129
debug = False
130

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

    
135

    
136
# #### ID strings #####################################################
137

    
138
# BTECH UV2501 pre-production units
139
UV2501pp_fp = "M2C294"
140
# BTECH UV2501 pre-production units 2 + and 1st Gen radios
141
UV2501pp2_fp = "M29204"
142
# B-TECH UV-2501 second generation (2G) radios
143
UV2501G2_fp = "BTG214"
144
# B-TECH UV-2501 third generation (3G) radios
145
UV2501G3_fp = "BTG324"
146

    
147
# B-TECH UV-2501+220 pre-production units
148
UV2501_220pp_fp = "M3C281"
149
# extra block read for the 2501+220 pre-production units
150
# the same for all of this radios so far
151
UV2501_220pp_id = "      280528"
152
# B-TECH UV-2501+220
153
UV2501_220_fp = "M3G201"
154
# new variant, let's call it Generation 2
155
UV2501_220G2_fp = "BTG211"
156
# B-TECH UV-2501+220 third generation (3G)
157
UV2501_220G3_fp = "BTG311"
158

    
159
# B-TECH UV-5001 pre-production units + 1st Gen radios
160
UV5001pp_fp = "V19204"
161
# B-TECH UV-5001 alpha units
162
UV5001alpha_fp = "V28204"
163
# B-TECH UV-5001 second generation (2G) radios
164
UV5001G2_fp = "BTG214"
165
# B-TECH UV-5001 second generation (2G2)
166
UV5001G22_fp = "V2G204"
167
# B-TECH UV-5001 third generation (3G)
168
UV5001G3_fp = "BTG304"
169

    
170
# B-TECH UV-25X2
171
UV25X2_fp = "UC2012"
172

    
173
# B-TECH UV-25X4
174
UV25X4_fp = "UC4014"
175

    
176
# B-TECH UV-50X2
177
UV50X2_fp = "UC2M12"
178

    
179
# special var to know when we found a BTECH Gen 3
180
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
181

    
182

    
183
# WACCOM Mini-8900
184
MINI8900_fp = "M28854"
185

    
186

    
187
# QYT KT-UV980
188
KTUV980_fp = "H28854"
189

    
190
# QYT KT8900
191
KT8900_fp = "M29154"
192
# New generations KT8900
193
KT8900_fp1 = "M2C234"
194
KT8900_fp2 = "M2G1F4"
195
KT8900_fp3 = "M2G2F4"
196
KT8900_fp4 = "M2G304"
197
KT8900_fp5 = "M2G314"
198
# this radio has an extra ID
199
KT8900_id = "303688"
200

    
201
# KT8900R
202
KT8900R_fp = "M3G1F4"
203
# Second Generation
204
KT8900R_fp1 = "M3G214"
205
# another model
206
KT8900R_fp2 = "M3C234"
207
# another model G4?
208
KT8900R_fp3 = "M39164"
209
# another model
210
KT8900R_fp4 = "M3G314"
211
# this radio has an extra ID
212
KT8900R_id = "280528"
213
# another extra ID in dec/2018
214
KT8900R_id2 = "\x05\x58\x3d\xf0\x10"
215

    
216
# KT7900D (quad band)
217
KT7900D_fp = "VC4004"
218
KT7900D_fp1 = "VC4284"
219

    
220
# QB25 (quad band) - a clone of KT7900D
221
QB25_fp = "QB-25"
222

    
223
# KT8900D (dual band)
224
KT8900D_fp = "VC2002"
225
KT8900D_fp1 = "VC8632"
226

    
227
# LUITON LT-588UV
228
LT588UV_fp = "V2G1F4"
229
# Added by rstrickoff gen 2 id
230
LT588UV_fp1 = "V2G214"
231

    
232

    
233
# ### MAGICS
234
# for the Waccom Mini-8900
235
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
236
# for the B-TECH UV-2501+220 (including pre production ones)
237
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
238
# for the QYT KT8900 & R
239
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
240
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
241
# magic string for all other models
242
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
243
# for the QYT KT7900D & KT8900D
244
MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
245
# for the BTECH UV-25X2 and UV-50X2
246
MSTRING_UV25X2 = "\x55\x20\x16\x12\x28\xFF\xDC\x02"
247
# for the BTECH UV-25X4
248
MSTRING_UV25X4 = "\x55\x20\x16\x11\x18\xFF\xDC\x02"
249

    
250

    
251
def _clean_buffer(radio):
252
    """Cleaning the read serial buffer, hard timeout to survive an infinite
253
    data stream"""
254

    
255
    # touching the serial timeout to optimize the flushing
256
    # restored at the end to the default value
257
    radio.pipe.timeout = 0.1
258
    dump = "1"
259
    datacount = 0
260

    
261
    try:
262
        while len(dump) > 0:
263
            dump = radio.pipe.read(100)
264
            datacount += len(dump)
265
            # hard limit to survive a infinite serial data stream
266
            # 5 times bigger than a normal rx block (69 bytes)
267
            if datacount > 345:
268
                seriale = "Please check your serial port selection."
269
                raise errors.RadioError(seriale)
270

    
271
        # restore the default serial timeout
272
        radio.pipe.timeout = STIMEOUT
273

    
274
    except Exception:
275
        raise errors.RadioError("Unknown error cleaning the serial buffer")
276

    
277

    
278
def _rawrecv(radio, amount):
279
    """Raw read from the radio device, less intensive way"""
280

    
281
    data = ""
282

    
283
    try:
284
        data = radio.pipe.read(amount)
285

    
286
        # DEBUG
287
        if debug is True:
288
            LOG.debug("<== (%d) bytes:\n\n%s" %
289
                      (len(data), util.hexprint(data)))
290

    
291
        # fail if no data is received
292
        if len(data) == 0:
293
            raise errors.RadioError("No data received from radio")
294

    
295
        # notice on the logs if short
296
        if len(data) < amount:
297
            LOG.warn("Short reading %d bytes from the %d requested." %
298
                     (len(data), amount))
299

    
300
    except:
301
        raise errors.RadioError("Error reading data from radio")
302

    
303
    return data
304

    
305

    
306
def _send(radio, data):
307
    """Send data to the radio device"""
308

    
309
    try:
310
        for byte in data:
311
            radio.pipe.write(byte)
312
            # Some OS (mainly Linux ones) are too fast on the serial and
313
            # get the MCU inside the radio stuck in the early stages, this
314
            # hits some models more than others.
315
            #
316
            # To cope with that we introduce a delay on the writes.
317
            # Many option have been tested (delaying only after error occures,
318
            # after short reads, only for linux, ...)
319
            # Finally, a static delay was chosen as simplest of all solutions
320
            # (Michael Wagner, OE4AMW)
321
            # (for details, see issue 3993)
322
            sleep(0.002)
323

    
324
        # DEBUG
325
        if debug is True:
326
            LOG.debug("==> (%d) bytes:\n\n%s" %
327
                      (len(data), util.hexprint(data)))
328
    except:
329
        raise errors.RadioError("Error sending data to radio")
330

    
331

    
332
def _make_frame(cmd, addr, length, data=""):
333
    """Pack the info in the headder format"""
334
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
335
    # add the data if set
336
    if len(data) != 0:
337
        frame += data
338

    
339
    return frame
340

    
341

    
342
def _recv(radio, addr):
343
    """Get data from the radio all at once to lower syscalls load"""
344

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

    
348
    # get the whole block
349
    block = _rawrecv(radio, BLOCK_SIZE + 5)
350

    
351
    # basic check
352
    if len(block) < (BLOCK_SIZE + 5):
353
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
354

    
355
    # checking for the ack
356
    if block[0] != ACK_CMD:
357
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
358

    
359
    # header validation
360
    c, a, l = struct.unpack(">BHB", block[1:5])
361
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
362
        LOG.debug("Invalid header for block 0x%04x" % addr)
363
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
364
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
365

    
366
    # return the data
367
    return block[5:]
368

    
369

    
370
def _start_clone_mode(radio, status):
371
    """Put the radio in clone mode and get the ident string, 3 tries"""
372

    
373
    # cleaning the serial buffer
374
    _clean_buffer(radio)
375

    
376
    # prep the data to show in the UI
377
    status.cur = 0
378
    status.msg = "Identifying the radio..."
379
    status.max = 3
380
    radio.status_fn(status)
381

    
382
    try:
383
        for a in range(0, status.max):
384
            # Update the UI
385
            status.cur = a + 1
386
            radio.status_fn(status)
387

    
388
            # send the magic word
389
            _send(radio, radio._magic)
390

    
391
            # Now you get a x06 of ACK if all goes well
392
            ack = radio.pipe.read(1)
393

    
394
            if ack == "\x06":
395
                # DEBUG
396
                LOG.info("Magic ACK received")
397
                status.cur = status.max
398
                radio.status_fn(status)
399

    
400
                return True
401

    
402
        return False
403

    
404
    except errors.RadioError:
405
        raise
406
    except Exception, e:
407
        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
408

    
409

    
410
def _do_ident(radio, status, upload=False):
411
    """Put the radio in PROGRAM mode & identify it"""
412
    #  set the serial discipline
413
    radio.pipe.baudrate = 9600
414
    radio.pipe.parity = "N"
415

    
416
    # open the radio into program mode
417
    if _start_clone_mode(radio, status) is False:
418
        msg = "Radio did not enter clone mode"
419
        # warning about old versions of QYT KT8900
420
        if radio.MODEL == "KT8900":
421
            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
422
            msg += " known variant of this radios that is a clone of it."
423
        raise errors.RadioError(msg)
424

    
425
    # Ok, get the ident string
426
    ident = _rawrecv(radio, 49)
427

    
428
    # basic check for the ident
429
    if len(ident) != 49:
430
        raise errors.RadioError("Radio send a short ident block.")
431

    
432
    # check if ident is OK
433
    itis = False
434
    for fp in radio._fileid:
435
        if fp in ident:
436
            # got it!
437
            itis = True
438
            # checking if we are dealing with a Gen 3 BTECH
439
            if radio.VENDOR == "BTECH" and fp in BTECH3:
440
                radio.btech3 = True
441

    
442
            break
443

    
444
    if itis is False:
445
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
446
        raise errors.RadioError("Radio identification failed.")
447

    
448
    # some radios needs a extra read and check for a code on it, this ones
449
    # has the check value in the _id2 var, others simply False
450
    if radio._id2 is not False:
451
        # lower the timeout here as this radios are reseting due to timeout
452
        radio.pipe.timeout = 0.05
453

    
454
        # query & receive the extra ID
455
        _send(radio, _make_frame("S", 0x3DF0, 16))
456
        id2 = _rawrecv(radio, 21)
457

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

    
465
        # ok, the correct string must be in the received data
466
        # the radio._id2 var will be always a list
467
        flag2 = False
468
        for _id2 in radio._id2:
469
            if _id2 in id2:
470
                flag2 = True
471

    
472
        if not flag2:
473
            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
474
                      (radio.MODEL, util.hexprint(id2)))
475
            raise errors.RadioError("The extra ID is wrong, aborting.")
476

    
477
        # this radios need a extra request/answer here on the upload
478
        # the amount of data received depends of the radio type
479
        #
480
        # also the first block of TX must no have the ACK at the beginning
481
        # see _upload for this.
482
        if upload is True:
483
            # send an ACK
484
            _send(radio, ACK_CMD)
485

    
486
            # the amount of data depend on the radio, so far we have two radios
487
            # reading two bytes with an ACK at the end and just ONE with just
488
            # one byte (QYT KT8900)
489
            # the JT-6188 appears a clone of the last, but reads TWO bytes.
490
            #
491
            # we will read two bytes with a custom timeout to not penalize the
492
            # users for this.
493
            #
494
            # we just check for a response and last byte being a ACK, that is
495
            # the common stone for all radios (3 so far)
496
            ack = _rawrecv(radio, 2)
497

    
498
            # checking
499
            if len(ack) == 0 or ack[-1:] != ACK_CMD:
500
                raise errors.RadioError("Radio didn't ACK the upload")
501

    
502
            # restore the default serial timeout
503
            radio.pipe.timeout = STIMEOUT
504

    
505
    # DEBUG
506
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
507

    
508
    return True
509

    
510

    
511
def _download(radio):
512
    """Get the memory map"""
513

    
514
    # UI progress
515
    status = chirp_common.Status()
516

    
517
    # put radio in program mode and identify it
518
    _do_ident(radio, status)
519

    
520
    # the models that doesn't have the extra ID have to make a dummy read here
521
    if radio._id2 is False:
522
        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
523
        discard = _rawrecv(radio, BLOCK_SIZE + 5)
524

    
525
        if debug is True:
526
            LOG.info("Dummy first block read done, got this:\n\n %s",
527
                     util.hexprint(discard))
528

    
529
    # reset the progress bar in the UI
530
    status.max = MEM_SIZE / BLOCK_SIZE
531
    status.msg = "Cloning from radio..."
532
    status.cur = 0
533
    radio.status_fn(status)
534

    
535
    # cleaning the serial buffer
536
    _clean_buffer(radio)
537

    
538
    data = ""
539
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
540
        # sending the read request
541
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
542

    
543
        # read
544
        d = _recv(radio, addr)
545

    
546
        # aggregate the data
547
        data += d
548

    
549
        # UI Update
550
        status.cur = addr / BLOCK_SIZE
551
        status.msg = "Cloning from radio..."
552
        radio.status_fn(status)
553

    
554
    return data
555

    
556

    
557
def _upload(radio):
558
    """Upload procedure"""
559

    
560
    # The UPLOAD mem is restricted to lower than 0x3100,
561
    # so we will overide that here localy
562
    MEM_SIZE = 0x3100
563

    
564
    # UI progress
565
    status = chirp_common.Status()
566

    
567
    # put radio in program mode and identify it
568
    _do_ident(radio, status, True)
569

    
570
    # get the data to upload to radio
571
    data = radio.get_mmap()
572

    
573
    # Reset the UI progress
574
    status.max = MEM_SIZE / TX_BLOCK_SIZE
575
    status.cur = 0
576
    status.msg = "Cloning to radio..."
577
    radio.status_fn(status)
578

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

    
583
    # cleaning the serial buffer
584
    _clean_buffer(radio)
585

    
586
    # the fun start here
587
    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
588
        # getting the block of data to send
589
        d = data[addr:addr + TX_BLOCK_SIZE]
590

    
591
        # build the frame to send
592
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
593

    
594
        # first block must not send the ACK at the beginning for the
595
        # ones that has the extra id, since this have to do a extra step
596
        if addr == 0 and radio._id2 is not False:
597
            frame = frame[1:]
598

    
599
        # send the frame
600
        _send(radio, frame)
601

    
602
        # receiving the response
603
        ack = _rawrecv(radio, 1)
604

    
605
        # basic check
606
        if len(ack) != 1:
607
            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
608

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

    
612
        # UI Update
613
        status.cur = addr / TX_BLOCK_SIZE
614
        status.msg = "Cloning to radio..."
615
        radio.status_fn(status)
616

    
617

    
618
def model_match(cls, data):
619
    """Match the opened/downloaded image to the correct version"""
620
    rid = data[0x3f70:0x3f76]
621

    
622
    if rid in cls._fileid:
623
        return True
624

    
625
    return False
626

    
627

    
628
def _decode_ranges(low, high):
629
    """Unpack the data in the ranges zones in the memmap and return
630
    a tuple with the integer corresponding to the Mhz it means"""
631
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
632
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
633
    ilow *= 1000000
634
    ihigh *= 1000000
635

    
636
    return (ilow, ihigh)
637

    
638

    
639
def _split(rf, f1, f2):
640
    """Returns False if the two freqs are in the same band (no split)
641
    or True otherwise"""
642

    
643
    # determine if the two freqs are in the same band
644
    for low, high in rf.valid_bands:
645
        if f1 >= low and f1 <= high and \
646
                f2 >= low and f2 <= high:
647
            # if the two freqs are on the same Band this is not a split
648
            return False
649

    
650
    # if you get here is because the freq pairs are split
651
    return True
652

    
653

    
654
class BTechMobileCommon(chirp_common.CloneModeRadio,
655
                        chirp_common.ExperimentalRadio):
656
    """BTECH's UV-5001 and alike radios"""
657
    VENDOR = "BTECH"
658
    MODEL = ""
659
    IDENT = ""
660
    BANDS = 2
661
    COLOR_LCD = False
662
    NAME_LENGTH = 6
663
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
664
                     chirp_common.PowerLevel("Low", watts=10)]
665
    _vhf_range = (130000000, 180000000)
666
    _220_range = (200000000, 271000000)
667
    _uhf_range = (400000000, 521000000)
668
    _350_range = (350000000, 391000000)
669
    _upper = 199
670
    _magic = MSTRING
671
    _fileid = None
672
    _id2 = False
673
    btech3 = False
674

    
675
    @classmethod
676
    def get_prompts(cls):
677
        rp = chirp_common.RadioPrompts()
678
        rp.experimental = \
679
            ('This driver is experimental.\n'
680
             '\n'
681
             'Please keep a copy of your memories with the original software '
682
             'if you treasure them, this driver is new and may contain'
683
             ' bugs.\n'
684
             '\n'
685
             )
686
        rp.pre_download = _(dedent("""\
687
            Follow these instructions to download your info:
688

    
689
            1 - Turn off your radio
690
            2 - Connect your interface cable
691
            3 - Turn on your radio
692
            4 - Do the download of your radio data
693

    
694
            """))
695
        rp.pre_upload = _(dedent("""\
696
            Follow these instructions to upload your info:
697

    
698
            1 - Turn off your radio
699
            2 - Connect your interface cable
700
            3 - Turn on your radio
701
            4 - Do the upload of your radio data
702

    
703
            """))
704
        return rp
705

    
706
    def get_features(self):
707
        """Get the radio's features"""
708

    
709
        # we will use the following var as global
710
        global POWER_LEVELS
711

    
712
        rf = chirp_common.RadioFeatures()
713
        rf.has_settings = True
714
        rf.has_bank = False
715
        rf.has_tuning_step = False
716
        rf.can_odd_split = True
717
        rf.has_name = True
718
        rf.has_offset = True
719
        rf.has_mode = True
720
        rf.has_dtcs = True
721
        rf.has_rx_dtcs = True
722
        rf.has_dtcs_polarity = True
723
        rf.has_ctone = True
724
        rf.has_cross = True
725
        rf.valid_modes = MODES
726
        rf.valid_characters = VALID_CHARS
727
        rf.valid_name_length = self.NAME_LENGTH
728
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
729
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
730
        rf.valid_cross_modes = [
731
            "Tone->Tone",
732
            "DTCS->",
733
            "->DTCS",
734
            "Tone->DTCS",
735
            "DTCS->Tone",
736
            "->Tone",
737
            "DTCS->DTCS"]
738
        rf.valid_skips = SKIP_VALUES
739
        rf.valid_dtcs_codes = DTCS
740
        rf.valid_tuning_steps = STEPS
741
        rf.memory_bounds = (0, self._upper)
742

    
743
        # power levels
744
        POWER_LEVELS = self._power_levels
745
        rf.valid_power_levels = POWER_LEVELS
746

    
747
        # normal dual bands
748
        rf.valid_bands = [self._vhf_range, self._uhf_range]
749

    
750
        # 220 band
751
        if self.BANDS == 3 or self.BANDS == 4:
752
            rf.valid_bands.append(self._220_range)
753

    
754
        # 350 band
755
        if self.BANDS == 4:
756
            rf.valid_bands.append(self._350_range)
757

    
758
        return rf
759

    
760
    def sync_in(self):
761
        """Download from radio"""
762
        data = _download(self)
763
        self._mmap = memmap.MemoryMap(data)
764
        self.process_mmap()
765

    
766
    def sync_out(self):
767
        """Upload to radio"""
768
        try:
769
            _upload(self)
770
        except errors.RadioError:
771
            raise
772
        except Exception, e:
773
            raise errors.RadioError("Error: %s" % e)
774

    
775
    def get_raw_memory(self, number):
776
        return repr(self._memobj.memory[number])
777

    
778
    def _decode_tone(self, val):
779
        """Parse the tone data to decode from mem, it returns:
780
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
781
        pol = None
782

    
783
        if val in [0, 65535]:
784
            return '', None, None
785
        elif val > 0x0258:
786
            a = val / 10.0
787
            return 'Tone', a, pol
788
        else:
789
            if val > 0x69:
790
                index = val - 0x6A
791
                pol = "R"
792
            else:
793
                index = val - 1
794
                pol = "N"
795

    
796
            tone = DTCS[index]
797
            return 'DTCS', tone, pol
798

    
799
    def _encode_tone(self, memval, mode, val, pol):
800
        """Parse the tone data to encode from UI to mem"""
801
        if mode == '' or mode is None:
802
            memval.set_raw("\x00\x00")
803
        elif mode == 'Tone':
804
            memval.set_value(val * 10)
805
        elif mode == 'DTCS':
806
            # detect the index in the DTCS list
807
            try:
808
                index = DTCS.index(val)
809
                if pol == "N":
810
                    index += 1
811
                else:
812
                    index += 0x6A
813
                memval.set_value(index)
814
            except:
815
                msg = "Digital Tone '%d' is not supported" % value
816
                LOG.error(msg)
817
                raise errors.RadioError(msg)
818
        else:
819
            msg = "Internal error: invalid mode '%s'" % mode
820
            LOG.error(msg)
821
            raise errors.InvalidDataError(msg)
822

    
823
    def get_memory(self, number):
824
        """Get the mem representation from the radio image"""
825
        _mem = self._memobj.memory[number]
826
        _names = self._memobj.names[number]
827

    
828
        # Create a high-level memory object to return to the UI
829
        mem = chirp_common.Memory()
830

    
831
        # Memory number
832
        mem.number = number
833

    
834
        if _mem.get_raw()[0] == "\xFF":
835
            mem.empty = True
836
            return mem
837

    
838
        # Freq and offset
839
        mem.freq = int(_mem.rxfreq) * 10
840
        # tx freq can be blank
841
        if _mem.get_raw()[4] == "\xFF":
842
            # TX freq not set
843
            mem.offset = 0
844
            mem.duplex = "off"
845
        else:
846
            # TX freq set
847
            offset = (int(_mem.txfreq) * 10) - mem.freq
848
            if offset != 0:
849
                if _split(self.get_features(), mem.freq, int(
850
                          _mem.txfreq) * 10):
851
                    mem.duplex = "split"
852
                    mem.offset = int(_mem.txfreq) * 10
853
                elif offset < 0:
854
                    mem.offset = abs(offset)
855
                    mem.duplex = "-"
856
                elif offset > 0:
857
                    mem.offset = offset
858
                    mem.duplex = "+"
859
            else:
860
                mem.offset = 0
861

    
862
        # name TAG of the channel
863
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
864

    
865
        # power
866
        mem.power = POWER_LEVELS[int(_mem.power)]
867

    
868
        # wide/narrow
869
        mem.mode = MODES[int(_mem.wide)]
870

    
871
        # skip
872
        mem.skip = SKIP_VALUES[_mem.add]
873

    
874
        # tone data
875
        rxtone = txtone = None
876
        txtone = self._decode_tone(_mem.txtone)
877
        rxtone = self._decode_tone(_mem.rxtone)
878
        chirp_common.split_tone_decode(mem, txtone, rxtone)
879

    
880
        # Extra
881
        mem.extra = RadioSettingGroup("extra", "Extra")
882

    
883
        if not self.COLOR_LCD or \
884
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
885
            scramble = RadioSetting("scramble", "Scramble",
886
                                    RadioSettingValueBoolean(bool(
887
                                        _mem.scramble)))
888
            mem.extra.append(scramble)
889

    
890
        bcl = RadioSetting("bcl", "Busy channel lockout",
891
                           RadioSettingValueBoolean(bool(_mem.bcl)))
892
        mem.extra.append(bcl)
893

    
894
        pttid = RadioSetting("pttid", "PTT ID",
895
                             RadioSettingValueList(PTTID_LIST,
896
                                                   PTTID_LIST[_mem.pttid]))
897
        mem.extra.append(pttid)
898

    
899
        # validating scode
900
        scode = _mem.scode if _mem.scode != 15 else 0
901
        pttidcode = RadioSetting("scode", "PTT ID signal code",
902
                                 RadioSettingValueList(
903
                                     PTTIDCODE_LIST,
904
                                     PTTIDCODE_LIST[scode]))
905
        mem.extra.append(pttidcode)
906

    
907
        optsig = RadioSetting("optsig", "Optional signaling",
908
                              RadioSettingValueList(
909
                                  OPTSIG_LIST,
910
                                  OPTSIG_LIST[_mem.optsig]))
911
        mem.extra.append(optsig)
912

    
913
        spmute = RadioSetting("spmute", "Speaker mute",
914
                              RadioSettingValueList(
915
                                  SPMUTE_LIST,
916
                                  SPMUTE_LIST[_mem.spmute]))
917
        mem.extra.append(spmute)
918

    
919
        return mem
920

    
921
    def set_memory(self, mem):
922
        """Set the memory data in the eeprom img from the UI"""
923
        # get the eprom representation of this channel
924
        _mem = self._memobj.memory[mem.number]
925
        _names = self._memobj.names[mem.number]
926

    
927
        mem_was_empty = False
928
        # same method as used in get_memory for determining if mem is empty
929
        # doing this BEFORE overwriting it with new values ...
930
        if _mem.get_raw()[0] == "\xFF":
931
            LOG.debug("This mem was empty before")
932
            mem_was_empty = True
933

    
934
        # if empty memmory
935
        if mem.empty:
936
            # the channel itself
937
            _mem.set_raw("\xFF" * 16)
938
            # the name tag
939
            _names.set_raw("\xFF" * 16)
940
            return
941

    
942
        if mem_was_empty:
943
            # Zero the whole memory if we're making it unempty for
944
            # the first time
945
            LOG.debug('Zeroing new memory')
946
            _mem.set_raw('\x00' * 16)
947

    
948
        # frequency
949
        _mem.rxfreq = mem.freq / 10
950

    
951
        # duplex
952
        if mem.duplex == "+":
953
            _mem.txfreq = (mem.freq + mem.offset) / 10
954
        elif mem.duplex == "-":
955
            _mem.txfreq = (mem.freq - mem.offset) / 10
956
        elif mem.duplex == "off":
957
            for i in _mem.txfreq:
958
                i.set_raw("\xFF")
959
        elif mem.duplex == "split":
960
            _mem.txfreq = mem.offset / 10
961
        else:
962
            _mem.txfreq = mem.freq / 10
963

    
964
        # tone data
965
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
966
            chirp_common.split_tone_encode(mem)
967
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
968
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
969

    
970
        # name TAG of the channel
971
        if len(mem.name) < self.NAME_LENGTH:
972
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
973
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
974
        _names.name = str(mem.name).replace(" ", "\xFF")
975

    
976
        # power, # default power level is high
977
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
978

    
979
        # wide/narrow
980
        _mem.wide = MODES.index(mem.mode)
981

    
982
        # scan add property
983
        _mem.add = SKIP_VALUES.index(mem.skip)
984

    
985
        # reseting unknowns, this have to be set by hand
986
        _mem.unknown0 = 0
987
        _mem.unknown1 = 0
988
        _mem.unknown2 = 0
989
        _mem.unknown3 = 0
990
        _mem.unknown4 = 0
991
        _mem.unknown5 = 0
992
        _mem.unknown6 = 0
993

    
994
        def _zero_settings():
995
            _mem.spmute = 0
996
            _mem.optsig = 0
997
            _mem.scramble = 0
998
            _mem.bcl = 0
999
            _mem.pttid = 0
1000
            _mem.scode = 0
1001

    
1002
        if self.COLOR_LCD and _mem.scramble:
1003
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1004
            _mem.scramble = 0
1005

    
1006
        # extra settings
1007
        if len(mem.extra) > 0:
1008
            # there are setting, parse
1009
            LOG.debug("Extra-Setting supplied. Setting them.")
1010
            # Zero them all first so any not provided by model don't
1011
            # stay set
1012
            _zero_settings()
1013
            for setting in mem.extra:
1014
                setattr(_mem, setting.get_name(), setting.value)
1015
        else:
1016
            if mem.empty:
1017
                LOG.debug("New mem is empty.")
1018
            else:
1019
                LOG.debug("New mem is NOT empty")
1020
                # set extra-settings to default ONLY when apreviously empty or
1021
                # deleted memory was edited to prevent errors such as #4121
1022
                if mem_was_empty:
1023
                    LOG.debug("old mem was empty. Setting default for extras.")
1024
                    _zero_settings()
1025

    
1026
        return mem
1027

    
1028
    def get_settings(self):
1029
        """Translate the bit in the mem_struct into settings in the UI"""
1030
        _mem = self._memobj
1031
        basic = RadioSettingGroup("basic", "Basic Settings")
1032
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1033
        other = RadioSettingGroup("other", "Other Settings")
1034
        work = RadioSettingGroup("work", "Work Mode Settings")
1035
        top = RadioSettings(basic, advanced, other, work)
1036

    
1037
        # Basic
1038
        if self.COLOR_LCD:
1039
            if not self.VENDOR == "BTECH":
1040
                # remove missing choice in non-BTECH radios
1041
                del LIST_TMR[12]
1042
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1043
                               RadioSettingValueList(
1044
                                   LIST_TMR,
1045
                                   LIST_TMR[_mem.settings.tmr]))
1046
            basic.append(tmr)
1047
        else:
1048
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1049
                               RadioSettingValueBoolean(_mem.settings.tdr))
1050
            basic.append(tdr)
1051

    
1052
        sql = RadioSetting("settings.sql", "Squelch level",
1053
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1054
        basic.append(sql)
1055

    
1056
        tot = RadioSetting("settings.tot", "Time out timer",
1057
                           RadioSettingValueList(
1058
                               LIST_TOT,
1059
                               LIST_TOT[_mem.settings.tot]))
1060
        basic.append(tot)
1061

    
1062
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1063
            apo = RadioSetting("settings.apo", "Auto power off timer",
1064
                               RadioSettingValueList(
1065
                                   LIST_APO,
1066
                                   LIST_APO[_mem.settings.apo]))
1067
            basic.append(apo)
1068
        else:
1069
            toa = RadioSetting("settings.apo", "Time out alert timer",
1070
                               RadioSettingValueList(
1071
                                   LIST_OFF1TO10,
1072
                                   LIST_OFF1TO10[_mem.settings.apo]))
1073
            basic.append(toa)
1074

    
1075
        abr = RadioSetting("settings.abr", "Backlight timer",
1076
                           RadioSettingValueList(
1077
                               LIST_OFF1TO50,
1078
                               LIST_OFF1TO50[_mem.settings.abr]))
1079
        basic.append(abr)
1080

    
1081
        beep = RadioSetting("settings.beep", "Key beep",
1082
                            RadioSettingValueBoolean(_mem.settings.beep))
1083
        basic.append(beep)
1084

    
1085
        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1086
                              RadioSettingValueList(
1087
                                  LIST_DTMFST,
1088
                                  LIST_DTMFST[_mem.settings.dtmfst]))
1089
        basic.append(dtmfst)
1090

    
1091
        if not self.COLOR_LCD:
1092
            prisc = RadioSetting("settings.prisc", "Priority scan",
1093
                                 RadioSettingValueBoolean(
1094
                                     _mem.settings.prisc))
1095
            basic.append(prisc)
1096

    
1097
            prich = RadioSetting("settings.prich", "Priority channel",
1098
                                 RadioSettingValueInteger(0, 199,
1099
                                                          _mem.settings.prich))
1100
            basic.append(prich)
1101

    
1102
        screv = RadioSetting("settings.screv", "Scan resume method",
1103
                             RadioSettingValueList(
1104
                                 LIST_SCREV,
1105
                                 LIST_SCREV[_mem.settings.screv]))
1106
        basic.append(screv)
1107

    
1108
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1109
                             RadioSettingValueInteger(0, 30,
1110
                                                      _mem.settings.pttlt))
1111
        basic.append(pttlt)
1112

    
1113
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1114
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1115
                                 RadioSettingValueList(
1116
                                     LIST_EMCTPX,
1117
                                     LIST_EMCTPX[_mem.settings.emctp]))
1118
            basic.append(emctp)
1119
        else:
1120
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1121
                                 RadioSettingValueList(
1122
                                     LIST_EMCTP,
1123
                                     LIST_EMCTP[_mem.settings.emctp]))
1124
            basic.append(emctp)
1125

    
1126
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1127
                             RadioSettingValueInteger(0, 199,
1128
                                                      _mem.settings.emcch))
1129
        basic.append(emcch)
1130

    
1131
        if self.COLOR_LCD:
1132
            if _mem.settings.sigbp > 0x01:
1133
                val = 0x00
1134
            else:
1135
                val = _mem.settings.sigbp
1136
            sigbp = RadioSetting("settings.sigbp", "Roger beep",
1137
                                 RadioSettingValueBoolean(val))
1138
            basic.append(sigbp)
1139
        else:
1140
            ringt = RadioSetting("settings.ringt", "Ring time",
1141
                                 RadioSettingValueList(
1142
                                     LIST_OFF1TO9,
1143
                                     LIST_OFF1TO9[_mem.settings.ringt]))
1144
            basic.append(ringt)
1145

    
1146
        camdf = RadioSetting("settings.camdf", "Display mode A",
1147
                             RadioSettingValueList(
1148
                                 LIST_MDF,
1149
                                 LIST_MDF[_mem.settings.camdf]))
1150
        basic.append(camdf)
1151

    
1152
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1153
                             RadioSettingValueList(
1154
                                 LIST_MDF,
1155
                                 LIST_MDF[_mem.settings.cbmdf]))
1156
        basic.append(cbmdf)
1157

    
1158
        if self.COLOR_LCD:
1159
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1160
                                 RadioSettingValueList(
1161
                                     LIST_MDF,
1162
                                     LIST_MDF[_mem.settings.ccmdf]))
1163
            basic.append(ccmdf)
1164

    
1165
            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1166
                                 RadioSettingValueList(
1167
                                     LIST_MDF,
1168
                                     LIST_MDF[_mem.settings.cdmdf]))
1169
            basic.append(cdmdf)
1170

    
1171
            langua = RadioSetting("settings.langua", "Language",
1172
                                  RadioSettingValueList(
1173
                                      LIST_LANGUA,
1174
                                      LIST_LANGUA[_mem.settings.langua]))
1175
            basic.append(langua)
1176

    
1177
        if self.VENDOR == "BTECH":
1178
            if self.COLOR_LCD:
1179
                sync = RadioSetting("settings.sync", "Channel display sync",
1180
                                    RadioSettingValueList(
1181
                                        LIST_SYNC,
1182
                                        LIST_SYNC[_mem.settings.sync]))
1183
                basic.append(sync)
1184
            else:
1185
                sync = RadioSetting("settings.sync", "A/B channel sync",
1186
                                    RadioSettingValueBoolean(
1187
                                        _mem.settings.sync))
1188
                basic.append(sync)
1189
        else:
1190
            autolk = RadioSetting("settings.sync", "Auto keylock",
1191
                                  RadioSettingValueBoolean(
1192
                                      _mem.settings.sync))
1193
            basic.append(autolk)
1194

    
1195
        if not self.COLOR_LCD:
1196
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1197
                                  RadioSettingValueList(
1198
                                      LIST_PONMSG,
1199
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1200
            basic.append(ponmsg)
1201

    
1202
        if self.COLOR_LCD:
1203
            mainfc = RadioSetting("settings.mainfc",
1204
                                  "Main LCD foreground color",
1205
                                  RadioSettingValueList(
1206
                                      LIST_COLOR8,
1207
                                      LIST_COLOR8[_mem.settings.mainfc]))
1208
            basic.append(mainfc)
1209

    
1210
            mainbc = RadioSetting("settings.mainbc",
1211
                                  "Main LCD background color",
1212
                                  RadioSettingValueList(
1213
                                      LIST_COLOR8,
1214
                                      LIST_COLOR8[_mem.settings.mainbc]))
1215
            basic.append(mainbc)
1216

    
1217
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1218
                                  RadioSettingValueList(
1219
                                      LIST_COLOR8,
1220
                                      LIST_COLOR8[_mem.settings.menufc]))
1221
            basic.append(menufc)
1222

    
1223
            menubc = RadioSetting("settings.menubc", "Menu background color",
1224
                                  RadioSettingValueList(
1225
                                      LIST_COLOR8,
1226
                                      LIST_COLOR8[_mem.settings.menubc]))
1227
            basic.append(menubc)
1228

    
1229
            stafc = RadioSetting("settings.stafc",
1230
                                 "Top status foreground color",
1231
                                 RadioSettingValueList(
1232
                                     LIST_COLOR8,
1233
                                     LIST_COLOR8[_mem.settings.stafc]))
1234
            basic.append(stafc)
1235

    
1236
            stabc = RadioSetting("settings.stabc",
1237
                                 "Top status background color",
1238
                                 RadioSettingValueList(
1239
                                     LIST_COLOR8,
1240
                                     LIST_COLOR8[_mem.settings.stabc]))
1241
            basic.append(stabc)
1242

    
1243
            sigfc = RadioSetting("settings.sigfc",
1244
                                 "Bottom status foreground color",
1245
                                 RadioSettingValueList(
1246
                                     LIST_COLOR8,
1247
                                     LIST_COLOR8[_mem.settings.sigfc]))
1248
            basic.append(sigfc)
1249

    
1250
            sigbc = RadioSetting("settings.sigbc",
1251
                                 "Bottom status background color",
1252
                                 RadioSettingValueList(
1253
                                     LIST_COLOR8,
1254
                                     LIST_COLOR8[_mem.settings.sigbc]))
1255
            basic.append(sigbc)
1256

    
1257
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1258
                                RadioSettingValueList(
1259
                                    LIST_COLOR8,
1260
                                    LIST_COLOR8[_mem.settings.rxfc]))
1261
            basic.append(rxfc)
1262

    
1263
            txfc = RadioSetting("settings.txfc",
1264
                                "Transmitting character color",
1265
                                RadioSettingValueList(
1266
                                    LIST_COLOR8,
1267
                                    LIST_COLOR8[_mem.settings.txfc]))
1268
            basic.append(txfc)
1269

    
1270
            txdisp = RadioSetting("settings.txdisp",
1271
                                  "Transmitting status display",
1272
                                  RadioSettingValueList(
1273
                                      LIST_TXDISP,
1274
                                      LIST_TXDISP[_mem.settings.txdisp]))
1275
            basic.append(txdisp)
1276
        else:
1277
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1278
                                 RadioSettingValueList(
1279
                                     LIST_COLOR4,
1280
                                     LIST_COLOR4[_mem.settings.wtled]))
1281
            basic.append(wtled)
1282

    
1283
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1284
                                 RadioSettingValueList(
1285
                                     LIST_COLOR4,
1286
                                     LIST_COLOR4[_mem.settings.rxled]))
1287
            basic.append(rxled)
1288

    
1289
            txled = RadioSetting("settings.txled", "TX backlight Color",
1290
                                 RadioSettingValueList(
1291
                                     LIST_COLOR4,
1292
                                     LIST_COLOR4[_mem.settings.txled]))
1293
            basic.append(txled)
1294

    
1295
        anil = RadioSetting("settings.anil", "ANI length",
1296
                            RadioSettingValueList(
1297
                                LIST_ANIL,
1298
                                LIST_ANIL[_mem.settings.anil]))
1299
        basic.append(anil)
1300

    
1301
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1302
                            RadioSettingValueList(
1303
                                LIST_REPS,
1304
                                LIST_REPS[_mem.settings.reps]))
1305
        basic.append(reps)
1306

    
1307
        repm = RadioSetting("settings.repm", "Relay condition",
1308
                            RadioSettingValueList(
1309
                                LIST_REPM,
1310
                                LIST_REPM[_mem.settings.repm]))
1311
        basic.append(repm)
1312

    
1313
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1314
            if self.COLOR_LCD:
1315
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1316
                                     RadioSettingValueList(
1317
                                         LIST_OFF1TO50,
1318
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1319
                basic.append(tmrmr)
1320
            else:
1321
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1322
                                     RadioSettingValueList(
1323
                                         LIST_OFF1TO50,
1324
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1325
                basic.append(tdrab)
1326

    
1327
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1328
                               RadioSettingValueBoolean(_mem.settings.ste))
1329
            basic.append(ste)
1330

    
1331
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1332
                                 RadioSettingValueList(
1333
                                     LIST_OFF1TO9,
1334
                                     LIST_OFF1TO9[_mem.settings.rpste]))
1335
            basic.append(rpste)
1336

    
1337
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1338
                                 RadioSettingValueList(
1339
                                     LIST_RPTDL,
1340
                                     LIST_RPTDL[_mem.settings.rptdl]))
1341
            basic.append(rptdl)
1342

    
1343
        if str(_mem.fingerprint.fp) in BTECH3:
1344
            mgain = RadioSetting("settings.mgain", "Mic gain",
1345
                                 RadioSettingValueInteger(0, 120,
1346
                                                          _mem.settings.mgain))
1347
            basic.append(mgain)
1348

    
1349
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1350
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1351
                                 RadioSettingValueInteger(0, 60,
1352
                                                          _mem.settings.dtmfg))
1353
            basic.append(dtmfg)
1354

    
1355
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1356
            mgain = RadioSetting("settings.mgain", "Mic gain",
1357
                                 RadioSettingValueInteger(0, 120,
1358
                                                          _mem.settings.mgain))
1359
            basic.append(mgain)
1360

    
1361
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1362
                                  RadioSettingValueList(
1363
                                      LIST_SKIPTX,
1364
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1365
            basic.append(skiptx)
1366

    
1367
            scmode = RadioSetting("settings.scmode", "Scan mode",
1368
                                  RadioSettingValueList(
1369
                                      LIST_SCMODE,
1370
                                      LIST_SCMODE[_mem.settings.scmode]))
1371
            basic.append(scmode)
1372

    
1373
        # Advanced
1374
        def _filter(name):
1375
            filtered = ""
1376
            for char in str(name):
1377
                if char in VALID_CHARS:
1378
                    filtered += char
1379
                else:
1380
                    filtered += " "
1381
            return filtered
1382

    
1383
        _msg = self._memobj.poweron_msg
1384
        if self.COLOR_LCD:
1385
            line1 = RadioSetting("poweron_msg.line1",
1386
                                 "Power-on message line 1",
1387
                                 RadioSettingValueString(0, 8, _filter(
1388
                                                         _msg.line1)))
1389
            advanced.append(line1)
1390
            line2 = RadioSetting("poweron_msg.line2",
1391
                                 "Power-on message line 2",
1392
                                 RadioSettingValueString(0, 8, _filter(
1393
                                                         _msg.line2)))
1394
            advanced.append(line2)
1395
            line3 = RadioSetting("poweron_msg.line3",
1396
                                 "Power-on message line 3",
1397
                                 RadioSettingValueString(0, 8, _filter(
1398
                                                         _msg.line3)))
1399
            advanced.append(line3)
1400
            line4 = RadioSetting("poweron_msg.line4",
1401
                                 "Power-on message line 4",
1402
                                 RadioSettingValueString(0, 8, _filter(
1403
                                                         _msg.line4)))
1404
            advanced.append(line4)
1405
            line5 = RadioSetting("poweron_msg.line5",
1406
                                 "Power-on message line 5",
1407
                                 RadioSettingValueString(0, 8, _filter(
1408
                                                         _msg.line5)))
1409
            advanced.append(line5)
1410
            line6 = RadioSetting("poweron_msg.line6",
1411
                                 "Power-on message line 6",
1412
                                 RadioSettingValueString(0, 8, _filter(
1413
                                                         _msg.line6)))
1414
            advanced.append(line6)
1415
            line7 = RadioSetting("poweron_msg.line7",
1416
                                 "Power-on message line 7",
1417
                                 RadioSettingValueString(0, 8, _filter(
1418
                                                         _msg.line7)))
1419
            advanced.append(line7)
1420
            line8 = RadioSetting("poweron_msg.line8", "Static message",
1421
                                 RadioSettingValueString(0, 8, _filter(
1422
                                                         _msg.line8)))
1423
            advanced.append(line8)
1424
        else:
1425
            line1 = RadioSetting("poweron_msg.line1",
1426
                                 "Power-on message line 1",
1427
                                 RadioSettingValueString(0, 6, _filter(
1428
                                                         _msg.line1)))
1429
            advanced.append(line1)
1430
            line2 = RadioSetting("poweron_msg.line2",
1431
                                 "Power-on message line 2",
1432
                                 RadioSettingValueString(0, 6, _filter(
1433
                                                         _msg.line2)))
1434
            advanced.append(line2)
1435

    
1436
        if self.MODEL in ("UV-2501", "UV-5001"):
1437
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1438
                                   RadioSettingValueBoolean(
1439
                                       _mem.settings2.vfomren))
1440
            advanced.append(vfomren)
1441

    
1442
            reseten = RadioSetting("settings2.reseten", "RESET",
1443
                                   RadioSettingValueBoolean(
1444
                                       _mem.settings2.reseten))
1445
            advanced.append(reseten)
1446

    
1447
            menuen = RadioSetting("settings2.menuen", "Menu",
1448
                                  RadioSettingValueBoolean(
1449
                                      _mem.settings2.menuen))
1450
            advanced.append(menuen)
1451

    
1452
        # Other
1453
        def convert_bytes_to_limit(bytes):
1454
            limit = ""
1455
            for byte in bytes:
1456
                if byte < 10:
1457
                    limit += chr(byte + 0x30)
1458
                else:
1459
                    break
1460
            return limit
1461

    
1462
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1463
            _ranges = self._memobj.ranges220
1464
            ranges = "ranges220"
1465
        else:
1466
            _ranges = self._memobj.ranges
1467
            ranges = "ranges"
1468

    
1469
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1470
        val = RadioSettingValueString(0, 3, _limit)
1471
        val.set_mutable(False)
1472
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1473
        other.append(vhf_low)
1474

    
1475
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1476
        val = RadioSettingValueString(0, 3, _limit)
1477
        val.set_mutable(False)
1478
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1479
        other.append(vhf_high)
1480

    
1481
        if self.BANDS == 3 or self.BANDS == 4:
1482
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1483
            val = RadioSettingValueString(0, 3, _limit)
1484
            val.set_mutable(False)
1485
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1486
            other.append(vhf2_low)
1487

    
1488
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1489
            val = RadioSettingValueString(0, 3, _limit)
1490
            val.set_mutable(False)
1491
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1492
            other.append(vhf2_high)
1493

    
1494
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1495
        val = RadioSettingValueString(0, 3, _limit)
1496
        val.set_mutable(False)
1497
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1498
        other.append(uhf_low)
1499

    
1500
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1501
        val = RadioSettingValueString(0, 3, _limit)
1502
        val.set_mutable(False)
1503
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1504
        other.append(uhf_high)
1505

    
1506
        if self.BANDS == 4:
1507
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1508
            val = RadioSettingValueString(0, 3, _limit)
1509
            val.set_mutable(False)
1510
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1511
            other.append(uhf2_low)
1512

    
1513
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1514
            val = RadioSettingValueString(0, 3, _limit)
1515
            val.set_mutable(False)
1516
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1517
            other.append(uhf2_high)
1518

    
1519
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1520
        val.set_mutable(False)
1521
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1522
        other.append(fp)
1523

    
1524
        # Work
1525
        if self.COLOR_LCD:
1526
            dispab = RadioSetting("settings2.dispab", "Display",
1527
                                  RadioSettingValueList(
1528
                                      LIST_ABCD,
1529
                                      LIST_ABCD[_mem.settings2.dispab]))
1530
            work.append(dispab)
1531
        else:
1532
            dispab = RadioSetting("settings2.dispab", "Display",
1533
                                  RadioSettingValueList(
1534
                                      LIST_AB,
1535
                                      LIST_AB[_mem.settings2.dispab]))
1536
            work.append(dispab)
1537

    
1538
        if self.COLOR_LCD:
1539
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1540
                                  RadioSettingValueList(
1541
                                      LIST_VFOMR,
1542
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1543
            work.append(vfomra)
1544

    
1545
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1546
                                  RadioSettingValueList(
1547
                                      LIST_VFOMR,
1548
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1549
            work.append(vfomrb)
1550

    
1551
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1552
                                  RadioSettingValueList(
1553
                                      LIST_VFOMR,
1554
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1555
            work.append(vfomrc)
1556

    
1557
            vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1558
                                  RadioSettingValueList(
1559
                                      LIST_VFOMR,
1560
                                      LIST_VFOMR[_mem.settings2.vfomrd]))
1561
            work.append(vfomrd)
1562
        else:
1563
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1564
                                 RadioSettingValueList(
1565
                                     LIST_VFOMR,
1566
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1567
            work.append(vfomr)
1568

    
1569
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1570
                               RadioSettingValueBoolean(
1571
                                   _mem.settings2.keylock))
1572
        work.append(keylock)
1573

    
1574
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1575
                             RadioSettingValueInteger(0, 199,
1576
                                                      _mem.settings2.mrcha))
1577
        work.append(mrcha)
1578

    
1579
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1580
                             RadioSettingValueInteger(0, 199,
1581
                                                      _mem.settings2.mrchb))
1582
        work.append(mrchb)
1583

    
1584
        if self.COLOR_LCD:
1585
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1586
                                 RadioSettingValueInteger(
1587
                                     0, 199, _mem.settings2.mrchc))
1588
            work.append(mrchc)
1589

    
1590
            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1591
                                 RadioSettingValueInteger(
1592
                                     0, 199, _mem.settings2.mrchd))
1593
            work.append(mrchd)
1594

    
1595
        def convert_bytes_to_freq(bytes):
1596
            real_freq = 0
1597
            for byte in bytes:
1598
                real_freq = (real_freq * 10) + byte
1599
            return chirp_common.format_freq(real_freq * 10)
1600

    
1601
        def my_validate(value):
1602
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1603
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1604
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1605
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1606
            if self.BANDS == 3 or self.BANDS == 4:
1607
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1608
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1609
            if self.BANDS == 4:
1610
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1611
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1612

    
1613
            value = chirp_common.parse_freq(value)
1614
            msg = ("Can't be less then %i.0000")
1615
            if value > 99000000 and value < _vhf_lower * 1000000:
1616
                raise InvalidValueError(msg % (_vhf_lower))
1617
            msg = ("Can't be betweeb %i.9975-%i.0000")
1618
            if self.BANDS == 2:
1619
                if (_vhf_upper + 1) * 1000000 <= value and \
1620
                        value < _uhf_lower * 1000000:
1621
                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
1622
            if self.BANDS == 3:
1623
                if (_vhf_upper + 1) * 1000000 <= value and \
1624
                        value < _vhf2_lower * 1000000:
1625
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1626
                if (_vhf2_upper + 1) * 1000000 <= value and \
1627
                        value < _uhf_lower * 1000000:
1628
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
1629
            if self.BANDS == 4:
1630
                if (_vhf_upper + 1) * 1000000 <= value and \
1631
                        value < _vhf2_lower * 1000000:
1632
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1633
                if (_vhf2_upper + 1) * 1000000 <= value and \
1634
                        value < _uhf2_lower * 1000000:
1635
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
1636
                if (_uhf2_upper + 1) * 1000000 <= value and \
1637
                        value < _uhf_lower * 1000000:
1638
                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
1639
            msg = ("Can't be greater then %i.9975")
1640
            if value > 99000000 and value >= _uhf_upper * 1000000:
1641
                raise InvalidValueError(msg % (_uhf_upper))
1642
            return chirp_common.format_freq(value)
1643

    
1644
        def apply_freq(setting, obj):
1645
            value = chirp_common.parse_freq(str(setting.value)) / 10
1646
            for i in range(7, -1, -1):
1647
                obj.freq[i] = value % 10
1648
                value /= 10
1649

    
1650
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1651
                                        _mem.vfo.a.freq))
1652
        val1a.set_validate_callback(my_validate)
1653
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1654
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1655
        work.append(vfoafreq)
1656

    
1657
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1658
                                        _mem.vfo.b.freq))
1659
        val1b.set_validate_callback(my_validate)
1660
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1661
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1662
        work.append(vfobfreq)
1663

    
1664
        if self.COLOR_LCD:
1665
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1666
                                            _mem.vfo.c.freq))
1667
            val1c.set_validate_callback(my_validate)
1668
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
1669
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
1670
            work.append(vfocfreq)
1671

    
1672
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1673
                                            _mem.vfo.d.freq))
1674
            val1d.set_validate_callback(my_validate)
1675
            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
1676
            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
1677
            work.append(vfodfreq)
1678

    
1679
        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1680
                                  RadioSettingValueList(
1681
                                      LIST_SHIFT,
1682
                                      LIST_SHIFT[_mem.vfo.a.shiftd]))
1683
        work.append(vfoashiftd)
1684

    
1685
        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1686
                                  RadioSettingValueList(
1687
                                      LIST_SHIFT,
1688
                                      LIST_SHIFT[_mem.vfo.b.shiftd]))
1689
        work.append(vfobshiftd)
1690

    
1691
        if self.COLOR_LCD:
1692
            vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
1693
                                      RadioSettingValueList(
1694
                                          LIST_SHIFT,
1695
                                          LIST_SHIFT[_mem.vfo.c.shiftd]))
1696
            work.append(vfocshiftd)
1697

    
1698
            vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
1699
                                      RadioSettingValueList(
1700
                                          LIST_SHIFT,
1701
                                          LIST_SHIFT[_mem.vfo.d.shiftd]))
1702
            work.append(vfodshiftd)
1703

    
1704
        def convert_bytes_to_offset(bytes):
1705
            real_offset = 0
1706
            for byte in bytes:
1707
                real_offset = (real_offset * 10) + byte
1708
            return chirp_common.format_freq(real_offset * 1000)
1709

    
1710
        def apply_offset(setting, obj):
1711
            value = chirp_common.parse_freq(str(setting.value)) / 1000
1712
            for i in range(5, -1, -1):
1713
                obj.offset[i] = value % 10
1714
                value /= 10
1715

    
1716
        if self.COLOR_LCD:
1717
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1718
                                            _mem.vfo.a.offset))
1719
            vfoaoffset = RadioSetting("vfo.a.offset",
1720
                                      "VFO A offset (0.000-999.999)", val1a)
1721
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1722
            work.append(vfoaoffset)
1723

    
1724
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1725
                                            _mem.vfo.b.offset))
1726
            vfoboffset = RadioSetting("vfo.b.offset",
1727
                                      "VFO B offset (0.000-999.999)", val1b)
1728
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1729
            work.append(vfoboffset)
1730

    
1731
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1732
                                            _mem.vfo.c.offset))
1733
            vfocoffset = RadioSetting("vfo.c.offset",
1734
                                      "VFO C offset (0.000-999.999)", val1c)
1735
            vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
1736
            work.append(vfocoffset)
1737

    
1738
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1739
                                            _mem.vfo.d.offset))
1740
            vfodoffset = RadioSetting("vfo.d.offset",
1741
                                      "VFO D offset (0.000-999.999)", val1d)
1742
            vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
1743
            work.append(vfodoffset)
1744
        else:
1745
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1746
                                            _mem.vfo.a.offset))
1747
            vfoaoffset = RadioSetting("vfo.a.offset",
1748
                                      "VFO A offset (0.000-99.999)", val1a)
1749
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1750
            work.append(vfoaoffset)
1751

    
1752
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1753
                                            _mem.vfo.b.offset))
1754
            vfoboffset = RadioSetting("vfo.b.offset",
1755
                                      "VFO B offset (0.000-99.999)", val1b)
1756
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1757
            work.append(vfoboffset)
1758

    
1759
        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
1760
                               RadioSettingValueList(
1761
                                   LIST_TXP,
1762
                                   LIST_TXP[_mem.vfo.a.power]))
1763
        work.append(vfoatxp)
1764

    
1765
        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
1766
                               RadioSettingValueList(
1767
                                   LIST_TXP,
1768
                                   LIST_TXP[_mem.vfo.b.power]))
1769
        work.append(vfobtxp)
1770

    
1771
        if self.COLOR_LCD:
1772
            vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
1773
                                   RadioSettingValueList(
1774
                                       LIST_TXP,
1775
                                       LIST_TXP[_mem.vfo.c.power]))
1776
            work.append(vfoctxp)
1777

    
1778
            vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
1779
                                   RadioSettingValueList(
1780
                                       LIST_TXP,
1781
                                       LIST_TXP[_mem.vfo.d.power]))
1782
            work.append(vfodtxp)
1783

    
1784
        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
1785
                                RadioSettingValueList(
1786
                                    LIST_WIDE,
1787
                                    LIST_WIDE[_mem.vfo.a.wide]))
1788
        work.append(vfoawide)
1789

    
1790
        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
1791
                                RadioSettingValueList(
1792
                                    LIST_WIDE,
1793
                                    LIST_WIDE[_mem.vfo.b.wide]))
1794
        work.append(vfobwide)
1795

    
1796
        if self.COLOR_LCD:
1797
            vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
1798
                                    RadioSettingValueList(
1799
                                        LIST_WIDE,
1800
                                        LIST_WIDE[_mem.vfo.c.wide]))
1801
            work.append(vfocwide)
1802

    
1803
            vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
1804
                                    RadioSettingValueList(
1805
                                        LIST_WIDE,
1806
                                        LIST_WIDE[_mem.vfo.d.wide]))
1807
            work.append(vfodwide)
1808

    
1809
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
1810
                                RadioSettingValueList(
1811
                                    LIST_STEP,
1812
                                    LIST_STEP[_mem.vfo.a.step]))
1813
        work.append(vfoastep)
1814

    
1815
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
1816
                                RadioSettingValueList(
1817
                                    LIST_STEP,
1818
                                    LIST_STEP[_mem.vfo.b.step]))
1819
        work.append(vfobstep)
1820

    
1821
        if self.COLOR_LCD:
1822
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
1823
                                    RadioSettingValueList(
1824
                                        LIST_STEP,
1825
                                        LIST_STEP[_mem.vfo.c.step]))
1826
            work.append(vfocstep)
1827

    
1828
            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
1829
                                    RadioSettingValueList(
1830
                                        LIST_STEP,
1831
                                        LIST_STEP[_mem.vfo.d.step]))
1832
            work.append(vfodstep)
1833

    
1834
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
1835
                                  RadioSettingValueList(
1836
                                      OPTSIG_LIST,
1837
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
1838
        work.append(vfoaoptsig)
1839

    
1840
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
1841
                                  RadioSettingValueList(
1842
                                      OPTSIG_LIST,
1843
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
1844
        work.append(vfoboptsig)
1845

    
1846
        if self.COLOR_LCD:
1847
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
1848
                                      RadioSettingValueList(
1849
                                          OPTSIG_LIST,
1850
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
1851
            work.append(vfocoptsig)
1852

    
1853
            vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
1854
                                      RadioSettingValueList(
1855
                                          OPTSIG_LIST,
1856
                                          OPTSIG_LIST[_mem.vfo.d.optsig]))
1857
            work.append(vfodoptsig)
1858

    
1859
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
1860
                                  RadioSettingValueList(
1861
                                      SPMUTE_LIST,
1862
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
1863
        work.append(vfoaspmute)
1864

    
1865
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
1866
                                  RadioSettingValueList(
1867
                                      SPMUTE_LIST,
1868
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
1869
        work.append(vfobspmute)
1870

    
1871
        if self.COLOR_LCD:
1872
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
1873
                                      RadioSettingValueList(
1874
                                          SPMUTE_LIST,
1875
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
1876
            work.append(vfocspmute)
1877

    
1878
            vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
1879
                                      RadioSettingValueList(
1880
                                          SPMUTE_LIST,
1881
                                          SPMUTE_LIST[_mem.vfo.d.spmute]))
1882
            work.append(vfodspmute)
1883

    
1884
        if not self.COLOR_LCD or \
1885
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
1886
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
1887
                                   RadioSettingValueBoolean(
1888
                                       _mem.vfo.a.scramble))
1889
            work.append(vfoascr)
1890

    
1891
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
1892
                                   RadioSettingValueBoolean(
1893
                                       _mem.vfo.b.scramble))
1894
            work.append(vfobscr)
1895

    
1896
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
1897
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
1898
                                   RadioSettingValueBoolean(
1899
                                       _mem.vfo.c.scramble))
1900
            work.append(vfocscr)
1901

    
1902
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
1903
                                   RadioSettingValueBoolean(
1904
                                       _mem.vfo.d.scramble))
1905
            work.append(vfodscr)
1906

    
1907
        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
1908
                                 RadioSettingValueList(
1909
                                     PTTIDCODE_LIST,
1910
                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
1911
        work.append(vfoascode)
1912

    
1913
        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
1914
                                 RadioSettingValueList(
1915
                                     PTTIDCODE_LIST,
1916
                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
1917
        work.append(vfobscode)
1918

    
1919
        if self.COLOR_LCD:
1920
            vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
1921
                                     RadioSettingValueList(
1922
                                         PTTIDCODE_LIST,
1923
                                         PTTIDCODE_LIST[_mem.vfo.c.scode]))
1924
            work.append(vfocscode)
1925

    
1926
            vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
1927
                                     RadioSettingValueList(
1928
                                         PTTIDCODE_LIST,
1929
                                         PTTIDCODE_LIST[_mem.vfo.d.scode]))
1930
            work.append(vfodscode)
1931

    
1932
        pttid = RadioSetting("settings.pttid", "PTT ID",
1933
                             RadioSettingValueList(
1934
                                 PTTID_LIST,
1935
                                 PTTID_LIST[_mem.settings.pttid]))
1936
        work.append(pttid)
1937

    
1938
        if not self.COLOR_LCD:
1939
            # FM presets
1940
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
1941
            top.append(fm_presets)
1942

    
1943
            def fm_validate(value):
1944
                if value == 0:
1945
                    return chirp_common.format_freq(value)
1946
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
1947
                    msg = ("FM-Preset-Frequency: " +
1948
                           "Must be between 87.5 and 108 MHz")
1949
                    raise InvalidValueError(msg)
1950
                return value
1951

    
1952
            def apply_fm_preset_name(setting, obj):
1953
                valstring = str(setting.value)
1954
                for i in range(0, 6):
1955
                    if valstring[i] in VALID_CHARS:
1956
                        obj[i] = valstring[i]
1957
                    else:
1958
                        obj[i] = '0xff'
1959

    
1960
            def apply_fm_freq(setting, obj):
1961
                value = chirp_common.parse_freq(str(setting.value)) / 10
1962
                for i in range(7, -1, -1):
1963
                    obj.freq[i] = value % 10
1964
                    value /= 10
1965

    
1966
            _presets = self._memobj.fm_radio_preset
1967
            i = 1
1968
            for preset in _presets:
1969
                line = RadioSetting("fm_presets_" + str(i),
1970
                                    "Station name " + str(i),
1971
                                    RadioSettingValueString(0, 6, _filter(
1972
                                        preset.broadcast_station_name)))
1973
                line.set_apply_callback(apply_fm_preset_name,
1974
                                        preset.broadcast_station_name)
1975

    
1976
                val = RadioSettingValueFloat(0, 108,
1977
                                             convert_bytes_to_freq(
1978
                                                 preset.freq))
1979
                fmfreq = RadioSetting("fm_presets_" + str(i) + "_freq",
1980
                                      "Frequency " + str(i), val)
1981
                val.set_validate_callback(fm_validate)
1982
                fmfreq.set_apply_callback(apply_fm_freq, preset)
1983
                fm_presets.append(line)
1984
                fm_presets.append(fmfreq)
1985

    
1986
                i = i + 1
1987

    
1988
        # DTMF-Setting
1989
        dtmf_enc_settings = RadioSettingGroup("dtmf_enc_settings",
1990
                                              "DTMF Encoding Settings")
1991
        dtmf_dec_settings = RadioSettingGroup("dtmf_dec_settings",
1992
                                              "DTMF Decoding Settings")
1993
        top.append(dtmf_enc_settings)
1994
        top.append(dtmf_dec_settings)
1995
        txdisable = RadioSetting("dtmf_settings.txdisable",
1996
                                 "TX-Disable",
1997
                                 RadioSettingValueBoolean(
1998
                                     _mem.dtmf_settings.txdisable))
1999
        dtmf_enc_settings.append(txdisable)
2000

    
2001
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
2002
                                 "RX-Disable",
2003
                                 RadioSettingValueBoolean(
2004
                                     _mem.dtmf_settings.rxdisable))
2005
        dtmf_enc_settings.append(rxdisable)
2006

    
2007
        dtmfspeed_on = RadioSetting(
2008
            "dtmf_settings.dtmfspeed_on",
2009
            "DTMF Speed (On Time)",
2010
            RadioSettingValueList(LIST_DTMF_SPEED,
2011
                                  LIST_DTMF_SPEED[
2012
                                      _mem.dtmf_settings.dtmfspeed_on]))
2013
        dtmf_enc_settings.append(dtmfspeed_on)
2014

    
2015
        dtmfspeed_off = RadioSetting(
2016
            "dtmf_settings.dtmfspeed_off",
2017
            "DTMF Speed (Off Time)",
2018
            RadioSettingValueList(LIST_DTMF_SPEED,
2019
                                  LIST_DTMF_SPEED[
2020
                                      _mem.dtmf_settings.dtmfspeed_off]))
2021
        dtmf_enc_settings.append(dtmfspeed_off)
2022

    
2023
        def memory2string(dmtf_mem):
2024
            dtmf_string = ""
2025
            for digit in dmtf_mem:
2026
                if digit != 255:
2027
                    index = LIST_DTMF_VALUES.index(digit)
2028
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2029
            return dtmf_string
2030

    
2031
        def apply_dmtf_frame(setting, obj):
2032
            LOG.debug("Setting DTMF-Code: " + str(setting.value))
2033
            val_string = str(setting.value)
2034
            for i in range(0, 16):
2035
                obj[i] = 255
2036
            i = 0
2037
            for current_char in val_string:
2038
                current_char = current_char.upper()
2039
                index = LIST_DTMF_DIGITS.index(current_char)
2040
                obj[i] = LIST_DTMF_VALUES[index]
2041
                i = i + 1
2042

    
2043
        codes = self._memobj.dtmf_codes
2044
        i = 1
2045
        for dtmfcode in codes:
2046
            val = RadioSettingValueString(0, 16, memory2string(
2047
                                              dtmfcode.code),
2048
                                          False, CHARSET_DTMF_DIGITS)
2049
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
2050
                                "DMTF Code " + str(i), val)
2051
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
2052
            dtmf_enc_settings.append(line)
2053
            i = i + 1
2054

    
2055
        line = RadioSetting("dtmf_settings.mastervice",
2056
                            "Master and Vice ID",
2057
                            RadioSettingValueBoolean(
2058
                                _mem.dtmf_settings.mastervice))
2059
        dtmf_dec_settings.append(line)
2060

    
2061
        val = RadioSettingValueString(0, 16, memory2string(
2062
                                          _mem.dtmf_settings.masterid),
2063
                                      False, CHARSET_DTMF_DIGITS)
2064
        line = RadioSetting("dtmf_settings.masterid",
2065
                            "Master Control ID ", val)
2066
        line.set_apply_callback(apply_dmtf_frame,
2067
                                _mem.dtmf_settings.masterid)
2068
        dtmf_dec_settings.append(line)
2069

    
2070
        line = RadioSetting("dtmf_settings.minspection",
2071
                            "Master Inspection",
2072
                            RadioSettingValueBoolean(
2073
                                _mem.dtmf_settings.minspection))
2074
        dtmf_dec_settings.append(line)
2075

    
2076
        line = RadioSetting("dtmf_settings.mmonitor",
2077
                            "Master Monitor",
2078
                            RadioSettingValueBoolean(
2079
                                _mem.dtmf_settings.mmonitor))
2080
        dtmf_dec_settings.append(line)
2081

    
2082
        line = RadioSetting("dtmf_settings.mstun",
2083
                            "Master Stun",
2084
                            RadioSettingValueBoolean(
2085
                                _mem.dtmf_settings.mstun))
2086
        dtmf_dec_settings.append(line)
2087

    
2088
        line = RadioSetting("dtmf_settings.mkill",
2089
                            "Master Kill",
2090
                            RadioSettingValueBoolean(
2091
                                _mem.dtmf_settings.mkill))
2092
        dtmf_dec_settings.append(line)
2093

    
2094
        line = RadioSetting("dtmf_settings.mrevive",
2095
                            "Master Revive",
2096
                            RadioSettingValueBoolean(
2097
                                _mem.dtmf_settings.mrevive))
2098
        dtmf_dec_settings.append(line)
2099

    
2100
        val = RadioSettingValueString(0, 16, memory2string(
2101
                                          _mem.dtmf_settings.viceid),
2102
                                      False, CHARSET_DTMF_DIGITS)
2103
        line = RadioSetting("dtmf_settings.viceid",
2104
                            "Vice Control ID ", val)
2105
        line.set_apply_callback(apply_dmtf_frame,
2106
                                _mem.dtmf_settings.viceid)
2107
        dtmf_dec_settings.append(line)
2108

    
2109
        line = RadioSetting("dtmf_settings.vinspection",
2110
                            "Vice Inspection",
2111
                            RadioSettingValueBoolean(
2112
                                _mem.dtmf_settings.vinspection))
2113
        dtmf_dec_settings.append(line)
2114

    
2115
        line = RadioSetting("dtmf_settings.vmonitor",
2116
                            "Vice Monitor",
2117
                            RadioSettingValueBoolean(
2118
                                _mem.dtmf_settings.vmonitor))
2119
        dtmf_dec_settings.append(line)
2120

    
2121
        line = RadioSetting("dtmf_settings.vstun",
2122
                            "Vice Stun",
2123
                            RadioSettingValueBoolean(
2124
                                _mem.dtmf_settings.vstun))
2125
        dtmf_dec_settings.append(line)
2126

    
2127
        line = RadioSetting("dtmf_settings.vkill",
2128
                            "Vice Kill",
2129
                            RadioSettingValueBoolean(
2130
                                _mem.dtmf_settings.vkill))
2131
        dtmf_dec_settings.append(line)
2132

    
2133
        line = RadioSetting("dtmf_settings.vrevive",
2134
                            "Vice Revive",
2135
                            RadioSettingValueBoolean(
2136
                                _mem.dtmf_settings.vrevive))
2137
        dtmf_dec_settings.append(line)
2138

    
2139
        val = RadioSettingValueString(0, 16, memory2string(
2140
                                          _mem.dtmf_settings.inspection),
2141
                                      False, CHARSET_DTMF_DIGITS)
2142
        line = RadioSetting("dtmf_settings.inspection",
2143
                            "Inspection", val)
2144
        line.set_apply_callback(apply_dmtf_frame,
2145
                                _mem.dtmf_settings.inspection)
2146
        dtmf_dec_settings.append(line)
2147

    
2148
        val = RadioSettingValueString(0, 16, memory2string(
2149
                                          _mem.dtmf_settings.alarmcode),
2150
                                      False, CHARSET_DTMF_DIGITS)
2151
        line = RadioSetting("dtmf_settings.alarmcode",
2152
                            "Alarm", val)
2153
        line.set_apply_callback(apply_dmtf_frame,
2154
                                _mem.dtmf_settings.alarmcode)
2155
        dtmf_dec_settings.append(line)
2156

    
2157
        val = RadioSettingValueString(0, 16, memory2string(
2158
                                          _mem.dtmf_settings.kill),
2159
                                      False, CHARSET_DTMF_DIGITS)
2160
        line = RadioSetting("dtmf_settings.kill",
2161
                            "Kill", val)
2162
        line.set_apply_callback(apply_dmtf_frame,
2163
                                _mem.dtmf_settings.kill)
2164
        dtmf_dec_settings.append(line)
2165

    
2166
        val = RadioSettingValueString(0, 16, memory2string(
2167
                                          _mem.dtmf_settings.monitor),
2168
                                      False, CHARSET_DTMF_DIGITS)
2169
        line = RadioSetting("dtmf_settings.monitor",
2170
                            "Monitor", val)
2171
        line.set_apply_callback(apply_dmtf_frame,
2172
                                _mem.dtmf_settings.monitor)
2173
        dtmf_dec_settings.append(line)
2174

    
2175
        val = RadioSettingValueString(0, 16, memory2string(
2176
                                          _mem.dtmf_settings.stun),
2177
                                      False, CHARSET_DTMF_DIGITS)
2178
        line = RadioSetting("dtmf_settings.stun",
2179
                            "Stun", val)
2180
        line.set_apply_callback(apply_dmtf_frame,
2181
                                _mem.dtmf_settings.stun)
2182
        dtmf_dec_settings.append(line)
2183

    
2184
        val = RadioSettingValueString(0, 16, memory2string(
2185
                                          _mem.dtmf_settings.revive),
2186
                                      False, CHARSET_DTMF_DIGITS)
2187
        line = RadioSetting("dtmf_settings.revive",
2188
                            "Revive", val)
2189
        line.set_apply_callback(apply_dmtf_frame,
2190
                                _mem.dtmf_settings.revive)
2191
        dtmf_dec_settings.append(line)
2192

    
2193
        def apply_dmtf_listvalue(setting, obj):
2194
            LOG.debug("Setting value: " + str(setting.value) + " from list")
2195
            val = str(setting.value)
2196
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2197
            val = LIST_DTMF_SPECIAL_VALUES[index]
2198
            obj.set_value(val)
2199

    
2200
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
2201
        line = RadioSetting(
2202
            "dtmf_settings.groupcode",
2203
            "Group Code",
2204
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2205
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2206
        line.set_apply_callback(apply_dmtf_listvalue,
2207
                                _mem.dtmf_settings.groupcode)
2208
        dtmf_dec_settings.append(line)
2209

    
2210
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
2211
        line = RadioSetting(
2212
            "dtmf_settings.spacecode",
2213
            "Space Code",
2214
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2215
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2216
        line.set_apply_callback(apply_dmtf_listvalue,
2217
                                _mem.dtmf_settings.spacecode)
2218
        dtmf_dec_settings.append(line)
2219

    
2220
        if self.COLOR_LCD:
2221
            line = RadioSetting(
2222
                "dtmf_settings.resettime",
2223
                "Reset time",
2224
                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
2225
                                      LIST_5TONE_RESET_COLOR[
2226
                                          _mem.dtmf_settings.resettime]))
2227
            dtmf_dec_settings.append(line)
2228
        else:
2229
            line = RadioSetting(
2230
                "dtmf_settings.resettime",
2231
                "Reset time",
2232
                RadioSettingValueList(LIST_5TONE_RESET,
2233
                                      LIST_5TONE_RESET[
2234
                                          _mem.dtmf_settings.resettime]))
2235
            dtmf_dec_settings.append(line)
2236

    
2237
        line = RadioSetting(
2238
            "dtmf_settings.delayproctime",
2239
            "Delay processing time",
2240
            RadioSettingValueList(LIST_DTMF_DELAY,
2241
                                  LIST_DTMF_DELAY[
2242
                                      _mem.dtmf_settings.delayproctime]))
2243
        dtmf_dec_settings.append(line)
2244

    
2245
        # 5 Tone Settings
2246
        stds_5tone = RadioSettingGroup("stds_5tone", "Standards")
2247
        codes_5tone = RadioSettingGroup("codes_5tone", "Codes")
2248

    
2249
        group_5tone = RadioSettingGroup("group_5tone", "5 Tone Settings")
2250
        group_5tone.append(stds_5tone)
2251
        group_5tone.append(codes_5tone)
2252

    
2253
        top.append(group_5tone)
2254

    
2255
        def apply_list_value(setting, obj):
2256
            options = setting.value.get_options()
2257
            obj.set_value(options.index(str(setting.value)))
2258

    
2259
        _5tone_standards = self._memobj._5tone_std_settings
2260
        i = 0
2261
        for standard in _5tone_standards:
2262
            std_5tone = RadioSettingGroup("std_5tone_" + str(i),
2263
                                          LIST_5TONE_STANDARDS[i])
2264
            stds_5tone.append(std_5tone)
2265

    
2266
            period = standard.period
2267
            if period == 255:
2268
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2269
                          " is not yet configured. Setting to 70ms.")
2270
                period = 5
2271

    
2272
            if period <= len(LIST_5TONE_STANDARD_PERIODS):
2273
                line = RadioSetting(
2274
                    "_5tone_std_settings_" + str(i) + "_period",
2275
                    "Period (ms)", RadioSettingValueList
2276
                    (LIST_5TONE_STANDARD_PERIODS,
2277
                     LIST_5TONE_STANDARD_PERIODS[period]))
2278
                line.set_apply_callback(apply_list_value, standard.period)
2279
                std_5tone.append(line)
2280
            else:
2281
                LOG.debug("Invalid value for 5tone period! Disabling.")
2282

    
2283
            group_tone = standard.group_tone
2284
            if group_tone == 255:
2285
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2286
                          " is not yet configured. Setting to A.")
2287
                group_tone = 10
2288

    
2289
            if group_tone <= len(LIST_5TONE_DIGITS):
2290
                line = RadioSetting(
2291
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2292
                    "Group Tone",
2293
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2294
                                          LIST_5TONE_DIGITS[
2295
                                              group_tone]))
2296
                line.set_apply_callback(apply_list_value,
2297
                                        standard.group_tone)
2298
                std_5tone.append(line)
2299
            else:
2300
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2301

    
2302
            repeat_tone = standard.repeat_tone
2303
            if repeat_tone == 255:
2304
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2305
                          " is not yet configured. Setting to E.")
2306
                repeat_tone = 14
2307

    
2308
            if repeat_tone <= len(LIST_5TONE_DIGITS):
2309
                line = RadioSetting(
2310
                    "_5tone_std_settings_" + str(i) + "_repttone",
2311
                    "Repeat Tone",
2312
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2313
                                          LIST_5TONE_DIGITS[
2314
                                              repeat_tone]))
2315
                line.set_apply_callback(apply_list_value,
2316
                                        standard.repeat_tone)
2317
                std_5tone.append(line)
2318
            else:
2319
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2320
            i = i + 1
2321

    
2322
        def my_apply_5tonestdlist_value(setting, obj):
2323
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2324
                obj.set_value(0xFF)
2325
            else:
2326
                obj.set_value(LIST_5TONE_STANDARDS.
2327
                              index(str(setting.value)))
2328

    
2329
        def apply_5tone_frame(setting, obj):
2330
            LOG.debug("Setting 5 Tone: " + str(setting.value))
2331
            valstring = str(setting.value)
2332
            if len(valstring) == 0:
2333
                for i in range(0, 5):
2334
                    obj[i] = 255
2335
            else:
2336
                validFrame = True
2337
                for i in range(0, 5):
2338
                    currentChar = valstring[i].upper()
2339
                    if currentChar in LIST_5TONE_DIGITS:
2340
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2341
                    else:
2342
                        validFrame = False
2343
                        LOG.debug("invalid char: " + str(currentChar))
2344
                if not validFrame:
2345
                    LOG.debug("setting whole frame to FF")
2346
                    for i in range(0, 5):
2347
                        obj[i] = 255
2348

    
2349
        def validate_5tone_frame(value):
2350
            if (len(str(value)) != 5) and (len(str(value)) != 0):
2351
                msg = ("5 Tone must have 5 digits or 0 digits")
2352
                raise InvalidValueError(msg)
2353
            for digit in str(value):
2354
                if digit.upper() not in LIST_5TONE_DIGITS:
2355
                    msg = (str(digit) + " is not a valid digit for 5tones")
2356
                    raise InvalidValueError(msg)
2357
            return value
2358

    
2359
        def frame2string(frame):
2360
            frameString = ""
2361
            for digit in frame:
2362
                if digit != 255:
2363
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2364
            return frameString
2365

    
2366
        _5tone_codes = self._memobj._5tone_codes
2367
        i = 1
2368
        for code in _5tone_codes:
2369
            code_5tone = RadioSettingGroup("code_5tone_" + str(i),
2370
                                           "5 Tone code " + str(i))
2371
            codes_5tone.append(code_5tone)
2372
            if (code.standard == 255):
2373
                currentVal = 15
2374
            else:
2375
                currentVal = code.standard
2376
            line = RadioSetting("_5tone_code_" + str(i) + "_std",
2377
                                " Standard",
2378
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2379
                                                      LIST_5TONE_STANDARDS[
2380
                                                          currentVal]))
2381
            line.set_apply_callback(my_apply_5tonestdlist_value,
2382
                                    code.standard)
2383
            code_5tone.append(line)
2384

    
2385
            val = RadioSettingValueString(0, 6,
2386
                                          frame2string(code.frame1), False)
2387
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
2388
                                " Frame 1", val)
2389
            val.set_validate_callback(validate_5tone_frame)
2390
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2391
            code_5tone.append(line)
2392

    
2393
            val = RadioSettingValueString(0, 6,
2394
                                          frame2string(code.frame2), False)
2395
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2396
                                " Frame 2", val)
2397
            val.set_validate_callback(validate_5tone_frame)
2398
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2399
            code_5tone.append(line)
2400

    
2401
            val = RadioSettingValueString(0, 6,
2402
                                          frame2string(code.frame3), False)
2403
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2404
                                " Frame 3", val)
2405
            val.set_validate_callback(validate_5tone_frame)
2406
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2407
            code_5tone.append(line)
2408
            i = i + 1
2409

    
2410
        _5_tone_decode1 = RadioSetting(
2411
            "_5tone_settings._5tone_decode_call_frame1",
2412
            "5 Tone decode call Frame 1",
2413
            RadioSettingValueBoolean(
2414
                _mem._5tone_settings._5tone_decode_call_frame1))
2415
        group_5tone.append(_5_tone_decode1)
2416

    
2417
        _5_tone_decode2 = RadioSetting(
2418
            "_5tone_settings._5tone_decode_call_frame2",
2419
            "5 Tone decode call Frame 2",
2420
            RadioSettingValueBoolean(
2421
                _mem._5tone_settings._5tone_decode_call_frame2))
2422
        group_5tone.append(_5_tone_decode2)
2423

    
2424
        _5_tone_decode3 = RadioSetting(
2425
            "_5tone_settings._5tone_decode_call_frame3",
2426
            "5 Tone decode call Frame 3",
2427
            RadioSettingValueBoolean(
2428
                _mem._5tone_settings._5tone_decode_call_frame3))
2429
        group_5tone.append(_5_tone_decode3)
2430

    
2431
        _5_tone_decode_disp1 = RadioSetting(
2432
            "_5tone_settings._5tone_decode_disp_frame1",
2433
            "5 Tone decode disp Frame 1",
2434
            RadioSettingValueBoolean(
2435
                _mem._5tone_settings._5tone_decode_disp_frame1))
2436
        group_5tone.append(_5_tone_decode_disp1)
2437

    
2438
        _5_tone_decode_disp2 = RadioSetting(
2439
            "_5tone_settings._5tone_decode_disp_frame2",
2440
            "5 Tone decode disp Frame 2",
2441
            RadioSettingValueBoolean(
2442
                _mem._5tone_settings._5tone_decode_disp_frame2))
2443
        group_5tone.append(_5_tone_decode_disp2)
2444

    
2445
        _5_tone_decode_disp3 = RadioSetting(
2446
            "_5tone_settings._5tone_decode_disp_frame3",
2447
            "5 Tone decode disp Frame 3",
2448
            RadioSettingValueBoolean(
2449
                _mem._5tone_settings._5tone_decode_disp_frame3))
2450
        group_5tone.append(_5_tone_decode_disp3)
2451

    
2452
        decode_standard = _mem._5tone_settings.decode_standard
2453
        if decode_standard == 255:
2454
            decode_standard = 0
2455
        if decode_standard <= len(LIST_5TONE_STANDARDS_without_none):
2456
            line = RadioSetting("_5tone_settings.decode_standard",
2457
                                "5 Tone-decode Standard",
2458
                                RadioSettingValueList(
2459
                                    LIST_5TONE_STANDARDS_without_none,
2460
                                    LIST_5TONE_STANDARDS_without_none[
2461
                                        decode_standard]))
2462
            group_5tone.append(line)
2463
        else:
2464
            LOG.debug("Invalid decode std...")
2465

    
2466
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2467
        if _5tone_delay1 == 255:
2468
            _5tone_delay1 = 20
2469

    
2470
        if _5tone_delay1 <= len(LIST_5TONE_DELAY):
2471
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2472
                                         LIST_5TONE_DELAY[
2473
                                             _5tone_delay1])
2474
            line = RadioSetting("_5tone_settings._5tone_delay1",
2475
                                "5 Tone Delay Frame 1", list)
2476
            group_5tone.append(line)
2477
        else:
2478
            LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
2479

    
2480
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2481
        if _5tone_delay2 == 255:
2482
            _5tone_delay2 = 20
2483
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2484

    
2485
        if _5tone_delay2 <= len(LIST_5TONE_DELAY):
2486
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2487
                                         LIST_5TONE_DELAY[
2488
                                             _5tone_delay2])
2489
            line = RadioSetting("_5tone_settings._5tone_delay2",
2490
                                "5 Tone Delay Frame 2", list)
2491
            group_5tone.append(line)
2492
        else:
2493
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2494

    
2495
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2496
        if _5tone_delay3 == 255:
2497
            _5tone_delay3 = 20
2498
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2499

    
2500
        if _5tone_delay3 <= len(LIST_5TONE_DELAY):
2501
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2502
                                         LIST_5TONE_DELAY[
2503
                                             _5tone_delay3])
2504
            line = RadioSetting("_5tone_settings._5tone_delay3",
2505
                                "5 Tone Delay Frame 3", list)
2506
            group_5tone.append(line)
2507
        else:
2508
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2509

    
2510
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2511
        if ext_length == 255:
2512
            ext_length = 0
2513
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2514

    
2515
        if ext_length <= len(LIST_5TONE_DELAY):
2516
            list = RadioSettingValueList(
2517
                LIST_5TONE_DELAY,
2518
                LIST_5TONE_DELAY[
2519
                    ext_length])
2520
            line = RadioSetting(
2521
                "_5tone_settings._5tone_first_digit_ext_length",
2522
                "First digit extend length", list)
2523
            group_5tone.append(line)
2524
        else:
2525
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2526

    
2527
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2528
        if decode_reset_time == 255:
2529
            decode_reset_time = 59
2530
            LOG.debug("Decode reset time unconfigured. resetting.")
2531
        if decode_reset_time <= len(LIST_5TONE_RESET):
2532
            list = RadioSettingValueList(
2533
                LIST_5TONE_RESET,
2534
                LIST_5TONE_RESET[
2535
                    decode_reset_time])
2536
            line = RadioSetting("_5tone_settings.decode_reset_time",
2537
                                "Decode reset time", list)
2538
            group_5tone.append(line)
2539
        else:
2540
            LOG.debug("Invalid value decode reset time! Disabling.")
2541

    
2542
        # 2 Tone
2543
        encode_2tone = RadioSettingGroup("encode_2tone", "2 Tone Encode")
2544
        decode_2tone = RadioSettingGroup("decode_2tone", "2 Code Decode")
2545

    
2546
        top.append(encode_2tone)
2547
        top.append(decode_2tone)
2548

    
2549
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2550
        if duration_1st_tone == 255:
2551
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2552
                      "configured. Setting to 600ms")
2553
            duration_1st_tone = 60
2554

    
2555
        if duration_1st_tone <= len(LIST_5TONE_DELAY):
2556
            line = RadioSetting("_2tone.duration_1st_tone",
2557
                                "Duration 1st Tone",
2558
                                RadioSettingValueList(LIST_5TONE_DELAY,
2559
                                                      LIST_5TONE_DELAY[
2560
                                                          duration_1st_tone]))
2561
            encode_2tone.append(line)
2562

    
2563
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2564
        if duration_2nd_tone == 255:
2565
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2566
                      "configured. Setting to 600ms")
2567
            duration_2nd_tone = 60
2568

    
2569
        if duration_2nd_tone <= len(LIST_5TONE_DELAY):
2570
            line = RadioSetting("_2tone.duration_2nd_tone",
2571
                                "Duration 2nd Tone",
2572
                                RadioSettingValueList(LIST_5TONE_DELAY,
2573
                                                      LIST_5TONE_DELAY[
2574
                                                          duration_2nd_tone]))
2575
            encode_2tone.append(line)
2576

    
2577
        duration_gap = self._memobj._2tone.duration_gap
2578
        if duration_gap == 255:
2579
            LOG.debug("Duration of gap is not yet " +
2580
                      "configured. Setting to 300ms")
2581
            duration_gap = 30
2582

    
2583
        if duration_gap <= len(LIST_5TONE_DELAY):
2584
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2585
                                RadioSettingValueList(LIST_5TONE_DELAY,
2586
                                                      LIST_5TONE_DELAY[
2587
                                                          duration_gap]))
2588
            encode_2tone.append(line)
2589

    
2590
        def _2tone_validate(value):
2591
            if value == 0:
2592
                return 65535
2593
            if value == 65535:
2594
                return value
2595
            if not (300 <= value and value <= 3000):
2596
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2597
                raise InvalidValueError(msg)
2598
            return value
2599

    
2600
        def apply_2tone_freq(setting, obj):
2601
            val = int(setting.value)
2602
            if (val == 0) or (val == 65535):
2603
                obj.set_value(65535)
2604
            else:
2605
                obj.set_value(val)
2606

    
2607
        i = 1
2608
        for code in self._memobj._2tone._2tone_encode:
2609
            code_2tone = RadioSettingGroup("code_2tone_" + str(i),
2610
                                           "Encode Code " + str(i))
2611
            encode_2tone.append(code_2tone)
2612

    
2613
            tmp = code.freq1
2614
            if tmp == 65535:
2615
                tmp = 0
2616
            val1 = RadioSettingValueInteger(0, 65535, tmp)
2617
            freq1 = RadioSetting("2tone_code_" + str(i) + "_freq1",
2618
                                 "Frequency 1", val1)
2619
            val1.set_validate_callback(_2tone_validate)
2620
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
2621
            code_2tone.append(freq1)
2622

    
2623
            tmp = code.freq2
2624
            if tmp == 65535:
2625
                tmp = 0
2626
            val2 = RadioSettingValueInteger(0, 65535, tmp)
2627
            freq2 = RadioSetting("2tone_code_" + str(i) + "_freq2",
2628
                                 "Frequency 2", val2)
2629
            val2.set_validate_callback(_2tone_validate)
2630
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
2631
            code_2tone.append(freq2)
2632

    
2633
            i = i + 1
2634

    
2635
        decode_reset_time = _mem._2tone.reset_time
2636
        if decode_reset_time == 255:
2637
            decode_reset_time = 59
2638
            LOG.debug("Decode reset time unconfigured. resetting.")
2639
        if decode_reset_time <= len(LIST_5TONE_RESET):
2640
            list = RadioSettingValueList(
2641
                LIST_5TONE_RESET,
2642
                LIST_5TONE_RESET[
2643
                    decode_reset_time])
2644
            line = RadioSetting("_2tone.reset_time",
2645
                                "Decode reset time", list)
2646
            decode_2tone.append(line)
2647
        else:
2648
            LOG.debug("Invalid value decode reset time! Disabling.")
2649

    
2650
        def apply_2tone_freq_pair(setting, obj):
2651
            val = int(setting.value)
2652
            derived_val = 65535
2653
            frqname = str(setting._name[-5:])
2654
            derivedname = "derived_from_" + frqname
2655

    
2656
            if (val == 0):
2657
                val = 65535
2658
                derived_val = 65535
2659
            else:
2660
                derived_val = int(round(2304000.0/val))
2661

    
2662
            obj[frqname].set_value(val)
2663
            obj[derivedname].set_value(derived_val)
2664

    
2665
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " +
2666
                      derivedname + ": " + str(derived_val))
2667

    
2668
        i = 1
2669
        for decode_code in self._memobj._2tone._2tone_decode:
2670
            _2tone_dec_code = RadioSettingGroup("code_2tone_" + str(i),
2671
                                                "Decode Code " + str(i))
2672
            decode_2tone.append(_2tone_dec_code)
2673

    
2674
            j = 1
2675
            for dec in decode_code.decs:
2676
                val = dec.dec
2677
                if val == 255:
2678
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
2679
                              " is not yet configured. Setting to 0.")
2680
                    val = 0
2681

    
2682
                if val <= len(LIST_2TONE_DEC):
2683
                    line = RadioSetting(
2684
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
2685
                        "Dec " + str(j), RadioSettingValueList
2686
                        (LIST_2TONE_DEC,
2687
                         LIST_2TONE_DEC[val]))
2688
                    line.set_apply_callback(apply_list_value, dec.dec)
2689
                    _2tone_dec_code.append(line)
2690
                else:
2691
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
2692

    
2693
                val = dec.response
2694
                if val == 255:
2695
                    LOG.debug("Response for Code " + str(i) + " Dec " +
2696
                              str(j) + " is not yet configured. Setting to 0.")
2697
                    val = 0
2698

    
2699
                if val <= len(LIST_2TONE_RESPONSE):
2700
                    line = RadioSetting(
2701
                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
2702
                        "Response " + str(j), RadioSettingValueList
2703
                        (LIST_2TONE_RESPONSE,
2704
                         LIST_2TONE_RESPONSE[val]))
2705
                    line.set_apply_callback(apply_list_value, dec.response)
2706
                    _2tone_dec_code.append(line)
2707
                else:
2708
                    LOG.debug("Invalid value for 2tone response! Disabling.")
2709

    
2710
                val = dec.alert
2711
                if val == 255:
2712
                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +
2713
                              " is not yet configured. Setting to 0.")
2714
                    val = 0
2715

    
2716
                if val <= len(PTTIDCODE_LIST):
2717
                    line = RadioSetting(
2718
                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
2719
                        "Alert " + str(j), RadioSettingValueList
2720
                        (PTTIDCODE_LIST,
2721
                         PTTIDCODE_LIST[val]))
2722
                    line.set_apply_callback(apply_list_value, dec.alert)
2723
                    _2tone_dec_code.append(line)
2724
                else:
2725
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
2726
                j = j + 1
2727

    
2728
            freq = self._memobj._2tone.freqs[i-1]
2729
            for char in ['A', 'B', 'C', 'D']:
2730
                setting_name = "freq" + str(char)
2731

    
2732
                tmp = freq[setting_name]
2733
                if tmp == 65535:
2734
                    tmp = 0
2735
                if tmp != 0:
2736
                    expected = int(round(2304000.0/tmp))
2737
                    from_mem = freq["derived_from_" + setting_name]
2738
                    if expected != from_mem:
2739
                        LOG.error("Expected " + str(expected) +
2740
                                  " but read " + str(from_mem) +
2741
                                  ". Disabling 2Tone Decode Freqs!")
2742
                        break
2743
                val = RadioSettingValueInteger(0, 65535, tmp)
2744
                frq = RadioSetting("2tone_dec_" + str(i) + "_freq" + str(char),
2745
                                   ("Decode Frequency " + str(char)), val)
2746
                val.set_validate_callback(_2tone_validate)
2747
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
2748
                _2tone_dec_code.append(frq)
2749

    
2750
            i = i + 1
2751

    
2752
        return top
2753

    
2754
    def set_settings(self, settings):
2755
        _settings = self._memobj.settings
2756
        for element in settings:
2757
            if not isinstance(element, RadioSetting):
2758
                if element.get_name() == "fm_preset":
2759
                    self._set_fm_preset(element)
2760
                else:
2761
                    self.set_settings(element)
2762
                    continue
2763
            else:
2764
                try:
2765
                    name = element.get_name()
2766
                    if "." in name:
2767
                        bits = name.split(".")
2768
                        obj = self._memobj
2769
                        for bit in bits[:-1]:
2770
                            if "/" in bit:
2771
                                bit, index = bit.split("/", 1)
2772
                                index = int(index)
2773
                                obj = getattr(obj, bit)[index]
2774
                            else:
2775
                                obj = getattr(obj, bit)
2776
                        setting = bits[-1]
2777
                    else:
2778
                        obj = _settings
2779
                        setting = element.get_name()
2780

    
2781
                    if element.has_apply_callback():
2782
                        LOG.debug("Using apply callback")
2783
                        element.run_apply_callback()
2784
                    elif element.value.get_mutable():
2785
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2786
                        setattr(obj, setting, element.value)
2787
                except Exception, e:
2788
                    LOG.debug(element.get_name())
2789
                    raise
2790

    
2791
    @classmethod
2792
    def match_model(cls, filedata, filename):
2793
        match_size = False
2794
        match_model = False
2795

    
2796
        # testing the file data size
2797
        if len(filedata) == MEM_SIZE:
2798
            match_size = True
2799

    
2800
        # testing the firmware model fingerprint
2801
        match_model = model_match(cls, filedata)
2802

    
2803
        if match_size and match_model:
2804
            return True
2805
        else:
2806
            return False
2807

    
2808

    
2809
MEM_FORMAT = """
2810
#seekto 0x0000;
2811
struct {
2812
  lbcd rxfreq[4];
2813
  lbcd txfreq[4];
2814
  ul16 rxtone;
2815
  ul16 txtone;
2816
  u8 unknown0:4,
2817
     scode:4;
2818
  u8 unknown1:2,
2819
     spmute:1,
2820
     unknown2:3,
2821
     optsig:2;
2822
  u8 unknown3:3,
2823
     scramble:1,
2824
     unknown4:3,
2825
     power:1;
2826
  u8 unknown5:1,
2827
     wide:1,
2828
     unknown6:2,
2829
     bcl:1,
2830
     add:1,
2831
     pttid:2;
2832
} memory[200];
2833

    
2834
#seekto 0x0E00;
2835
struct {
2836
  u8 tdr;
2837
  u8 unknown1;
2838
  u8 sql;
2839
  u8 unknown2[2];
2840
  u8 tot;
2841
  u8 apo;           // BTech radios use this as the Auto Power Off time
2842
                    // other radios use this as pre-Time Out Alert
2843
  u8 unknown3;
2844
  u8 abr;
2845
  u8 beep;
2846
  u8 unknown4[4];
2847
  u8 dtmfst;
2848
  u8 unknown5[2];
2849
  u8 prisc;
2850
  u8 prich;
2851
  u8 screv;
2852
  u8 unknown6[2];
2853
  u8 pttid;
2854
  u8 pttlt;
2855
  u8 unknown7;
2856
  u8 emctp;
2857
  u8 emcch;
2858
  u8 ringt;
2859
  u8 unknown8;
2860
  u8 camdf;
2861
  u8 cbmdf;
2862
  u8 sync;          // BTech radios use this as the display sync setting
2863
                    // other radios use this as the auto keypad lock setting
2864
  u8 ponmsg;
2865
  u8 wtled;
2866
  u8 rxled;
2867
  u8 txled;
2868
  u8 unknown9[5];
2869
  u8 anil;
2870
  u8 reps;
2871
  u8 repm;
2872
  u8 tdrab;
2873
  u8 ste;
2874
  u8 rpste;
2875
  u8 rptdl;
2876
  u8 mgain;
2877
  u8 dtmfg;
2878
} settings;
2879

    
2880
#seekto 0x0E80;
2881
struct {
2882
  u8 unknown1;
2883
  u8 vfomr;
2884
  u8 keylock;
2885
  u8 unknown2;
2886
  u8 unknown3:4,
2887
     vfomren:1,
2888
     unknown4:1,
2889
     reseten:1,
2890
     menuen:1;
2891
  u8 unknown5[11];
2892
  u8 dispab;
2893
  u8 mrcha;
2894
  u8 mrchb;
2895
  u8 menu;
2896
} settings2;
2897

    
2898
#seekto 0x0EC0;
2899
struct {
2900
  char line1[6];
2901
  char line2[6];
2902
} poweron_msg;
2903

    
2904
struct settings_vfo {
2905
  u8 freq[8];
2906
  u8 offset[6];
2907
  u8 unknown2[2];
2908
  ul16 rxtone;
2909
  ul16 txtone;
2910
  u8 scode;
2911
  u8 spmute;
2912
  u8 optsig;
2913
  u8 scramble;
2914
  u8 wide;
2915
  u8 power;
2916
  u8 shiftd;
2917
  u8 step;
2918
  u8 unknown3[4];
2919
};
2920

    
2921
#seekto 0x0F00;
2922
struct {
2923
  struct settings_vfo a;
2924
  struct settings_vfo b;
2925
} vfo;
2926

    
2927
#seekto 0x1000;
2928
struct {
2929
  char name[6];
2930
  u8 unknown1[10];
2931
} names[200];
2932

    
2933
#seekto 0x2400;
2934
struct {
2935
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2936
  u8 group_tone;
2937
  u8 repeat_tone;
2938
  u8 unused[13];
2939
} _5tone_std_settings[15];
2940

    
2941
#seekto 0x2500;
2942
struct {
2943
  u8 frame1[5];
2944
  u8 frame2[5];
2945
  u8 frame3[5];
2946
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2947
} _5tone_codes[15];
2948

    
2949
#seekto 0x25F0;
2950
struct {
2951
  u8 _5tone_delay1; // * 10ms
2952
  u8 _5tone_delay2; // * 10ms
2953
  u8 _5tone_delay3; // * 10ms
2954
  u8 _5tone_first_digit_ext_length;
2955
  u8 unknown1;
2956
  u8 unknown2;
2957
  u8 unknown3;
2958
  u8 unknown4;
2959
  u8 decode_standard;
2960
  u8 unknown5:5,
2961
     _5tone_decode_call_frame3:1,
2962
     _5tone_decode_call_frame2:1,
2963
     _5tone_decode_call_frame1:1;
2964
  u8 unknown6:5,
2965
     _5tone_decode_disp_frame3:1,
2966
     _5tone_decode_disp_frame2:1,
2967
     _5tone_decode_disp_frame1:1;
2968
  u8 decode_reset_time; // * 100 + 100ms
2969
} _5tone_settings;
2970

    
2971
#seekto 0x2900;
2972
struct {
2973
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2974
} dtmf_codes[15];
2975

    
2976
#seekto 0x29F0;
2977
struct {
2978
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
2979
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
2980
  u8 unknown0[14];
2981
  u8 inspection[16];
2982
  u8 monitor[16];
2983
  u8 alarmcode[16];
2984
  u8 stun[16];
2985
  u8 kill[16];
2986
  u8 revive[16];
2987
  u8 unknown1[16];
2988
  u8 unknown2[16];
2989
  u8 unknown3[16];
2990
  u8 unknown4[16];
2991
  u8 unknown5[16];
2992
  u8 unknown6[16];
2993
  u8 unknown7[16];
2994
  u8 masterid[16];
2995
  u8 viceid[16];
2996
  u8 unused01:7,
2997
     mastervice:1;
2998
  u8 unused02:3,
2999
     mrevive:1,
3000
     mkill:1,
3001
     mstun:1,
3002
     mmonitor:1,
3003
     minspection:1;
3004
  u8 unused03:3,
3005
     vrevive:1,
3006
     vkill:1,
3007
     vstun:1,
3008
     vmonitor:1,
3009
     vinspection:1;
3010
  u8 unused04:6,
3011
     txdisable:1,
3012
     rxdisable:1;
3013
  u8 groupcode;
3014
  u8 spacecode;
3015
  u8 delayproctime; // * 100 + 100ms
3016
  u8 resettime;     // * 100 + 100ms
3017
} dtmf_settings;
3018

    
3019
#seekto 0x2D00;
3020
struct {
3021
  struct {
3022
    ul16 freq1;
3023
    u8 unused01[6];
3024
    ul16 freq2;
3025
    u8 unused02[6];
3026
  } _2tone_encode[15];
3027
  u8 duration_1st_tone; // *10ms
3028
  u8 duration_2nd_tone; // *10ms
3029
  u8 duration_gap;      // *10ms
3030
  u8 unused03[13];
3031
  struct {
3032
    struct {
3033
      u8 dec;      // one out of LIST_2TONE_DEC
3034
      u8 response; // one out of LIST_2TONE_RESPONSE
3035
      u8 alert;    // 1-16
3036
    } decs[4];
3037
    u8 unused04[4];
3038
  } _2tone_decode[15];
3039
  u8 unused05[16];
3040

    
3041
  struct {
3042
    ul16 freqA;
3043
    ul16 freqB;
3044
    ul16 freqC;
3045
    ul16 freqD;
3046
    // unknown what those values mean, but they are
3047
    // derived from configured frequencies
3048
    ul16 derived_from_freqA; // 2304000/freqA
3049
    ul16 derived_from_freqB; // 2304000/freqB
3050
    ul16 derived_from_freqC; // 2304000/freqC
3051
    ul16 derived_from_freqD; // 2304000/freqD
3052
  }freqs[15];
3053
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3054
} _2tone;
3055

    
3056
#seekto 0x3000;
3057
struct {
3058
  u8 freq[8];
3059
  char broadcast_station_name[6];
3060
  u8 unknown[2];
3061
} fm_radio_preset[16];
3062

    
3063
#seekto 0x3C90;
3064
struct {
3065
  u8 vhf_low[3];
3066
  u8 vhf_high[3];
3067
  u8 uhf_low[3];
3068
  u8 uhf_high[3];
3069
} ranges;
3070

    
3071
// the UV-2501+220 & KT8900R has different zones for storing ranges
3072

    
3073
#seekto 0x3CD0;
3074
struct {
3075
  u8 vhf_low[3];
3076
  u8 vhf_high[3];
3077
  u8 unknown1[4];
3078
  u8 unknown2[6];
3079
  u8 vhf2_low[3];
3080
  u8 vhf2_high[3];
3081
  u8 unknown3[4];
3082
  u8 unknown4[6];
3083
  u8 uhf_low[3];
3084
  u8 uhf_high[3];
3085
} ranges220;
3086

    
3087
#seekto 0x3F70;
3088
struct {
3089
  char fp[6];
3090
} fingerprint;
3091

    
3092
"""
3093

    
3094

    
3095
class BTech(BTechMobileCommon):
3096
    """BTECH's UV-5001 and alike radios"""
3097
    BANDS = 2
3098
    COLOR_LCD = False
3099
    NAME_LENGTH = 6
3100

    
3101
    def set_options(self):
3102
        """This is to read the options from the image and set it in the
3103
        environment, for now just the limits of the freqs in the VHF/UHF
3104
        ranges"""
3105

    
3106
        # setting the correct ranges for each radio type
3107
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3108
            # the model 2501+220 has a segment in 220
3109
            # and a different position in the memmap
3110
            # also the QYT KT8900R
3111
            ranges = self._memobj.ranges220
3112
        else:
3113
            ranges = self._memobj.ranges
3114

    
3115
        # the normal dual bands
3116
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3117
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3118

    
3119
        # DEBUG
3120
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3121
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3122

    
3123
        # 220Mhz radios case
3124
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3125
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3126
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3127
            self._220_range = vhf2
3128

    
3129
        # set the class with the real data
3130
        self._vhf_range = vhf
3131
        self._uhf_range = uhf
3132

    
3133
    def process_mmap(self):
3134
        """Process the mem map into the mem object"""
3135

    
3136
        # Get it
3137
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3138

    
3139
        # load specific parameters from the radio image
3140
        self.set_options()
3141

    
3142

    
3143
# Declaring Aliases (Clones of the real radios)
3144
class JT2705M(chirp_common.Alias):
3145
    VENDOR = "Jetstream"
3146
    MODEL = "JT2705M"
3147

    
3148

    
3149
class JT6188Mini(chirp_common.Alias):
3150
    VENDOR = "Juentai"
3151
    MODEL = "JT-6188 Mini"
3152

    
3153

    
3154
class JT6188Plus(chirp_common.Alias):
3155
    VENDOR = "Juentai"
3156
    MODEL = "JT-6188 Plus"
3157

    
3158

    
3159
class SSGT890(chirp_common.Alias):
3160
    VENDOR = "Sainsonic"
3161
    MODEL = "GT-890"
3162

    
3163

    
3164
class ZastoneMP300(chirp_common.Alias):
3165
    VENDOR = "Zastone"
3166
    MODEL = "MP-300"
3167

    
3168

    
3169
# real radios
3170
@directory.register
3171
class UV2501(BTech):
3172
    """Baofeng Tech UV2501"""
3173
    MODEL = "UV-2501"
3174
    _fileid = [UV2501G3_fp,
3175
               UV2501G2_fp,
3176
               UV2501pp2_fp,
3177
               UV2501pp_fp]
3178

    
3179

    
3180
@directory.register
3181
class UV2501_220(BTech):
3182
    """Baofeng Tech UV2501+220"""
3183
    MODEL = "UV-2501+220"
3184
    BANDS = 3
3185
    _magic = MSTRING_220
3186
    _id2 = [UV2501_220pp_id, ]
3187
    _fileid = [UV2501_220G3_fp,
3188
               UV2501_220G2_fp,
3189
               UV2501_220_fp,
3190
               UV2501_220pp_fp]
3191

    
3192

    
3193
@directory.register
3194
class UV5001(BTech):
3195
    """Baofeng Tech UV5001"""
3196
    MODEL = "UV-5001"
3197
    _fileid = [UV5001G3_fp,
3198
               UV5001G22_fp,
3199
               UV5001G2_fp,
3200
               UV5001alpha_fp,
3201
               UV5001pp_fp]
3202
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3203
                     chirp_common.PowerLevel("Low", watts=10)]
3204

    
3205

    
3206
@directory.register
3207
class MINI8900(BTech):
3208
    """WACCOM MINI-8900"""
3209
    VENDOR = "WACCOM"
3210
    MODEL = "MINI-8900"
3211
    _magic = MSTRING_MINI8900
3212
    _fileid = [MINI8900_fp, ]
3213
    # Clones
3214
    ALIASES = [JT6188Plus, ]
3215

    
3216

    
3217
@directory.register
3218
class KTUV980(BTech):
3219
    """QYT KT-UV980"""
3220
    VENDOR = "QYT"
3221
    MODEL = "KT-UV980"
3222
    _vhf_range = (136000000, 175000000)
3223
    _uhf_range = (400000000, 481000000)
3224
    _magic = MSTRING_MINI8900
3225
    _fileid = [KTUV980_fp, ]
3226
    # Clones
3227
    ALIASES = [JT2705M, ]
3228

    
3229
# Please note that there is a version of this radios that is a clone of the
3230
# Waccom Mini8900, maybe an early version?
3231

    
3232

    
3233
@directory.register
3234
class KT9800(BTech):
3235
    """QYT KT8900"""
3236
    VENDOR = "QYT"
3237
    MODEL = "KT8900"
3238
    _vhf_range = (136000000, 175000000)
3239
    _uhf_range = (400000000, 481000000)
3240
    _magic = MSTRING_KT8900
3241
    _fileid = [KT8900_fp,
3242
               KT8900_fp1,
3243
               KT8900_fp2,
3244
               KT8900_fp3,
3245
               KT8900_fp4,
3246
               KT8900_fp5]
3247
    _id2 = [KT8900_id, ]
3248
    # Clones
3249
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3250

    
3251

    
3252
@directory.register
3253
class KT9800R(BTech):
3254
    """QYT KT8900R"""
3255
    VENDOR = "QYT"
3256
    MODEL = "KT8900R"
3257
    BANDS = 3
3258
    _vhf_range = (136000000, 175000000)
3259
    _220_range = (240000000, 271000000)
3260
    _uhf_range = (400000000, 481000000)
3261
    _magic = MSTRING_KT8900R
3262
    _fileid = [KT8900R_fp,
3263
               KT8900R_fp1,
3264
               KT8900R_fp2,
3265
               KT8900R_fp3,
3266
               KT8900R_fp4]
3267
    _id2 = [KT8900R_id, KT8900R_id2]
3268

    
3269

    
3270
@directory.register
3271
class LT588UV(BTech):
3272
    """LUITON LT-588UV"""
3273
    VENDOR = "LUITON"
3274
    MODEL = "LT-588UV"
3275
    _vhf_range = (136000000, 175000000)
3276
    _uhf_range = (400000000, 481000000)
3277
    _magic = MSTRING_KT8900
3278
    _fileid = [LT588UV_fp,
3279
               LT588UV_fp1]
3280
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
3281
                     chirp_common.PowerLevel("Low", watts=10)]
3282

    
3283

    
3284
COLOR_MEM_FORMAT = """
3285
#seekto 0x0000;
3286
struct {
3287
  lbcd rxfreq[4];
3288
  lbcd txfreq[4];
3289
  ul16 rxtone;
3290
  ul16 txtone;
3291
  u8 unknown0:4,
3292
     scode:4;
3293
  u8 unknown1:2,
3294
     spmute:1,
3295
     unknown2:3,
3296
     optsig:2;
3297
  u8 unknown3:3,
3298
     scramble:1,
3299
     unknown4:3,
3300
     power:1;
3301
  u8 unknown5:1,
3302
     wide:1,
3303
     unknown6:2,
3304
     bcl:1,
3305
     add:1,
3306
     pttid:2;
3307
} memory[200];
3308

    
3309
#seekto 0x0E00;
3310
struct {
3311
  u8 tmr;
3312
  u8 unknown1;
3313
  u8 sql;
3314
  u8 unknown2[2];
3315
  u8 tot;
3316
  u8 apo;
3317
  u8 unknown3;
3318
  u8 abr;
3319
  u8 beep;
3320
  u8 unknown4[4];
3321
  u8 dtmfst;
3322
  u8 unknown5[2];
3323
  u8 screv;
3324
  u8 unknown6[2];
3325
  u8 pttid;
3326
  u8 pttlt;
3327
  u8 unknown7;
3328
  u8 emctp;
3329
  u8 emcch;
3330
  u8 sigbp;
3331
  u8 unknown8;
3332
  u8 camdf;
3333
  u8 cbmdf;
3334
  u8 ccmdf;
3335
  u8 cdmdf;
3336
  u8 langua;
3337
  u8 sync;          // BTech radios use this as the display sync
3338
                    // setting, other radios use this as the auto
3339
                    // keypad lock setting
3340
  u8 mainfc;
3341
  u8 mainbc;
3342
  u8 menufc;
3343
  u8 menubc;
3344
  u8 stafc;
3345
  u8 stabc;
3346
  u8 sigfc;
3347
  u8 sigbc;
3348
  u8 rxfc;
3349
  u8 txfc;
3350
  u8 txdisp;
3351
  u8 unknown9[5];
3352
  u8 anil;
3353
  u8 reps;
3354
  u8 repm;
3355
  u8 tmrmr;
3356
  u8 ste;
3357
  u8 rpste;
3358
  u8 rptdl;
3359
  u8 dtmfg;
3360
  u8 mgain;
3361
  u8 skiptx;
3362
  u8 scmode;
3363
} settings;
3364

    
3365
#seekto 0x0E80;
3366
struct {
3367
  u8 unknown1;
3368
  u8 vfomr;
3369
  u8 keylock;
3370
  u8 unknown2;
3371
  u8 unknown3:4,
3372
     vfomren:1,
3373
     unknown4:1,
3374
     reseten:1,
3375
     menuen:1;
3376
  u8 unknown5[11];
3377
  u8 dispab;
3378
  u8 unknown6[2];
3379
  u8 menu;
3380
  u8 unknown7[7];
3381
  u8 vfomra;
3382
  u8 vfomrb;
3383
  u8 vfomrc;
3384
  u8 vfomrd;
3385
  u8 mrcha;
3386
  u8 mrchb;
3387
  u8 mrchc;
3388
  u8 mrchd;
3389
} settings2;
3390

    
3391
struct settings_vfo {
3392
  u8 freq[8];
3393
  u8 offset[6];
3394
  u8 unknown2[2];
3395
  ul16 rxtone;
3396
  ul16 txtone;
3397
  u8 scode;
3398
  u8 spmute;
3399
  u8 optsig;
3400
  u8 scramble;
3401
  u8 wide;
3402
  u8 power;
3403
  u8 shiftd;
3404
  u8 step;
3405
  u8 unknown3[4];
3406
};
3407

    
3408
#seekto 0x0F00;
3409
struct {
3410
  struct settings_vfo a;
3411
  struct settings_vfo b;
3412
  struct settings_vfo c;
3413
  struct settings_vfo d;
3414
} vfo;
3415

    
3416
#seekto 0x0F80;
3417
struct {
3418
  char line1[8];
3419
  char line2[8];
3420
  char line3[8];
3421
  char line4[8];
3422
  char line5[8];
3423
  char line6[8];
3424
  char line7[8];
3425
  char line8[8];
3426
} poweron_msg;
3427

    
3428
#seekto 0x1000;
3429
struct {
3430
  char name[8];
3431
  u8 unknown1[8];
3432
} names[200];
3433

    
3434
#seekto 0x2400;
3435
struct {
3436
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3437
  u8 group_tone;
3438
  u8 repeat_tone;
3439
  u8 unused[13];
3440
} _5tone_std_settings[15];
3441

    
3442
#seekto 0x2500;
3443
struct {
3444
  u8 frame1[5];
3445
  u8 frame2[5];
3446
  u8 frame3[5];
3447
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3448
} _5tone_codes[15];
3449

    
3450
#seekto 0x25F0;
3451
struct {
3452
  u8 _5tone_delay1; // * 10ms
3453
  u8 _5tone_delay2; // * 10ms
3454
  u8 _5tone_delay3; // * 10ms
3455
  u8 _5tone_first_digit_ext_length;
3456
  u8 unknown1;
3457
  u8 unknown2;
3458
  u8 unknown3;
3459
  u8 unknown4;
3460
  u8 decode_standard;
3461
  u8 unknown5:5,
3462
     _5tone_decode_call_frame3:1,
3463
     _5tone_decode_call_frame2:1,
3464
     _5tone_decode_call_frame1:1;
3465
  u8 unknown6:5,
3466
     _5tone_decode_disp_frame3:1,
3467
     _5tone_decode_disp_frame2:1,
3468
     _5tone_decode_disp_frame1:1;
3469
  u8 decode_reset_time; // * 100 + 100ms
3470
} _5tone_settings;
3471

    
3472
#seekto 0x2900;
3473
struct {
3474
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3475
} dtmf_codes[15];
3476

    
3477
#seekto 0x29F0;
3478
struct {
3479
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3480
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3481
  u8 unknown0[14];
3482
  u8 inspection[16];
3483
  u8 monitor[16];
3484
  u8 alarmcode[16];
3485
  u8 stun[16];
3486
  u8 kill[16];
3487
  u8 revive[16];
3488
  u8 unknown1[16];
3489
  u8 unknown2[16];
3490
  u8 unknown3[16];
3491
  u8 unknown4[16];
3492
  u8 unknown5[16];
3493
  u8 unknown6[16];
3494
  u8 unknown7[16];
3495
  u8 masterid[16];
3496
  u8 viceid[16];
3497
  u8 unused01:7,
3498
     mastervice:1;
3499
  u8 unused02:3,
3500
     mrevive:1,
3501
     mkill:1,
3502
     mstun:1,
3503
     mmonitor:1,
3504
     minspection:1;
3505
  u8 unused03:3,
3506
     vrevive:1,
3507
     vkill:1,
3508
     vstun:1,
3509
     vmonitor:1,
3510
     vinspection:1;
3511
  u8 unused04:6,
3512
     txdisable:1,
3513
     rxdisable:1;
3514
  u8 groupcode;
3515
  u8 spacecode;
3516
  u8 delayproctime; // * 100 + 100ms
3517
  u8 resettime;     // * 100 + 100ms
3518
} dtmf_settings;
3519

    
3520
#seekto 0x2D00;
3521
struct {
3522
  struct {
3523
    ul16 freq1;
3524
    u8 unused01[6];
3525
    ul16 freq2;
3526
    u8 unused02[6];
3527
  } _2tone_encode[15];
3528
  u8 duration_1st_tone; // *10ms
3529
  u8 duration_2nd_tone; // *10ms
3530
  u8 duration_gap;      // *10ms
3531
  u8 unused03[13];
3532
  struct {
3533
    struct {
3534
      u8 dec;      // one out of LIST_2TONE_DEC
3535
      u8 response; // one out of LIST_2TONE_RESPONSE
3536
      u8 alert;    // 1-16
3537
    } decs[4];
3538
    u8 unused04[4];
3539
  } _2tone_decode[15];
3540
  u8 unused05[16];
3541

    
3542
  struct {
3543
    ul16 freqA;
3544
    ul16 freqB;
3545
    ul16 freqC;
3546
    ul16 freqD;
3547
    // unknown what those values mean, but they are
3548
    // derived from configured frequencies
3549
    ul16 derived_from_freqA; // 2304000/freqA
3550
    ul16 derived_from_freqB; // 2304000/freqB
3551
    ul16 derived_from_freqC; // 2304000/freqC
3552
    ul16 derived_from_freqD; // 2304000/freqD
3553
  }freqs[15];
3554
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3555
} _2tone;
3556

    
3557
#seekto 0x3D80;
3558
struct {
3559
  u8 vhf_low[3];
3560
  u8 vhf_high[3];
3561
  u8 unknown1[4];
3562
  u8 unknown2[6];
3563
  u8 vhf2_low[3];
3564
  u8 vhf2_high[3];
3565
  u8 unknown3[4];
3566
  u8 unknown4[6];
3567
  u8 uhf_low[3];
3568
  u8 uhf_high[3];
3569
  u8 unknown5[4];
3570
  u8 unknown6[6];
3571
  u8 uhf2_low[3];
3572
  u8 uhf2_high[3];
3573
} ranges;
3574

    
3575
#seekto 0x3F70;
3576
struct {
3577
  char fp[6];
3578
} fingerprint;
3579

    
3580
"""
3581

    
3582

    
3583
class BTechColor(BTechMobileCommon):
3584
    """BTECH's Color LCD Mobile and alike radios"""
3585
    COLOR_LCD = True
3586
    NAME_LENGTH = 8
3587

    
3588
    def process_mmap(self):
3589
        """Process the mem map into the mem object"""
3590

    
3591
        # Get it
3592
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3593

    
3594
        # load specific parameters from the radio image
3595
        self.set_options()
3596

    
3597
    def set_options(self):
3598
        """This is to read the options from the image and set it in the
3599
        environment, for now just the limits of the freqs in the VHF/UHF
3600
        ranges"""
3601

    
3602
        # setting the correct ranges for each radio type
3603
        ranges = self._memobj.ranges
3604

    
3605
        # the normal dual bands
3606
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3607
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3608

    
3609
        # DEBUG
3610
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3611
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3612

    
3613
        # the additional bands
3614
        if self.MODEL in ["UV-25X4", "KT7900D"]:
3615
            # 200Mhz band
3616
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3617
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3618
            self._220_range = vhf2
3619

    
3620
            # 350Mhz band
3621
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
3622
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
3623
            self._350_range = uhf2
3624

    
3625
        # set the class with the real data
3626
        self._vhf_range = vhf
3627
        self._uhf_range = uhf
3628

    
3629

    
3630
# Declaring Aliases (Clones of the real radios)
3631
class SKT8900D(chirp_common.Alias):
3632
    VENDOR = "Surecom"
3633
    MODEL = "S-KT8900D"
3634

    
3635

    
3636
class QB25(chirp_common.Alias):
3637
    VENDOR = "Radioddity"
3638
    MODEL = "QB25"
3639

    
3640

    
3641
# real radios
3642
@directory.register
3643
class UV25X2(BTechColor):
3644
    """Baofeng Tech UV25X2"""
3645
    MODEL = "UV-25X2"
3646
    BANDS = 2
3647
    _vhf_range = (130000000, 180000000)
3648
    _uhf_range = (400000000, 521000000)
3649
    _magic = MSTRING_UV25X2
3650
    _fileid = [UV25X2_fp, ]
3651

    
3652

    
3653
@directory.register
3654
class UV25X4(BTechColor):
3655
    """Baofeng Tech UV25X4"""
3656
    MODEL = "UV-25X4"
3657
    BANDS = 4
3658
    _vhf_range = (130000000, 180000000)
3659
    _220_range = (200000000, 271000000)
3660
    _uhf_range = (400000000, 521000000)
3661
    _350_range = (350000000, 391000000)
3662
    _magic = MSTRING_UV25X4
3663
    _fileid = [UV25X4_fp, ]
3664

    
3665

    
3666
@directory.register
3667
class UV50X2(BTechColor):
3668
    """Baofeng Tech UV50X2"""
3669
    MODEL = "UV-50X2"
3670
    BANDS = 2
3671
    _vhf_range = (130000000, 180000000)
3672
    _uhf_range = (400000000, 521000000)
3673
    _magic = MSTRING_UV25X2
3674
    _fileid = [UV50X2_fp, ]
3675
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3676
                     chirp_common.PowerLevel("Low", watts=10)]
3677

    
3678

    
3679
@directory.register
3680
class KT7900D(BTechColor):
3681
    """QYT KT7900D"""
3682
    VENDOR = "QYT"
3683
    MODEL = "KT7900D"
3684
    BANDS = 4
3685
    _vhf_range = (136000000, 175000000)
3686
    _220_range = (200000000, 271000000)
3687
    _uhf_range = (400000000, 481000000)
3688
    _350_range = (350000000, 371000000)
3689
    _magic = MSTRING_KT8900D
3690
    _fileid = [KT7900D_fp, KT7900D_fp1, QB25_fp, ]
3691
    # Clones
3692
    ALIASES = [SKT8900D, QB25, ]
3693

    
3694

    
3695
@directory.register
3696
class KT8900D(BTechColor):
3697
    """QYT KT8900D"""
3698
    VENDOR = "QYT"
3699
    MODEL = "KT8900D"
3700
    BANDS = 2
3701
    _vhf_range = (136000000, 175000000)
3702
    _uhf_range = (400000000, 481000000)
3703
    _magic = MSTRING_KT8900D
3704
    _fileid = [KT8900D_fp, KT8900D_fp1]
    (1-1/1)