Project

General

Profile

Bug #6265 » btech.py

Pavel Milanes, 12/29/2018 10:00 PM

 
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
LOG = logging.getLogger(__name__)
23

    
24
from time import sleep
25
from chirp import chirp_common, directory, memmap
26
from chirp import bitwise, errors, util
27
from chirp.settings import RadioSettingGroup, RadioSetting, \
28
    RadioSettingValueBoolean, RadioSettingValueList, \
29
    RadioSettingValueString, RadioSettingValueInteger, \
30
    RadioSettingValueFloat, RadioSettings, InvalidValueError
31
from textwrap import dedent
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 not ack 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.memory_bounds = (0, self._upper)
741

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

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

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

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

    
757
        return rf
758

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

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

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

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

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

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

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

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

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

    
830
        # Memory number
831
        mem.number = number
832

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

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

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

    
863
        # power
864
        mem.power = POWER_LEVELS[int(_mem.power)]
865

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

    
869
        # skip
870
        mem.skip = SKIP_VALUES[_mem.add]
871

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

    
878
        # Extra
879
        mem.extra = RadioSettingGroup("extra", "Extra")
880

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

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

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

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

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

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

    
917
        return mem
918

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

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

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

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

    
946
        # frequency
947
        _mem.rxfreq = mem.freq / 10
948

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

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

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

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

    
977
        # wide/narrow
978
        _mem.wide = MODES.index(mem.mode)
979

    
980
        # scan add property
981
        _mem.add = SKIP_VALUES.index(mem.skip)
982

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

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

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

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

    
1024
        return mem
1025

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

    
1035
        # Basic
1036
        if self.COLOR_LCD:
1037
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1038
                               RadioSettingValueList(
1039
                                   LIST_TMR,
1040
                                   LIST_TMR[_mem.settings.tmr]))
1041
            basic.append(tmr)
1042
        else:
1043
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1044
                               RadioSettingValueBoolean(_mem.settings.tdr))
1045
            basic.append(tdr)
1046

    
1047
        sql = RadioSetting("settings.sql", "Squelch level",
1048
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1049
        basic.append(sql)
1050

    
1051
        tot = RadioSetting("settings.tot", "Time out timer",
1052
                           RadioSettingValueList(
1053
                               LIST_TOT,
1054
                               LIST_TOT[_mem.settings.tot]))
1055
        basic.append(tot)
1056

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

    
1070
        abr = RadioSetting("settings.abr", "Backlight timer",
1071
                           RadioSettingValueList(
1072
                               LIST_OFF1TO50,
1073
                               LIST_OFF1TO50[_mem.settings.abr]))
1074
        basic.append(abr)
1075

    
1076
        beep = RadioSetting("settings.beep", "Key beep",
1077
                            RadioSettingValueBoolean(_mem.settings.beep))
1078
        basic.append(beep)
1079

    
1080
        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1081
                              RadioSettingValueList(
1082
                                  LIST_DTMFST,
1083
                                  LIST_DTMFST[_mem.settings.dtmfst]))
1084
        basic.append(dtmfst)
1085

    
1086
        if not self.COLOR_LCD:
1087
            prisc = RadioSetting("settings.prisc", "Priority scan",
1088
                                 RadioSettingValueBoolean(
1089
                                     _mem.settings.prisc))
1090
            basic.append(prisc)
1091

    
1092
            prich = RadioSetting("settings.prich", "Priority channel",
1093
                                 RadioSettingValueInteger(0, 199,
1094
                                     _mem.settings.prich))
1095
            basic.append(prich)
1096

    
1097
        screv = RadioSetting("settings.screv", "Scan resume method",
1098
                             RadioSettingValueList(
1099
                                 LIST_SCREV,
1100
                                 LIST_SCREV[_mem.settings.screv]))
1101
        basic.append(screv)
1102

    
1103
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1104
                             RadioSettingValueInteger(0, 30,
1105
                                 _mem.settings.pttlt))
1106
        basic.append(pttlt)
1107

    
1108
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1109
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1110
                                 RadioSettingValueList(
1111
                                     LIST_EMCTPX,
1112
                                     LIST_EMCTPX[_mem.settings.emctp]))
1113
            basic.append(emctp)
1114
        else:
1115
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1116
                                 RadioSettingValueList(
1117
                                     LIST_EMCTP,
1118
                                     LIST_EMCTP[_mem.settings.emctp]))
1119
            basic.append(emctp)
1120

    
1121
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1122
                             RadioSettingValueInteger(0, 199,
1123
                                 _mem.settings.emcch))
1124
        basic.append(emcch)
1125

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

    
1141
        camdf = RadioSetting("settings.camdf", "Display mode A",
1142
                             RadioSettingValueList(
1143
                                 LIST_MDF,
1144
                                 LIST_MDF[_mem.settings.camdf]))
1145
        basic.append(camdf)
1146

    
1147
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1148
                             RadioSettingValueList(
1149
                                 LIST_MDF,
1150
                                 LIST_MDF[_mem.settings.cbmdf]))
1151
        basic.append(cbmdf)
1152

    
1153
        if self.COLOR_LCD:
1154
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1155
                                 RadioSettingValueList(
1156
                                     LIST_MDF,
1157
                                     LIST_MDF[_mem.settings.ccmdf]))
1158
            basic.append(ccmdf)
1159

    
1160
            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1161
                                 RadioSettingValueList(
1162
                                     LIST_MDF,
1163
                                     LIST_MDF[_mem.settings.cdmdf]))
1164
            basic.append(cdmdf)
1165

    
1166
            langua = RadioSetting("settings.langua", "Language",
1167
                                  RadioSettingValueList(
1168
                                      LIST_LANGUA,
1169
                                      LIST_LANGUA[_mem.settings.langua]))
1170
            basic.append(langua)
1171

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

    
1190
        if not self.COLOR_LCD:
1191
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1192
                                  RadioSettingValueList(
1193
                                      LIST_PONMSG,
1194
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1195
            basic.append(ponmsg)
1196

    
1197
        if self.COLOR_LCD:
1198
            mainfc = RadioSetting("settings.mainfc",
1199
                                  "Main LCD foreground color",
1200
                                      RadioSettingValueList(
1201
                                          LIST_COLOR8,
1202
                                          LIST_COLOR8[_mem.settings.mainfc]))
1203
            basic.append(mainfc)
1204

    
1205
            mainbc = RadioSetting("settings.mainbc",
1206
                                  "Main LCD background color",
1207
                                      RadioSettingValueList(
1208
                                          LIST_COLOR8,
1209
                                          LIST_COLOR8[_mem.settings.mainbc]))
1210
            basic.append(mainbc)
1211

    
1212
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1213
                                  RadioSettingValueList(
1214
                                      LIST_COLOR8,
1215
                                      LIST_COLOR8[_mem.settings.menufc]))
1216
            basic.append(menufc)
1217

    
1218
            menubc = RadioSetting("settings.menubc", "Menu background color",
1219
                                  RadioSettingValueList(
1220
                                      LIST_COLOR8,
1221
                                      LIST_COLOR8[_mem.settings.menubc]))
1222
            basic.append(menubc)
1223

    
1224
            stafc = RadioSetting("settings.stafc",
1225
                                 "Top status foreground color",
1226
                                     RadioSettingValueList(
1227
                                         LIST_COLOR8,
1228
                                         LIST_COLOR8[_mem.settings.stafc]))
1229
            basic.append(stafc)
1230

    
1231
            stabc = RadioSetting("settings.stabc",
1232
                                 "Top status background color",
1233
                                     RadioSettingValueList(
1234
                                         LIST_COLOR8,
1235
                                         LIST_COLOR8[_mem.settings.stabc]))
1236
            basic.append(stabc)
1237

    
1238
            sigfc = RadioSetting("settings.sigfc",
1239
                                 "Bottom status foreground color",
1240
                                     RadioSettingValueList(
1241
                                         LIST_COLOR8,
1242
                                         LIST_COLOR8[_mem.settings.sigfc]))
1243
            basic.append(sigfc)
1244

    
1245
            sigbc = RadioSetting("settings.sigbc",
1246
                                 "Bottom status background color",
1247
                                     RadioSettingValueList(
1248
                                         LIST_COLOR8,
1249
                                         LIST_COLOR8[_mem.settings.sigbc]))
1250
            basic.append(sigbc)
1251

    
1252
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1253
                                RadioSettingValueList(
1254
                                    LIST_COLOR8,
1255
                                    LIST_COLOR8[_mem.settings.rxfc]))
1256
            basic.append(rxfc)
1257

    
1258
            txfc = RadioSetting("settings.txfc",
1259
                                "Transmitting character color",
1260
                                    RadioSettingValueList(
1261
                                        LIST_COLOR8,
1262
                                        LIST_COLOR8[_mem.settings.txfc]))
1263
            basic.append(txfc)
1264

    
1265
            txdisp = RadioSetting("settings.txdisp",
1266
                                  "Transmitting status display",
1267
                                      RadioSettingValueList(
1268
                                          LIST_TXDISP,
1269
                                          LIST_TXDISP[_mem.settings.txdisp]))
1270
            basic.append(txdisp)
1271
        else:
1272
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1273
                                 RadioSettingValueList(
1274
                                     LIST_COLOR4,
1275
                                     LIST_COLOR4[_mem.settings.wtled]))
1276
            basic.append(wtled)
1277

    
1278
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1279
                                 RadioSettingValueList(
1280
                                     LIST_COLOR4,
1281
                                     LIST_COLOR4[_mem.settings.rxled]))
1282
            basic.append(rxled)
1283

    
1284
            txled = RadioSetting("settings.txled", "TX backlight Color",
1285
                                 RadioSettingValueList(
1286
                                     LIST_COLOR4,
1287
                                     LIST_COLOR4[_mem.settings.txled]))
1288
            basic.append(txled)
1289

    
1290
        anil = RadioSetting("settings.anil", "ANI length",
1291
                            RadioSettingValueList(
1292
                                LIST_ANIL,
1293
                                LIST_ANIL[_mem.settings.anil]))
1294
        basic.append(anil)
1295

    
1296
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1297
                            RadioSettingValueList(
1298
                                LIST_REPS,
1299
                                LIST_REPS[_mem.settings.reps]))
1300
        basic.append(reps)
1301

    
1302
        repm = RadioSetting("settings.repm", "Relay condition",
1303
                            RadioSettingValueList(
1304
                                LIST_REPM,
1305
                                LIST_REPM[_mem.settings.repm]))
1306
        basic.append(repm)
1307

    
1308
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1309
            if self.COLOR_LCD:
1310
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1311
                                     RadioSettingValueList(
1312
                                         LIST_OFF1TO50,
1313
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1314
                basic.append(tmrmr)
1315
            else:
1316
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1317
                                     RadioSettingValueList(
1318
                                         LIST_OFF1TO50,
1319
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1320
                basic.append(tdrab)
1321

    
1322
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1323
                               RadioSettingValueBoolean(_mem.settings.ste))
1324
            basic.append(ste)
1325

    
1326
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1327
                                 RadioSettingValueList(
1328
                                     LIST_OFF1TO9,
1329
                                     LIST_OFF1TO9[_mem.settings.rpste]))
1330
            basic.append(rpste)
1331

    
1332
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1333
                                 RadioSettingValueList(
1334
                                     LIST_RPTDL,
1335
                                     LIST_RPTDL[_mem.settings.rptdl]))
1336
            basic.append(rptdl)
1337

    
1338
        if str(_mem.fingerprint.fp) in BTECH3:
1339
            mgain = RadioSetting("settings.mgain", "Mic gain",
1340
                                 RadioSettingValueInteger(0, 120,
1341
                                     _mem.settings.mgain))
1342
            basic.append(mgain)
1343

    
1344
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1345
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1346
                                 RadioSettingValueInteger(0, 60,
1347
                                     _mem.settings.dtmfg))
1348
            basic.append(dtmfg)
1349

    
1350
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1351
            mgain = RadioSetting("settings.mgain", "Mic gain",
1352
                                 RadioSettingValueInteger(0, 120,
1353
                                     _mem.settings.mgain))
1354
            basic.append(mgain)
1355

    
1356
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1357
                                  RadioSettingValueList(
1358
                                      LIST_SKIPTX,
1359
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1360
            basic.append(skiptx)
1361

    
1362
            scmode = RadioSetting("settings.scmode", "Scan mode",
1363
                                  RadioSettingValueList(
1364
                                      LIST_SCMODE,
1365
                                      LIST_SCMODE[_mem.settings.scmode]))
1366
            basic.append(scmode)
1367

    
1368
        # Advanced
1369
        def _filter(name):
1370
            filtered = ""
1371
            for char in str(name):
1372
                if char in VALID_CHARS:
1373
                    filtered += char
1374
                else:
1375
                    filtered += " "
1376
            return filtered
1377

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

    
1431
        if self.MODEL in ("UV-2501", "UV-5001"):
1432
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1433
                                   RadioSettingValueBoolean(
1434
                                       _mem.settings2.vfomren))
1435
            advanced.append(vfomren)
1436

    
1437
            reseten = RadioSetting("settings2.reseten", "RESET",
1438
                                   RadioSettingValueBoolean(
1439
                                       _mem.settings2.reseten))
1440
            advanced.append(reseten)
1441

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

    
1447
        # Other
1448
        def convert_bytes_to_limit(bytes):
1449
            limit = ""
1450
            for byte in bytes:
1451
                if byte < 10:
1452
                    limit += chr(byte + 0x30)
1453
                else:
1454
                    break
1455
            return limit
1456

    
1457
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1458
            _ranges = self._memobj.ranges220
1459
            ranges = "ranges220"
1460
        else:
1461
            _ranges = self._memobj.ranges
1462
            ranges = "ranges"
1463

    
1464
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1465
        val = RadioSettingValueString(0, 3, _limit)
1466
        val.set_mutable(False)
1467
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1468
        other.append(vhf_low)
1469

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

    
1476
        if self.BANDS == 3 or self.BANDS == 4:
1477
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1478
            val = RadioSettingValueString(0, 3, _limit)
1479
            val.set_mutable(False)
1480
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1481
            other.append(vhf2_low)
1482

    
1483
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1484
            val = RadioSettingValueString(0, 3, _limit)
1485
            val.set_mutable(False)
1486
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1487
            other.append(vhf2_high)
1488

    
1489
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1490
        val = RadioSettingValueString(0, 3, _limit)
1491
        val.set_mutable(False)
1492
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1493
        other.append(uhf_low)
1494

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

    
1501
        if self.BANDS == 4:
1502
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1503
            val = RadioSettingValueString(0, 3, _limit)
1504
            val.set_mutable(False)
1505
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1506
            other.append(uhf2_low)
1507

    
1508
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1509
            val = RadioSettingValueString(0, 3, _limit)
1510
            val.set_mutable(False)
1511
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1512
            other.append(uhf2_high)
1513

    
1514
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1515
        val.set_mutable(False)
1516
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1517
        other.append(fp)
1518

    
1519

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

    
1534
        if self.COLOR_LCD:
1535
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1536
                                  RadioSettingValueList(
1537
                                      LIST_VFOMR,
1538
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1539
            work.append(vfomra)
1540

    
1541
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1542
                                  RadioSettingValueList(
1543
                                      LIST_VFOMR,
1544
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1545
            work.append(vfomrb)
1546

    
1547
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1548
                                  RadioSettingValueList(
1549
                                      LIST_VFOMR,
1550
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1551
            work.append(vfomrc)
1552

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

    
1565

    
1566
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1567
                           RadioSettingValueBoolean(_mem.settings2.keylock))
1568
        work.append(keylock)
1569

    
1570
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1571
                             RadioSettingValueInteger(0, 199,
1572
                                 _mem.settings2.mrcha))
1573
        work.append(mrcha)
1574

    
1575
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1576
                             RadioSettingValueInteger(0, 199,
1577
                                 _mem.settings2.mrchb))
1578
        work.append(mrchb)
1579

    
1580
        if self.COLOR_LCD:
1581
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1582
                                 RadioSettingValueInteger(0, 199,
1583
                                     _mem.settings2.mrchc))
1584
            work.append(mrchc)
1585

    
1586
            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1587
                                 RadioSettingValueInteger(0, 199,
1588
                                     _mem.settings2.mrchd))
1589
            work.append(mrchd)
1590

    
1591
        def convert_bytes_to_freq(bytes):
1592
            real_freq = 0
1593
            for byte in bytes:
1594
                real_freq = (real_freq * 10) + byte
1595
            return chirp_common.format_freq(real_freq * 10)
1596

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

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

    
1640
        def apply_freq(setting, obj):
1641
            value = chirp_common.parse_freq(str(setting.value)) / 10
1642
            for i in range(7, -1, -1):
1643
                obj.freq[i] = value % 10
1644
                value /= 10
1645

    
1646
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1647
                                        _mem.vfo.a.freq))
1648
        val1a.set_validate_callback(my_validate)
1649
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1650
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1651
        work.append(vfoafreq)
1652

    
1653
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1654
                                        _mem.vfo.b.freq))
1655
        val1b.set_validate_callback(my_validate)
1656
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1657
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1658
        work.append(vfobfreq)
1659

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

    
1668
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1669
                                            _mem.vfo.d.freq))
1670
            val1d.set_validate_callback(my_validate)
1671
            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
1672
            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
1673
            work.append(vfodfreq)
1674

    
1675
        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1676
                                  RadioSettingValueList(
1677
                                      LIST_SHIFT,
1678
                                      LIST_SHIFT[_mem.vfo.a.shiftd]))
1679
        work.append(vfoashiftd)
1680

    
1681
        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1682
                                  RadioSettingValueList(
1683
                                      LIST_SHIFT,
1684
                                      LIST_SHIFT[_mem.vfo.b.shiftd]))
1685
        work.append(vfobshiftd)
1686

    
1687
        if self.COLOR_LCD:
1688
            vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
1689
                                      RadioSettingValueList(
1690
                                          LIST_SHIFT,
1691
                                          LIST_SHIFT[_mem.vfo.c.shiftd]))
1692
            work.append(vfocshiftd)
1693

    
1694
            vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
1695
                                      RadioSettingValueList(
1696
                                          LIST_SHIFT,
1697
                                          LIST_SHIFT[_mem.vfo.d.shiftd]))
1698
            work.append(vfodshiftd)
1699

    
1700
        def convert_bytes_to_offset(bytes):
1701
            real_offset = 0
1702
            for byte in bytes:
1703
                real_offset = (real_offset * 10) + byte
1704
            return chirp_common.format_freq(real_offset * 1000)
1705

    
1706
        def apply_offset(setting, obj):
1707
            value = chirp_common.parse_freq(str(setting.value)) / 1000
1708
            for i in range(5, -1, -1):
1709
                obj.offset[i] = value % 10
1710
                value /= 10
1711

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

    
1720
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1721
                                            _mem.vfo.b.offset))
1722
            vfoboffset = RadioSetting("vfo.b.offset",
1723
                                      "VFO B offset (0.000-999.999)", val1b)
1724
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1725
            work.append(vfoboffset)
1726

    
1727
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1728
                                            _mem.vfo.c.offset))
1729
            vfocoffset = RadioSetting("vfo.c.offset",
1730
                                      "VFO C offset (0.000-999.999)", val1c)
1731
            vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
1732
            work.append(vfocoffset)
1733

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

    
1748
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1749
                                            _mem.vfo.b.offset))
1750
            vfoboffset = RadioSetting("vfo.b.offset",
1751
                                      "VFO B offset (0.000-99.999)", val1b)
1752
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1753
            work.append(vfoboffset)
1754

    
1755

    
1756
        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
1757
                               RadioSettingValueList(
1758
                                   LIST_TXP,
1759
                                   LIST_TXP[_mem.vfo.a.power]))
1760
        work.append(vfoatxp)
1761

    
1762
        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
1763
                               RadioSettingValueList(
1764
                                   LIST_TXP,
1765
                                   LIST_TXP[_mem.vfo.b.power]))
1766
        work.append(vfobtxp)
1767

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

    
1775
            vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
1776
                                   RadioSettingValueList(
1777
                                       LIST_TXP,
1778
                                       LIST_TXP[_mem.vfo.d.power]))
1779
            work.append(vfodtxp)
1780

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

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

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

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

    
1806
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
1807
                                RadioSettingValueList(
1808
                                    LIST_STEP,
1809
                                    LIST_STEP[_mem.vfo.a.step]))
1810
        work.append(vfoastep)
1811

    
1812
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
1813
                                RadioSettingValueList(
1814
                                    LIST_STEP,
1815
                                    LIST_STEP[_mem.vfo.b.step]))
1816
        work.append(vfobstep)
1817

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

    
1825
            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
1826
                                    RadioSettingValueList(
1827
                                        LIST_STEP,
1828
                                        LIST_STEP[_mem.vfo.d.step]))
1829
            work.append(vfodstep)
1830

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

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

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

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

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

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

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

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

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

    
1888
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
1889
                                   RadioSettingValueBoolean(
1890
                                       _mem.vfo.b.scramble))
1891
            work.append(vfobscr)
1892

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

    
1899
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
1900
                                   RadioSettingValueBoolean(
1901
                                       _mem.vfo.d.scramble))
1902
            work.append(vfodscr)
1903

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

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

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

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

    
1929
        pttid = RadioSetting("settings.pttid", "PTT ID",
1930
                             RadioSettingValueList(
1931
                                 PTTID_LIST,
1932
                                 PTTID_LIST[_mem.settings.pttid]))
1933
        work.append(pttid)
1934

    
1935
        if not self.COLOR_LCD:
1936
            #FM presets
1937
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
1938
            top.append(fm_presets)
1939

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

    
1948
            def apply_fm_preset_name(setting, obj):
1949
                valstring = str (setting.value)
1950
                for i in range(0,6):
1951
                    if valstring[i] in VALID_CHARS:
1952
                        obj[i] = valstring[i]
1953
                    else:
1954
                        obj[i] = '0xff'
1955

    
1956
            def apply_fm_freq(setting, obj):
1957
                value = chirp_common.parse_freq(str(setting.value)) / 10
1958
                for i in range(7, -1, -1):
1959
                    obj.freq[i] = value % 10
1960
                    value /= 10
1961

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

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

    
1982
                i = i + 1
1983

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

    
1997
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
1998
                                 "RX-Disable",
1999
                                 RadioSettingValueBoolean(
2000
                                     _mem.dtmf_settings.rxdisable))
2001
        dtmf_enc_settings.append(rxdisable)
2002

    
2003
        dtmfspeed_on = RadioSetting(
2004
            "dtmf_settings.dtmfspeed_on",
2005
            "DTMF Speed (On Time)",
2006
            RadioSettingValueList(LIST_DTMF_SPEED,
2007
                                  LIST_DTMF_SPEED[
2008
                                      _mem.dtmf_settings.dtmfspeed_on]))
2009
        dtmf_enc_settings.append(dtmfspeed_on)
2010

    
2011
        dtmfspeed_off = RadioSetting(
2012
            "dtmf_settings.dtmfspeed_off",
2013
            "DTMF Speed (Off Time)",
2014
            RadioSettingValueList(LIST_DTMF_SPEED,
2015
                                  LIST_DTMF_SPEED[
2016
                                      _mem.dtmf_settings.dtmfspeed_off]))
2017
        dtmf_enc_settings.append(dtmfspeed_off)
2018

    
2019
        def memory2string(dmtf_mem):
2020
            dtmf_string = ""
2021
            for digit in dmtf_mem:
2022
                if digit != 255:
2023
                    index = LIST_DTMF_VALUES.index(digit)
2024
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2025
            return dtmf_string
2026

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

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

    
2051
        line = RadioSetting("dtmf_settings.mastervice",
2052
                            "Master and Vice ID",
2053
                            RadioSettingValueBoolean(
2054
                                _mem.dtmf_settings.mastervice))
2055
        dtmf_dec_settings.append(line)
2056

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

    
2067
        line = RadioSetting("dtmf_settings.minspection",
2068
                            "Master Inspection",
2069
                            RadioSettingValueBoolean(
2070
                                _mem.dtmf_settings.minspection))
2071
        dtmf_dec_settings.append(line)
2072

    
2073
        line = RadioSetting("dtmf_settings.mmonitor",
2074
                            "Master Monitor",
2075
                            RadioSettingValueBoolean(
2076
                                _mem.dtmf_settings.mmonitor))
2077
        dtmf_dec_settings.append(line)
2078

    
2079
        line = RadioSetting("dtmf_settings.mstun",
2080
                            "Master Stun",
2081
                            RadioSettingValueBoolean(
2082
                                _mem.dtmf_settings.mstun))
2083
        dtmf_dec_settings.append(line)
2084

    
2085
        line = RadioSetting("dtmf_settings.mkill",
2086
                            "Master Kill",
2087
                            RadioSettingValueBoolean(
2088
                                _mem.dtmf_settings.mkill))
2089
        dtmf_dec_settings.append(line)
2090

    
2091
        line = RadioSetting("dtmf_settings.mrevive",
2092
                            "Master Revive",
2093
                            RadioSettingValueBoolean(
2094
                                _mem.dtmf_settings.mrevive))
2095
        dtmf_dec_settings.append(line)
2096

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

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

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

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

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

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

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

    
2147
        val = RadioSettingValueString(0, 16,
2148
                                      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,
2158
                                      memory2string(
2159
                                          _mem.dtmf_settings.kill),
2160
                                          False, CHARSET_DTMF_DIGITS)
2161
        line = RadioSetting("dtmf_settings.kill",
2162
                            "Kill", val)
2163
        line.set_apply_callback(apply_dmtf_frame,
2164
                                _mem.dtmf_settings.kill)
2165
        dtmf_dec_settings.append(line)
2166

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

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

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

    
2197
        def apply_dmtf_listvalue(setting, obj):
2198
            LOG.debug("Setting value: "+ str(setting.value) + " from list")
2199
            val = str(setting.value)
2200
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2201
            val = LIST_DTMF_SPECIAL_VALUES[index]
2202
            obj.set_value(val)
2203

    
2204
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
2205
        line = RadioSetting(
2206
            "dtmf_settings.groupcode",
2207
            "Group Code",
2208
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2209
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2210
        line.set_apply_callback(apply_dmtf_listvalue,
2211
                                _mem.dtmf_settings.groupcode)
2212
        dtmf_dec_settings.append(line)
2213

    
2214
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
2215
        line = RadioSetting(
2216
            "dtmf_settings.spacecode",
2217
            "Space Code",
2218
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2219
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2220
        line.set_apply_callback(apply_dmtf_listvalue,
2221
                                _mem.dtmf_settings.spacecode)
2222
        dtmf_dec_settings.append(line)
2223

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

    
2241
        line = RadioSetting(
2242
            "dtmf_settings.delayproctime",
2243
            "Delay processing time",
2244
            RadioSettingValueList(LIST_DTMF_DELAY,
2245
                                  LIST_DTMF_DELAY[
2246
                                      _mem.dtmf_settings.delayproctime]))
2247
        dtmf_dec_settings.append(line)
2248

    
2249

    
2250
        # 5 Tone Settings
2251
        stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
2252
        codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
2253

    
2254
        group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
2255
        group_5tone.append(stds_5tone)
2256
        group_5tone.append(codes_5tone)
2257

    
2258
        top.append(group_5tone)
2259

    
2260
        def apply_list_value(setting, obj):
2261
            options = setting.value.get_options()
2262
            obj.set_value ( options.index(str(setting.value)) )
2263

    
2264
        _5tone_standards = self._memobj._5tone_std_settings
2265
        i = 0
2266
        for standard in _5tone_standards:
2267
            std_5tone = RadioSettingGroup ("std_5tone_" + str(i),
2268
                                           LIST_5TONE_STANDARDS[i])
2269
            stds_5tone.append(std_5tone)
2270

    
2271
            period = standard.period
2272
            if period == 255:
2273
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2274
                          " is not yet configured. Setting to 70ms.")
2275
                period = 5
2276

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

    
2288
            group_tone = standard.group_tone
2289
            if group_tone == 255:
2290
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2291
                          " is not yet configured. Setting to A.")
2292
                group_tone = 10
2293

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

    
2307
            repeat_tone = standard.repeat_tone
2308
            if repeat_tone == 255:
2309
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2310
                          " is not yet configured. Setting to E.")
2311
                repeat_tone = 14
2312

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

    
2327
        def my_apply_5tonestdlist_value(setting, obj):
2328
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2329
                obj.set_value(0xFF)
2330
            else:
2331
                obj.set_value( LIST_5TONE_STANDARDS.
2332
                              index(str(setting.value)) )
2333

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

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

    
2364
        def frame2string(frame):
2365
            frameString = ""
2366
            for digit in frame:
2367
                if digit != 255:
2368
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2369
            return frameString
2370

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

    
2390
            val = RadioSettingValueString(0, 6,
2391
                                          frame2string(code.frame1), False)
2392
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
2393
                                " Frame 1", val)
2394
            val.set_validate_callback(validate_5tone_frame)
2395
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2396
            code_5tone.append(line)
2397

    
2398
            val = RadioSettingValueString(0, 6,
2399
                                          frame2string(code.frame2), False)
2400
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2401
                                " Frame 2", val)
2402
            val.set_validate_callback(validate_5tone_frame)
2403
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2404
            code_5tone.append(line)
2405

    
2406
            val = RadioSettingValueString(0, 6,
2407
                                          frame2string(code.frame3), False)
2408
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2409
                                " Frame 3", val)
2410
            val.set_validate_callback(validate_5tone_frame)
2411
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2412
            code_5tone.append(line)
2413
            i = i + 1
2414

    
2415
        _5_tone_decode1 = RadioSetting(
2416
            "_5tone_settings._5tone_decode_call_frame1",
2417
            "5 Tone decode call Frame 1",
2418
            RadioSettingValueBoolean(
2419
                _mem._5tone_settings._5tone_decode_call_frame1))
2420
        group_5tone.append(_5_tone_decode1)
2421

    
2422
        _5_tone_decode2 = RadioSetting(
2423
            "_5tone_settings._5tone_decode_call_frame2",
2424
            "5 Tone decode call Frame 2",
2425
            RadioSettingValueBoolean(
2426
                _mem._5tone_settings._5tone_decode_call_frame2))
2427
        group_5tone.append(_5_tone_decode2)
2428

    
2429
        _5_tone_decode3 = RadioSetting(
2430
            "_5tone_settings._5tone_decode_call_frame3",
2431
            "5 Tone decode call Frame 3",
2432
            RadioSettingValueBoolean(
2433
                _mem._5tone_settings._5tone_decode_call_frame3))
2434
        group_5tone.append(_5_tone_decode3)
2435

    
2436
        _5_tone_decode_disp1 = RadioSetting(
2437
            "_5tone_settings._5tone_decode_disp_frame1",
2438
            "5 Tone decode disp Frame 1",
2439
            RadioSettingValueBoolean(
2440
                _mem._5tone_settings._5tone_decode_disp_frame1))
2441
        group_5tone.append(_5_tone_decode_disp1)
2442

    
2443
        _5_tone_decode_disp2 = RadioSetting(
2444
            "_5tone_settings._5tone_decode_disp_frame2",
2445
            "5 Tone decode disp Frame 2",
2446
            RadioSettingValueBoolean(
2447
                _mem._5tone_settings._5tone_decode_disp_frame2))
2448
        group_5tone.append(_5_tone_decode_disp2)
2449

    
2450
        _5_tone_decode_disp3 = RadioSetting(
2451
            "_5tone_settings._5tone_decode_disp_frame3",
2452
            "5 Tone decode disp Frame 3",
2453
            RadioSettingValueBoolean(
2454
                _mem._5tone_settings._5tone_decode_disp_frame3))
2455
        group_5tone.append(_5_tone_decode_disp3)
2456

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

    
2471
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2472
        if _5tone_delay1 == 255:
2473
            _5tone_delay1 = 20
2474

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

    
2485
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2486
        if _5tone_delay2 == 255:
2487
            _5tone_delay2 = 20
2488
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2489

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

    
2500
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2501
        if _5tone_delay3 == 255:
2502
            _5tone_delay3 = 20
2503
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2504

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

    
2515
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2516
        if ext_length == 255:
2517
            ext_length = 0
2518
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2519

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

    
2533
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2534
        if decode_reset_time == 255:
2535
            decode_reset_time = 59
2536
            LOG.debug("Decode reset time unconfigured. resetting.")
2537
        if decode_reset_time <= len(LIST_5TONE_RESET):
2538
            list = RadioSettingValueList(
2539
                LIST_5TONE_RESET,
2540
                LIST_5TONE_RESET[
2541
                    decode_reset_time])
2542
            line = RadioSetting("_5tone_settings.decode_reset_time",
2543
                                "Decode reset time", list)
2544
            group_5tone.append(line)
2545
        else:
2546
            LOG.debug("Invalid value decode reset time! Disabling.")
2547

    
2548
        # 2 Tone
2549
        encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
2550
        decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
2551

    
2552
        top.append(encode_2tone)
2553
        top.append(decode_2tone)
2554

    
2555
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2556
        if duration_1st_tone == 255:
2557
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2558
                      "configured. Setting to 600ms")
2559
            duration_1st_tone = 60
2560

    
2561
        if duration_1st_tone <= len( LIST_5TONE_DELAY ):
2562
            line = RadioSetting("_2tone.duration_1st_tone",
2563
                                "Duration 1st Tone",
2564
                                RadioSettingValueList(LIST_5TONE_DELAY,
2565
                                                      LIST_5TONE_DELAY[
2566
                                                          duration_1st_tone]))
2567
            encode_2tone.append(line)
2568

    
2569
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2570
        if duration_2nd_tone == 255:
2571
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2572
                      "configured. Setting to 600ms")
2573
            duration_2nd_tone = 60
2574

    
2575
        if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
2576
            line = RadioSetting("_2tone.duration_2nd_tone",
2577
                                "Duration 2nd Tone",
2578
                                RadioSettingValueList(LIST_5TONE_DELAY,
2579
                                                      LIST_5TONE_DELAY[
2580
                                                          duration_2nd_tone]))
2581
            encode_2tone.append(line)
2582

    
2583
        duration_gap = self._memobj._2tone.duration_gap
2584
        if duration_gap == 255:
2585
            LOG.debug("Duration of gap is not yet " +
2586
                      "configured. Setting to 300ms")
2587
            duration_gap = 30
2588

    
2589
        if duration_gap <= len( LIST_5TONE_DELAY ):
2590
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2591
                                RadioSettingValueList(LIST_5TONE_DELAY,
2592
                                                      LIST_5TONE_DELAY[
2593
                                                          duration_gap]))
2594
            encode_2tone.append(line)
2595

    
2596
        def _2tone_validate(value):
2597
            if value == 0:
2598
                return 65535
2599
            if value == 65535:
2600
                return value
2601
            if not (300 <= value and value <= 3000):
2602
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2603
                raise InvalidValueError(msg)
2604
            return value
2605

    
2606
        def apply_2tone_freq(setting, obj):
2607
            val = int(setting.value)
2608
            if (val == 0) or (val == 65535):
2609
                obj.set_value(65535)
2610
            else:
2611
                obj.set_value(val)
2612

    
2613
        i = 1
2614
        for code in  self._memobj._2tone._2tone_encode:
2615
            code_2tone = RadioSettingGroup ("code_2tone_" + str(i),
2616
                                           "Encode Code " + str(i))
2617
            encode_2tone.append(code_2tone)
2618

    
2619
            tmp = code.freq1
2620
            if tmp == 65535:
2621
                tmp = 0
2622
            val1 = RadioSettingValueInteger(0, 65535, tmp)
2623
            freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1",
2624
                                 "Frequency 1", val1)
2625
            val1.set_validate_callback(_2tone_validate)
2626
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
2627
            code_2tone.append(freq1)
2628

    
2629
            tmp = code.freq2
2630
            if tmp == 65535:
2631
                tmp = 0
2632
            val2 = RadioSettingValueInteger(0, 65535, tmp)
2633
            freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2",
2634
                                 "Frequency 2", val2)
2635
            val2.set_validate_callback(_2tone_validate)
2636
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
2637
            code_2tone.append(freq2)
2638

    
2639
            i = i + 1
2640

    
2641
        decode_reset_time = _mem._2tone.reset_time
2642
        if decode_reset_time == 255:
2643
            decode_reset_time = 59
2644
            LOG.debug("Decode reset time unconfigured. resetting.")
2645
        if decode_reset_time <= len(LIST_5TONE_RESET):
2646
            list = RadioSettingValueList(
2647
                LIST_5TONE_RESET,
2648
                LIST_5TONE_RESET[
2649
                    decode_reset_time])
2650
            line = RadioSetting("_2tone.reset_time",
2651
                                "Decode reset time", list)
2652
            decode_2tone.append(line)
2653
        else:
2654
            LOG.debug("Invalid value decode reset time! Disabling.")
2655

    
2656
        def apply_2tone_freq_pair(setting, obj):
2657
            val = int(setting.value)
2658
            derived_val = 65535
2659
            frqname = str(setting._name[-5:])
2660
            derivedname = "derived_from_" + frqname
2661

    
2662
            if (val == 0):
2663
                val = 65535
2664
                derived_val = 65535
2665
            else:
2666
                derived_val = int(round(2304000.0/val))
2667

    
2668
            obj[frqname].set_value( val )
2669
            obj[derivedname].set_value( derived_val )
2670

    
2671
            LOG.debug("Apply " + frqname + ": " + str(val) + " | "
2672
                      + derivedname + ": " + str(derived_val))
2673

    
2674
        i = 1
2675
        for decode_code in  self._memobj._2tone._2tone_decode:
2676
            _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
2677
                                           "Decode Code " + str(i))
2678
            decode_2tone.append(_2tone_dec_code)
2679

    
2680
            j = 1
2681
            for dec in decode_code.decs:
2682
                val = dec.dec
2683
                if val == 255:
2684
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
2685
                              " is not yet configured. Setting to 0.")
2686
                    val = 0
2687

    
2688
                if val <= len( LIST_2TONE_DEC ):
2689
                    line = RadioSetting(
2690
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
2691
                        "Dec " + str(j), RadioSettingValueList
2692
                        (LIST_2TONE_DEC,
2693
                         LIST_2TONE_DEC[val]))
2694
                    line.set_apply_callback(apply_list_value, dec.dec)
2695
                    _2tone_dec_code.append(line)
2696
                else:
2697
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
2698

    
2699
                val = dec.response
2700
                if val == 255:
2701
                    LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
2702
                              " is not yet configured. Setting to 0.")
2703
                    val = 0
2704

    
2705
                if val <= len( LIST_2TONE_RESPONSE ):
2706
                    line = RadioSetting(
2707
                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
2708
                        "Response " + str(j), RadioSettingValueList
2709
                        (LIST_2TONE_RESPONSE,
2710
                         LIST_2TONE_RESPONSE[val]))
2711
                    line.set_apply_callback(apply_list_value, dec.response)
2712
                    _2tone_dec_code.append(line)
2713
                else:
2714
                    LOG.debug("Invalid value for 2tone response! Disabling.")
2715

    
2716
                val = dec.alert
2717
                if val == 255:
2718
                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +
2719
                              " is not yet configured. Setting to 0.")
2720
                    val = 0
2721

    
2722
                if val <= len( PTTIDCODE_LIST ):
2723
                    line = RadioSetting(
2724
                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
2725
                        "Alert " + str(j), RadioSettingValueList
2726
                        (PTTIDCODE_LIST,
2727
                         PTTIDCODE_LIST[val]))
2728
                    line.set_apply_callback(apply_list_value, dec.alert)
2729
                    _2tone_dec_code.append(line)
2730
                else:
2731
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
2732
                j = j + 1
2733

    
2734
            freq = self._memobj._2tone.freqs[i-1]
2735
            for char in ['A', 'B', 'C', 'D']:
2736
                setting_name = "freq" + str(char)
2737

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

    
2756
            i = i + 1
2757

    
2758
        return top
2759

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

    
2787
                    if element.has_apply_callback():
2788
                        LOG.debug("Using apply callback")
2789
                        element.run_apply_callback()
2790
                    elif element.value.get_mutable():
2791
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2792
                        setattr(obj, setting, element.value)
2793
                except Exception, e:
2794
                    LOG.debug(element.get_name())
2795
                    raise
2796

    
2797
    @classmethod
2798
    def match_model(cls, filedata, filename):
2799
        match_size = False
2800
        match_model = False
2801

    
2802
        # testing the file data size
2803
        if len(filedata) == MEM_SIZE:
2804
            match_size = True
2805

    
2806
        # testing the firmware model fingerprint
2807
        match_model = model_match(cls, filedata)
2808

    
2809
        if match_size and match_model:
2810
            return True
2811
        else:
2812
            return False
2813

    
2814

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

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

    
2886
#seekto 0x0E80;
2887
struct {
2888
  u8 unknown1;
2889
  u8 vfomr;
2890
  u8 keylock;
2891
  u8 unknown2;
2892
  u8 unknown3:4,
2893
     vfomren:1,
2894
     unknown4:1,
2895
     reseten:1,
2896
     menuen:1;
2897
  u8 unknown5[11];
2898
  u8 dispab;
2899
  u8 mrcha;
2900
  u8 mrchb;
2901
  u8 menu;
2902
} settings2;
2903

    
2904
#seekto 0x0EC0;
2905
struct {
2906
  char line1[6];
2907
  char line2[6];
2908
} poweron_msg;
2909

    
2910
struct settings_vfo {
2911
  u8 freq[8];
2912
  u8 offset[6];
2913
  u8 unknown2[2];
2914
  ul16 rxtone;
2915
  ul16 txtone;
2916
  u8 scode;
2917
  u8 spmute;
2918
  u8 optsig;
2919
  u8 scramble;
2920
  u8 wide;
2921
  u8 power;
2922
  u8 shiftd;
2923
  u8 step;
2924
  u8 unknown3[4];
2925
};
2926

    
2927
#seekto 0x0F00;
2928
struct {
2929
  struct settings_vfo a;
2930
  struct settings_vfo b;
2931
} vfo;
2932

    
2933
#seekto 0x1000;
2934
struct {
2935
  char name[6];
2936
  u8 unknown1[10];
2937
} names[200];
2938

    
2939
#seekto 0x2400;
2940
struct {
2941
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2942
  u8 group_tone;
2943
  u8 repeat_tone;
2944
  u8 unused[13];
2945
} _5tone_std_settings[15];
2946

    
2947
#seekto 0x2500;
2948
struct {
2949
  u8 frame1[5];
2950
  u8 frame2[5];
2951
  u8 frame3[5];
2952
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2953
} _5tone_codes[15];
2954

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

    
2977
#seekto 0x2900;
2978
struct {
2979
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2980
} dtmf_codes[15];
2981

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

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

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

    
3062
#seekto 0x3000;
3063
struct {
3064
  u8 freq[8];
3065
  char broadcast_station_name[6];
3066
  u8 unknown[2];
3067
} fm_radio_preset[16];
3068

    
3069
#seekto 0x3C90;
3070
struct {
3071
  u8 vhf_low[3];
3072
  u8 vhf_high[3];
3073
  u8 uhf_low[3];
3074
  u8 uhf_high[3];
3075
} ranges;
3076

    
3077
// the UV-2501+220 & KT8900R has different zones for storing ranges
3078

    
3079
#seekto 0x3CD0;
3080
struct {
3081
  u8 vhf_low[3];
3082
  u8 vhf_high[3];
3083
  u8 unknown1[4];
3084
  u8 unknown2[6];
3085
  u8 vhf2_low[3];
3086
  u8 vhf2_high[3];
3087
  u8 unknown3[4];
3088
  u8 unknown4[6];
3089
  u8 uhf_low[3];
3090
  u8 uhf_high[3];
3091
} ranges220;
3092

    
3093
#seekto 0x3F70;
3094
struct {
3095
  char fp[6];
3096
} fingerprint;
3097

    
3098
"""
3099

    
3100

    
3101
class BTech(BTechMobileCommon):
3102
    """BTECH's UV-5001 and alike radios"""
3103
    BANDS = 2
3104
    COLOR_LCD = False
3105
    NAME_LENGTH = 6
3106

    
3107
    def set_options(self):
3108
        """This is to read the options from the image and set it in the
3109
        environment, for now just the limits of the freqs in the VHF/UHF
3110
        ranges"""
3111

    
3112
        # setting the correct ranges for each radio type
3113
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3114
            # the model 2501+220 has a segment in 220
3115
            # and a different position in the memmap
3116
            # also the QYT KT8900R
3117
            ranges = self._memobj.ranges220
3118
        else:
3119
            ranges = self._memobj.ranges
3120

    
3121
        # the normal dual bands
3122
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3123
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3124

    
3125
        # DEBUG
3126
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3127
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3128

    
3129
        # 220Mhz radios case
3130
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3131
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3132
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3133
            self._220_range = vhf2
3134

    
3135
        # set the class with the real data
3136
        self._vhf_range = vhf
3137
        self._uhf_range = uhf
3138

    
3139
    def process_mmap(self):
3140
        """Process the mem map into the mem object"""
3141

    
3142
        # Get it
3143
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3144

    
3145
        # load specific parameters from the radio image
3146
        self.set_options()
3147

    
3148

    
3149
# Declaring Aliases (Clones of the real radios)
3150
class JT2705M(chirp_common.Alias):
3151
    VENDOR = "Jetstream"
3152
    MODEL = "JT2705M"
3153

    
3154

    
3155
class JT6188Mini(chirp_common.Alias):
3156
    VENDOR = "Juentai"
3157
    MODEL = "JT-6188 Mini"
3158

    
3159

    
3160
class JT6188Plus(chirp_common.Alias):
3161
    VENDOR = "Juentai"
3162
    MODEL = "JT-6188 Plus"
3163

    
3164

    
3165
class SSGT890(chirp_common.Alias):
3166
    VENDOR = "Sainsonic"
3167
    MODEL = "GT-890"
3168

    
3169

    
3170
class ZastoneMP300(chirp_common.Alias):
3171
    VENDOR = "Zastone"
3172
    MODEL = "MP-300"
3173

    
3174

    
3175
# real radios
3176
@directory.register
3177
class UV2501(BTech):
3178
    """Baofeng Tech UV2501"""
3179
    MODEL = "UV-2501"
3180
    _fileid = [UV2501G3_fp,
3181
               UV2501G2_fp,
3182
               UV2501pp2_fp,
3183
               UV2501pp_fp]
3184

    
3185

    
3186
@directory.register
3187
class UV2501_220(BTech):
3188
    """Baofeng Tech UV2501+220"""
3189
    MODEL = "UV-2501+220"
3190
    BANDS = 3
3191
    _magic = MSTRING_220
3192
    _id2 = [UV2501_220pp_id, ]
3193
    _fileid = [UV2501_220G3_fp,
3194
               UV2501_220G2_fp,
3195
               UV2501_220_fp,
3196
               UV2501_220pp_fp]
3197

    
3198

    
3199
@directory.register
3200
class UV5001(BTech):
3201
    """Baofeng Tech UV5001"""
3202
    MODEL = "UV-5001"
3203
    _fileid = [UV5001G3_fp,
3204
               UV5001G22_fp,
3205
               UV5001G2_fp,
3206
               UV5001alpha_fp,
3207
               UV5001pp_fp]
3208
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3209
                     chirp_common.PowerLevel("Low", watts=10)]
3210

    
3211

    
3212
@directory.register
3213
class MINI8900(BTech):
3214
    """WACCOM MINI-8900"""
3215
    VENDOR = "WACCOM"
3216
    MODEL = "MINI-8900"
3217
    _magic = MSTRING_MINI8900
3218
    _fileid = [MINI8900_fp, ]
3219
    # Clones
3220
    ALIASES = [JT6188Plus, ]
3221

    
3222

    
3223
@directory.register
3224
class KTUV980(BTech):
3225
    """QYT KT-UV980"""
3226
    VENDOR = "QYT"
3227
    MODEL = "KT-UV980"
3228
    _vhf_range = (136000000, 175000000)
3229
    _uhf_range = (400000000, 481000000)
3230
    _magic = MSTRING_MINI8900
3231
    _fileid = [KTUV980_fp, ]
3232
    # Clones
3233
    ALIASES = [JT2705M, ]
3234

    
3235
# Please note that there is a version of this radios that is a clone of the
3236
# Waccom Mini8900, maybe an early version?
3237
@directory.register
3238
class KT9800(BTech):
3239
    """QYT KT8900"""
3240
    VENDOR = "QYT"
3241
    MODEL = "KT8900"
3242
    _vhf_range = (136000000, 175000000)
3243
    _uhf_range = (400000000, 481000000)
3244
    _magic = MSTRING_KT8900
3245
    _fileid = [KT8900_fp,
3246
               KT8900_fp1,
3247
               KT8900_fp2,
3248
               KT8900_fp3,
3249
               KT8900_fp4,
3250
               KT8900_fp5]
3251
    _id2 = [KT8900_id, ]
3252
    # Clones
3253
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3254

    
3255

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

    
3273

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

    
3287

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

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

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

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

    
3412
#seekto 0x0F00;
3413
struct {
3414
  struct settings_vfo a;
3415
  struct settings_vfo b;
3416
  struct settings_vfo c;
3417
  struct settings_vfo d;
3418
} vfo;
3419

    
3420
#seekto 0x0F80;
3421
struct {
3422
  char line1[8];
3423
  char line2[8];
3424
  char line3[8];
3425
  char line4[8];
3426
  char line5[8];
3427
  char line6[8];
3428
  char line7[8];
3429
  char line8[8];
3430
} poweron_msg;
3431

    
3432
#seekto 0x1000;
3433
struct {
3434
  char name[8];
3435
  u8 unknown1[8];
3436
} names[200];
3437

    
3438
#seekto 0x2400;
3439
struct {
3440
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3441
  u8 group_tone;
3442
  u8 repeat_tone;
3443
  u8 unused[13];
3444
} _5tone_std_settings[15];
3445

    
3446
#seekto 0x2500;
3447
struct {
3448
  u8 frame1[5];
3449
  u8 frame2[5];
3450
  u8 frame3[5];
3451
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3452
} _5tone_codes[15];
3453

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

    
3476
#seekto 0x2900;
3477
struct {
3478
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3479
} dtmf_codes[15];
3480

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

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

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

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

    
3579
#seekto 0x3F70;
3580
struct {
3581
  char fp[6];
3582
} fingerprint;
3583

    
3584
"""
3585

    
3586

    
3587
class BTechColor(BTechMobileCommon):
3588
    """BTECH's Color LCD Mobile and alike radios"""
3589
    COLOR_LCD = True
3590
    NAME_LENGTH = 8
3591

    
3592
    def process_mmap(self):
3593
        """Process the mem map into the mem object"""
3594

    
3595
        # Get it
3596
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3597

    
3598
        # load specific parameters from the radio image
3599
        self.set_options()
3600

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

    
3606
        # setting the correct ranges for each radio type
3607
        ranges = self._memobj.ranges
3608

    
3609
        # the normal dual bands
3610
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3611
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3612

    
3613
        # DEBUG
3614
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3615
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3616

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

    
3624
            # 350Mhz band
3625
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
3626
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
3627
            self._350_range = uhf2
3628

    
3629
        # set the class with the real data
3630
        self._vhf_range = vhf
3631
        self._uhf_range = uhf
3632

    
3633

    
3634
# Declaring Aliases (Clones of the real radios)
3635
class SKT8900D(chirp_common.Alias):
3636
    VENDOR = "Surecom"
3637
    MODEL = "S-KT8900D"
3638

    
3639

    
3640
class QB25(chirp_common.Alias):
3641
    VENDOR = "Radioddity"
3642
    MODEL = "QB25"
3643

    
3644

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

    
3656

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

    
3669

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

    
3682

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

    
3698

    
3699
@directory.register
3700
class KT8900D(BTechColor):
3701
    """QYT KT8900D"""
3702
    VENDOR = "QYT"
3703
    MODEL = "KT8900D"
3704
    BANDS = 2
3705
    _vhf_range = (136000000, 175000000)
3706
    _uhf_range = (400000000, 481000000)
3707
    _magic = MSTRING_KT8900D
3708
    _fileid = [KT8900D_fp, KT8900D_fp1]
(5-5/5)