Project

General

Profile

Bug #5325 ยป btech_vc4284.py

test driver to support MCU version: VC4284 - Jim Unroe, 11/05/2017 06:36 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

    
214
# KT7900D (quad band)
215
KT7900D_fp = "VC4004"
216
KT7900D_fp1 = "VC4284"
217

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

    
221
# KT8900D (dual band)
222
KT8900D_fp = "VC2002"
223

    
224
# LUITON LT-588UV
225
LT588UV_fp = "V2G1F4"
226
# Added by rstrickoff gen 2 id
227
LT588UV_fp1 = "V2G214"
228

    
229

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

    
247

    
248
def _clean_buffer(radio):
249
    """Cleaning the read serial buffer, hard timeout to survive an infinite
250
    data stream"""
251

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

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

    
268
        # restore the default serial timeout
269
        radio.pipe.timeout = STIMEOUT
270

    
271
    except Exception:
272
        raise errors.RadioError("Unknown error cleaning the serial buffer")
273

    
274

    
275
def _rawrecv(radio, amount):
276
    """Raw read from the radio device, less intensive way"""
277

    
278
    data = ""
279

    
280
    try:
281
        data = radio.pipe.read(amount)
282

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

    
288
        # fail if no data is received
289
        if len(data) == 0:
290
            raise errors.RadioError("No data received from radio")
291

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

    
297
    except:
298
        raise errors.RadioError("Error reading data from radio")
299

    
300
    return data
301

    
302

    
303
def _send(radio, data):
304
    """Send data to the radio device"""
305

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

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

    
328

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

    
336
    return frame
337

    
338

    
339
def _recv(radio, addr):
340
    """Get data from the radio all at once to lower syscalls load"""
341

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

    
345
    # get the whole block
346
    block = _rawrecv(radio, BLOCK_SIZE + 5)
347

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

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

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

    
363
    # return the data
364
    return block[5:]
365

    
366

    
367
def _start_clone_mode(radio, status):
368
    """Put the radio in clone mode and get the ident string, 3 tries"""
369

    
370
    # cleaning the serial buffer
371
    _clean_buffer(radio)
372

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

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

    
385
            # send the magic word
386
            _send(radio, radio._magic)
387

    
388
            # Now you get a x06 of ACK if all goes well
389
            ack = radio.pipe.read(1)
390

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

    
397
                return True
398

    
399
        return False
400

    
401
    except errors.RadioError:
402
        raise
403
    except Exception, e:
404
        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
405

    
406

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

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

    
422
    # Ok, get the ident string
423
    ident = _rawrecv(radio, 49)
424

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

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

    
439
            break
440

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

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

    
451
        # query & receive the extra ID
452
        _send(radio, _make_frame("S", 0x3DF0, 16))
453
        id2 = _rawrecv(radio, 21)
454

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

    
462
        # ok, the correct string must be in the received data
463
        if radio._id2 not in id2:
464
            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
465
                      (radio.MODEL, util.hexprint(id2)))
466
            raise errors.RadioError("The extra ID is wrong, aborting.")
467

    
468
        # this radios need a extra request/answer here on the upload
469
        # the amount of data received depends of the radio type
470
        #
471
        # also the first block of TX must no have the ACK at the beginning
472
        # see _upload for this.
473
        if upload is True:
474
            # send an ACK
475
            _send(radio, ACK_CMD)
476

    
477
            # the amount of data depend on the radio, so far we have two radios
478
            # reading two bytes with an ACK at the end and just ONE with just
479
            # one byte (QYT KT8900)
480
            # the JT-6188 appears a clone of the last, but reads TWO bytes.
481
            #
482
            # we will read two bytes with a custom timeout to not penalize the
483
            # users for this.
484
            #
485
            # we just check for a response and last byte being a ACK, that is
486
            # the common stone for all radios (3 so far)
487
            ack = _rawrecv(radio, 2)
488

    
489
            # checking
490
            if len(ack) == 0 or ack[-1:] != ACK_CMD:
491
                raise errors.RadioError("Radio didn't ACK the upload")
492

    
493
            # restore the default serial timeout
494
            radio.pipe.timeout = STIMEOUT
495

    
496
    # DEBUG
497
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
498

    
499
    return True
500

    
501

    
502
def _download(radio):
503
    """Get the memory map"""
504

    
505
    # UI progress
506
    status = chirp_common.Status()
507

    
508
    # put radio in program mode and identify it
509
    _do_ident(radio, status)
510

    
511
    # the models that doesn't have the extra ID have to make a dummy read here
512
    if radio._id2 is False:
513
        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
514
        discard = _rawrecv(radio, BLOCK_SIZE + 5)
515

    
516
        if debug is True:
517
            LOG.info("Dummy first block read done, got this:\n\n %s",
518
                     util.hexprint(discard))
519

    
520
    # reset the progress bar in the UI
521
    status.max = MEM_SIZE / BLOCK_SIZE
522
    status.msg = "Cloning from radio..."
523
    status.cur = 0
524
    radio.status_fn(status)
525

    
526
    # cleaning the serial buffer
527
    _clean_buffer(radio)
528

    
529
    data = ""
530
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
531
        # sending the read request
532
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
533

    
534
        # read
535
        d = _recv(radio, addr)
536

    
537
        # aggregate the data
538
        data += d
539

    
540
        # UI Update
541
        status.cur = addr / BLOCK_SIZE
542
        status.msg = "Cloning from radio..."
543
        radio.status_fn(status)
544

    
545
    return data
546

    
547

    
548
def _upload(radio):
549
    """Upload procedure"""
550

    
551
    # The UPLOAD mem is restricted to lower than 0x3100,
552
    # so we will overide that here localy
553
    MEM_SIZE = 0x3100
554

    
555
    # UI progress
556
    status = chirp_common.Status()
557

    
558
    # put radio in program mode and identify it
559
    _do_ident(radio, status, True)
560

    
561
    # get the data to upload to radio
562
    data = radio.get_mmap()
563

    
564
    # Reset the UI progress
565
    status.max = MEM_SIZE / TX_BLOCK_SIZE
566
    status.cur = 0
567
    status.msg = "Cloning to radio..."
568
    radio.status_fn(status)
569

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

    
574
    # cleaning the serial buffer
575
    _clean_buffer(radio)
576

    
577
    # the fun start here
578
    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
579
        # getting the block of data to send
580
        d = data[addr:addr + TX_BLOCK_SIZE]
581

    
582
        # build the frame to send
583
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
584

    
585
        # first block must not send the ACK at the beginning for the
586
        # ones that has the extra id, since this have to do a extra step
587
        if addr == 0 and radio._id2 is not False:
588
            frame = frame[1:]
589

    
590
        # send the frame
591
        _send(radio, frame)
592

    
593
        # receiving the response
594
        ack = _rawrecv(radio, 1)
595

    
596
        # basic check
597
        if len(ack) != 1:
598
            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
599

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

    
603
         # UI Update
604
        status.cur = addr / TX_BLOCK_SIZE
605
        status.msg = "Cloning to radio..."
606
        radio.status_fn(status)
607

    
608

    
609
def model_match(cls, data):
610
    """Match the opened/downloaded image to the correct version"""
611
    rid = data[0x3f70:0x3f76]
612

    
613
    if rid in cls._fileid:
614
        return True
615

    
616
    return False
617

    
618

    
619
def _decode_ranges(low, high):
620
    """Unpack the data in the ranges zones in the memmap and return
621
    a tuple with the integer corresponding to the Mhz it means"""
622
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
623
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
624
    ilow *= 1000000
625
    ihigh *= 1000000
626

    
627
    return (ilow, ihigh)
628

    
629

    
630
def _split(rf, f1, f2):
631
    """Returns False if the two freqs are in the same band (no split)
632
    or True otherwise"""
633

    
634
    # determine if the two freqs are in the same band
635
    for low, high in rf.valid_bands:
636
        if f1 >= low and f1 <= high and \
637
                f2 >= low and f2 <= high:
638
            # if the two freqs are on the same Band this is not a split
639
            return False
640

    
641
    # if you get here is because the freq pairs are split
642
    return True
643

    
644

    
645
class BTechMobileCommon(chirp_common.CloneModeRadio,
646
                        chirp_common.ExperimentalRadio):
647
    """BTECH's UV-5001 and alike radios"""
648
    VENDOR = "BTECH"
649
    MODEL = ""
650
    IDENT = ""
651
    BANDS = 2
652
    COLOR_LCD = False
653
    NAME_LENGTH = 6
654
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
655
                     chirp_common.PowerLevel("Low", watts=10)]
656
    _vhf_range = (130000000, 180000000)
657
    _220_range = (200000000, 271000000)
658
    _uhf_range = (400000000, 521000000)
659
    _350_range = (350000000, 391000000)
660
    _upper = 199
661
    _magic = MSTRING
662
    _fileid = None
663
    _id2 = False
664
    btech3 = False
665

    
666
    @classmethod
667
    def get_prompts(cls):
668
        rp = chirp_common.RadioPrompts()
669
        rp.experimental = \
670
            ('This driver is experimental.\n'
671
             '\n'
672
             'Please keep a copy of your memories with the original software '
673
             'if you treasure them, this driver is new and may contain'
674
             ' bugs.\n'
675
             '\n'
676
             )
677
        rp.pre_download = _(dedent("""\
678
            Follow these instructions to download your info:
679

    
680
            1 - Turn off your radio
681
            2 - Connect your interface cable
682
            3 - Turn on your radio
683
            4 - Do the download of your radio data
684

    
685
            """))
686
        rp.pre_upload = _(dedent("""\
687
            Follow these instructions to upload 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 upload of your radio data
693

    
694
            """))
695
        return rp
696

    
697
    def get_features(self):
698
        """Get the radio's features"""
699

    
700
        # we will use the following var as global
701
        global POWER_LEVELS
702

    
703
        rf = chirp_common.RadioFeatures()
704
        rf.has_settings = True
705
        rf.has_bank = False
706
        rf.has_tuning_step = False
707
        rf.can_odd_split = True
708
        rf.has_name = True
709
        rf.has_offset = True
710
        rf.has_mode = True
711
        rf.has_dtcs = True
712
        rf.has_rx_dtcs = True
713
        rf.has_dtcs_polarity = True
714
        rf.has_ctone = True
715
        rf.has_cross = True
716
        rf.valid_modes = MODES
717
        rf.valid_characters = VALID_CHARS
718
        rf.valid_name_length = self.NAME_LENGTH
719
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
720
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
721
        rf.valid_cross_modes = [
722
            "Tone->Tone",
723
            "DTCS->",
724
            "->DTCS",
725
            "Tone->DTCS",
726
            "DTCS->Tone",
727
            "->Tone",
728
            "DTCS->DTCS"]
729
        rf.valid_skips = SKIP_VALUES
730
        rf.valid_dtcs_codes = DTCS
731
        rf.memory_bounds = (0, self._upper)
732

    
733
        # power levels
734
        POWER_LEVELS = self._power_levels
735
        rf.valid_power_levels = POWER_LEVELS
736

    
737
        # normal dual bands
738
        rf.valid_bands = [self._vhf_range, self._uhf_range]
739

    
740
        # 220 band
741
        if self.BANDS == 3 or self.BANDS == 4:
742
            rf.valid_bands.append(self._220_range)
743

    
744
        # 350 band
745
        if self.BANDS == 4:
746
            rf.valid_bands.append(self._350_range)
747

    
748
        return rf
749

    
750
    def sync_in(self):
751
        """Download from radio"""
752
        data = _download(self)
753
        self._mmap = memmap.MemoryMap(data)
754
        self.process_mmap()
755

    
756
    def sync_out(self):
757
        """Upload to radio"""
758
        try:
759
            _upload(self)
760
        except errors.RadioError:
761
            raise
762
        except Exception, e:
763
            raise errors.RadioError("Error: %s" % e)
764

    
765
    def get_raw_memory(self, number):
766
        return repr(self._memobj.memory[number])
767

    
768
    def _decode_tone(self, val):
769
        """Parse the tone data to decode from mem, it returns:
770
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
771
        pol = None
772

    
773
        if val in [0, 65535]:
774
            return '', None, None
775
        elif val > 0x0258:
776
            a = val / 10.0
777
            return 'Tone', a, pol
778
        else:
779
            if val > 0x69:
780
                index = val - 0x6A
781
                pol = "R"
782
            else:
783
                index = val - 1
784
                pol = "N"
785

    
786
            tone = DTCS[index]
787
            return 'DTCS', tone, pol
788

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

    
813
    def get_memory(self, number):
814
        """Get the mem representation from the radio image"""
815
        _mem = self._memobj.memory[number]
816
        _names = self._memobj.names[number]
817

    
818
        # Create a high-level memory object to return to the UI
819
        mem = chirp_common.Memory()
820

    
821
        # Memory number
822
        mem.number = number
823

    
824
        if _mem.get_raw()[0] == "\xFF":
825
            mem.empty = True
826
            return mem
827

    
828
        # Freq and offset
829
        mem.freq = int(_mem.rxfreq) * 10
830
        # tx freq can be blank
831
        if _mem.get_raw()[4] == "\xFF":
832
            # TX freq not set
833
            mem.offset = 0
834
            mem.duplex = "off"
835
        else:
836
            # TX freq set
837
            offset = (int(_mem.txfreq) * 10) - mem.freq
838
            if offset != 0:
839
                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
840
                    mem.duplex = "split"
841
                    mem.offset = int(_mem.txfreq) * 10
842
                elif offset < 0:
843
                    mem.offset = abs(offset)
844
                    mem.duplex = "-"
845
                elif offset > 0:
846
                    mem.offset = offset
847
                    mem.duplex = "+"
848
            else:
849
                mem.offset = 0
850

    
851
        # name TAG of the channel
852
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
853

    
854
        # power
855
        mem.power = POWER_LEVELS[int(_mem.power)]
856

    
857
        # wide/narrow
858
        mem.mode = MODES[int(_mem.wide)]
859

    
860
        # skip
861
        mem.skip = SKIP_VALUES[_mem.add]
862

    
863
        # tone data
864
        rxtone = txtone = None
865
        txtone = self._decode_tone(_mem.txtone)
866
        rxtone = self._decode_tone(_mem.rxtone)
867
        chirp_common.split_tone_decode(mem, txtone, rxtone)
868

    
869
        # Extra
870
        mem.extra = RadioSettingGroup("extra", "Extra")
871

    
872
        if not self.COLOR_LCD or \
873
            (self.COLOR_LCD and not self.VENDOR == "BTECH"):
874
            scramble = RadioSetting("scramble", "Scramble",
875
                                    RadioSettingValueBoolean(bool(
876
                                        _mem.scramble)))
877
            mem.extra.append(scramble)
878

    
879
        bcl = RadioSetting("bcl", "Busy channel lockout",
880
                           RadioSettingValueBoolean(bool(_mem.bcl)))
881
        mem.extra.append(bcl)
882

    
883
        pttid = RadioSetting("pttid", "PTT ID",
884
                             RadioSettingValueList(PTTID_LIST,
885
                                                   PTTID_LIST[_mem.pttid]))
886
        mem.extra.append(pttid)
887

    
888
        # validating scode
889
        scode = _mem.scode if _mem.scode != 15 else 0
890
        pttidcode = RadioSetting("scode", "PTT ID signal code",
891
                                 RadioSettingValueList(
892
                                     PTTIDCODE_LIST,
893
                                     PTTIDCODE_LIST[scode]))
894
        mem.extra.append(pttidcode)
895

    
896
        optsig = RadioSetting("optsig", "Optional signaling",
897
                              RadioSettingValueList(
898
                                  OPTSIG_LIST,
899
                                  OPTSIG_LIST[_mem.optsig]))
900
        mem.extra.append(optsig)
901

    
902
        spmute = RadioSetting("spmute", "Speaker mute",
903
                              RadioSettingValueList(
904
                                  SPMUTE_LIST,
905
                                  SPMUTE_LIST[_mem.spmute]))
906
        mem.extra.append(spmute)
907

    
908
        return mem
909

    
910
    def set_memory(self, mem):
911
        """Set the memory data in the eeprom img from the UI"""
912
        # get the eprom representation of this channel
913
        _mem = self._memobj.memory[mem.number]
914
        _names = self._memobj.names[mem.number]
915

    
916
        mem_was_empty = False
917
        # same method as used in get_memory for determining if mem is empty
918
        # doing this BEFORE overwriting it with new values ...
919
        if _mem.get_raw()[0] == "\xFF":
920
            LOG.debug("This mem was empty before")
921
            mem_was_empty = True
922
        
923
        # if empty memmory
924
        if mem.empty:
925
            # the channel itself
926
            _mem.set_raw("\xFF" * 16)
927
            # the name tag
928
            _names.set_raw("\xFF" * 16)
929
            return
930

    
931
        if mem_was_empty:
932
            # Zero the whole memory if we're making it unempty for
933
            # the first time
934
            LOG.debug('Zeroing new memory')
935
            _mem.set_raw('\x00' * 16)
936

    
937
        # frequency
938
        _mem.rxfreq = mem.freq / 10
939

    
940
        # duplex
941
        if mem.duplex == "+":
942
            _mem.txfreq = (mem.freq + mem.offset) / 10
943
        elif mem.duplex == "-":
944
            _mem.txfreq = (mem.freq - mem.offset) / 10
945
        elif mem.duplex == "off":
946
            for i in _mem.txfreq:
947
                i.set_raw("\xFF")
948
        elif mem.duplex == "split":
949
            _mem.txfreq = mem.offset / 10
950
        else:
951
            _mem.txfreq = mem.freq / 10
952

    
953
        # tone data
954
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
955
            chirp_common.split_tone_encode(mem)
956
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
957
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
958

    
959
        # name TAG of the channel
960
        if len(mem.name) < self.NAME_LENGTH:
961
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
962
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
963
        _names.name = str(mem.name).replace(" ", "\xFF")
964

    
965
        # power, # default power level is high
966
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
967

    
968
        # wide/narrow
969
        _mem.wide = MODES.index(mem.mode)
970

    
971
        # scan add property
972
        _mem.add = SKIP_VALUES.index(mem.skip)
973

    
974
        # reseting unknowns, this have to be set by hand
975
        _mem.unknown0 = 0
976
        _mem.unknown1 = 0
977
        _mem.unknown2 = 0
978
        _mem.unknown3 = 0
979
        _mem.unknown4 = 0
980
        _mem.unknown5 = 0
981
        _mem.unknown6 = 0
982

    
983
        def _zero_settings():
984
            _mem.spmute = 0
985
            _mem.optsig = 0
986
            _mem.scramble = 0
987
            _mem.bcl = 0
988
            _mem.pttid = 0
989
            _mem.scode = 0
990

    
991
        if self.COLOR_LCD and _mem.scramble:
992
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
993
            _mem.scramble = 0
994

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

    
1015
        return mem
1016

    
1017
    def get_settings(self):
1018
        """Translate the bit in the mem_struct into settings in the UI"""
1019
        _mem = self._memobj
1020
        basic = RadioSettingGroup("basic", "Basic Settings")
1021
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1022
        other = RadioSettingGroup("other", "Other Settings")
1023
        work = RadioSettingGroup("work", "Work Mode Settings")
1024
        top = RadioSettings(basic, advanced, other, work)
1025

    
1026
        # Basic
1027
        if self.COLOR_LCD:
1028
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1029
                               RadioSettingValueList(
1030
                                   LIST_TMR,
1031
                                   LIST_TMR[_mem.settings.tmr]))
1032
            basic.append(tmr)
1033
        else:
1034
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1035
                               RadioSettingValueBoolean(_mem.settings.tdr))
1036
            basic.append(tdr)
1037

    
1038
        sql = RadioSetting("settings.sql", "Squelch level",
1039
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1040
        basic.append(sql)
1041

    
1042
        tot = RadioSetting("settings.tot", "Time out timer",
1043
                           RadioSettingValueList(
1044
                               LIST_TOT,
1045
                               LIST_TOT[_mem.settings.tot]))
1046
        basic.append(tot)
1047

    
1048
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1049
            apo = RadioSetting("settings.apo", "Auto power off timer",
1050
                               RadioSettingValueList(
1051
                                   LIST_APO,
1052
                                   LIST_APO[_mem.settings.apo]))
1053
            basic.append(apo)
1054
        else:
1055
            toa = RadioSetting("settings.apo", "Time out alert timer",
1056
                               RadioSettingValueList(
1057
                                   LIST_OFF1TO10, 
1058
                                   LIST_OFF1TO10[_mem.settings.apo]))
1059
            basic.append(toa)
1060

    
1061
        abr = RadioSetting("settings.abr", "Backlight timer",
1062
                           RadioSettingValueList(
1063
                               LIST_OFF1TO50,
1064
                               LIST_OFF1TO50[_mem.settings.abr]))
1065
        basic.append(abr)
1066

    
1067
        beep = RadioSetting("settings.beep", "Key beep",
1068
                            RadioSettingValueBoolean(_mem.settings.beep))
1069
        basic.append(beep)
1070

    
1071
        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1072
                              RadioSettingValueList(
1073
                                  LIST_DTMFST,
1074
                                  LIST_DTMFST[_mem.settings.dtmfst]))
1075
        basic.append(dtmfst)
1076

    
1077
        if not self.COLOR_LCD:
1078
            prisc = RadioSetting("settings.prisc", "Priority scan",
1079
                                 RadioSettingValueBoolean(
1080
                                     _mem.settings.prisc))
1081
            basic.append(prisc)
1082

    
1083
            prich = RadioSetting("settings.prich", "Priority channel",
1084
                                 RadioSettingValueInteger(0, 199,
1085
                                     _mem.settings.prich))
1086
            basic.append(prich)
1087

    
1088
        screv = RadioSetting("settings.screv", "Scan resume method",
1089
                             RadioSettingValueList(
1090
                                 LIST_SCREV,
1091
                                 LIST_SCREV[_mem.settings.screv]))
1092
        basic.append(screv)
1093

    
1094
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1095
                             RadioSettingValueInteger(0, 30,
1096
                                 _mem.settings.pttlt))
1097
        basic.append(pttlt)
1098

    
1099
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1100
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1101
                                 RadioSettingValueList(
1102
                                     LIST_EMCTPX,
1103
                                     LIST_EMCTPX[_mem.settings.emctp]))
1104
            basic.append(emctp)
1105
        else:
1106
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1107
                                 RadioSettingValueList(
1108
                                     LIST_EMCTP,
1109
                                     LIST_EMCTP[_mem.settings.emctp]))
1110
            basic.append(emctp)
1111

    
1112
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1113
                             RadioSettingValueInteger(0, 199,
1114
                                 _mem.settings.emcch))
1115
        basic.append(emcch)
1116

    
1117
        if self.COLOR_LCD:
1118
            if _mem.settings.sigbp > 0x01:
1119
                val = 0x00
1120
            else:
1121
                val = _mem.settings.sigbp
1122
            sigbp = RadioSetting("settings.sigbp", "Roger beep",
1123
                                 RadioSettingValueBoolean(val))
1124
            basic.append(sigbp)
1125
        else:
1126
            ringt = RadioSetting("settings.ringt", "Ring time",
1127
                                 RadioSettingValueList(
1128
                                     LIST_OFF1TO9,
1129
                                     LIST_OFF1TO9[_mem.settings.ringt]))
1130
            basic.append(ringt)
1131

    
1132
        camdf = RadioSetting("settings.camdf", "Display mode A",
1133
                             RadioSettingValueList(
1134
                                 LIST_MDF,
1135
                                 LIST_MDF[_mem.settings.camdf]))
1136
        basic.append(camdf)
1137

    
1138
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1139
                             RadioSettingValueList(
1140
                                 LIST_MDF,
1141
                                 LIST_MDF[_mem.settings.cbmdf]))
1142
        basic.append(cbmdf)
1143

    
1144
        if self.COLOR_LCD:
1145
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1146
                                 RadioSettingValueList(
1147
                                     LIST_MDF,
1148
                                     LIST_MDF[_mem.settings.ccmdf]))
1149
            basic.append(ccmdf)
1150

    
1151
            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1152
                                 RadioSettingValueList(
1153
                                     LIST_MDF,
1154
                                     LIST_MDF[_mem.settings.cdmdf]))
1155
            basic.append(cdmdf)
1156

    
1157
            langua = RadioSetting("settings.langua", "Language",
1158
                                  RadioSettingValueList(
1159
                                      LIST_LANGUA,
1160
                                      LIST_LANGUA[_mem.settings.langua]))
1161
            basic.append(langua)
1162

    
1163
        if self.VENDOR == "BTECH":
1164
            if self.COLOR_LCD:
1165
                sync = RadioSetting("settings.sync", "Channel display sync",
1166
                                    RadioSettingValueList(
1167
                                        LIST_SYNC,
1168
                                        LIST_SYNC[_mem.settings.sync]))
1169
                basic.append(sync)
1170
            else:
1171
                sync = RadioSetting("settings.sync", "A/B channel sync",
1172
                                    RadioSettingValueBoolean(
1173
                                        _mem.settings.sync))
1174
                basic.append(sync)
1175
        else:
1176
            autolk = RadioSetting("settings.sync", "Auto keylock",
1177
                                  RadioSettingValueBoolean(
1178
                                      _mem.settings.sync))
1179
            basic.append(autolk)
1180

    
1181
        if not self.COLOR_LCD:
1182
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1183
                                  RadioSettingValueList(
1184
                                      LIST_PONMSG,
1185
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1186
            basic.append(ponmsg)
1187

    
1188
        if self.COLOR_LCD:
1189
            mainfc = RadioSetting("settings.mainfc", 
1190
                                  "Main LCD foreground color",
1191
                                      RadioSettingValueList(
1192
                                          LIST_COLOR8,
1193
                                          LIST_COLOR8[_mem.settings.mainfc]))
1194
            basic.append(mainfc)
1195

    
1196
            mainbc = RadioSetting("settings.mainbc",
1197
                                  "Main LCD background color",
1198
                                      RadioSettingValueList(
1199
                                          LIST_COLOR8,
1200
                                          LIST_COLOR8[_mem.settings.mainbc]))
1201
            basic.append(mainbc)
1202

    
1203
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1204
                                  RadioSettingValueList(
1205
                                      LIST_COLOR8,
1206
                                      LIST_COLOR8[_mem.settings.menufc]))
1207
            basic.append(menufc)
1208

    
1209
            menubc = RadioSetting("settings.menubc", "Menu background color",
1210
                                  RadioSettingValueList(
1211
                                      LIST_COLOR8,
1212
                                      LIST_COLOR8[_mem.settings.menubc]))
1213
            basic.append(menubc)
1214

    
1215
            stafc = RadioSetting("settings.stafc",
1216
                                 "Top status foreground color",
1217
                                     RadioSettingValueList(
1218
                                         LIST_COLOR8,
1219
                                         LIST_COLOR8[_mem.settings.stafc]))
1220
            basic.append(stafc)
1221

    
1222
            stabc = RadioSetting("settings.stabc",
1223
                                 "Top status background color",
1224
                                     RadioSettingValueList(
1225
                                         LIST_COLOR8,
1226
                                         LIST_COLOR8[_mem.settings.stabc]))
1227
            basic.append(stabc)
1228

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

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

    
1243
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1244
                                RadioSettingValueList(
1245
                                    LIST_COLOR8,
1246
                                    LIST_COLOR8[_mem.settings.rxfc]))
1247
            basic.append(rxfc)
1248

    
1249
            txfc = RadioSetting("settings.txfc",
1250
                                "Transmitting character color",
1251
                                    RadioSettingValueList(
1252
                                        LIST_COLOR8,
1253
                                        LIST_COLOR8[_mem.settings.txfc]))
1254
            basic.append(txfc)
1255

    
1256
            txdisp = RadioSetting("settings.txdisp",
1257
                                  "Transmitting status display",
1258
                                      RadioSettingValueList(
1259
                                          LIST_TXDISP,
1260
                                          LIST_TXDISP[_mem.settings.txdisp]))
1261
            basic.append(txdisp)
1262
        else:
1263
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1264
                                 RadioSettingValueList(
1265
                                     LIST_COLOR4,
1266
                                     LIST_COLOR4[_mem.settings.wtled]))
1267
            basic.append(wtled)
1268

    
1269
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1270
                                 RadioSettingValueList(
1271
                                     LIST_COLOR4,
1272
                                     LIST_COLOR4[_mem.settings.rxled]))
1273
            basic.append(rxled)
1274

    
1275
            txled = RadioSetting("settings.txled", "TX backlight Color",
1276
                                 RadioSettingValueList(
1277
                                     LIST_COLOR4,
1278
                                     LIST_COLOR4[_mem.settings.txled]))
1279
            basic.append(txled)
1280

    
1281
        anil = RadioSetting("settings.anil", "ANI length",
1282
                            RadioSettingValueList(
1283
                                LIST_ANIL,
1284
                                LIST_ANIL[_mem.settings.anil]))
1285
        basic.append(anil)
1286

    
1287
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1288
                            RadioSettingValueList(
1289
                                LIST_REPS,
1290
                                LIST_REPS[_mem.settings.reps]))
1291
        basic.append(reps)
1292

    
1293
        repm = RadioSetting("settings.repm", "Relay condition",
1294
                            RadioSettingValueList(
1295
                                LIST_REPM,
1296
                                LIST_REPM[_mem.settings.repm]))
1297
        basic.append(repm)
1298

    
1299
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1300
            if self.COLOR_LCD:
1301
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1302
                                     RadioSettingValueList(
1303
                                         LIST_OFF1TO50,
1304
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1305
                basic.append(tmrmr)
1306
            else:
1307
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1308
                                     RadioSettingValueList(
1309
                                         LIST_OFF1TO50, 
1310
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1311
                basic.append(tdrab)
1312

    
1313
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1314
                               RadioSettingValueBoolean(_mem.settings.ste))
1315
            basic.append(ste)
1316

    
1317
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1318
                                 RadioSettingValueList(
1319
                                     LIST_OFF1TO9,
1320
                                     LIST_OFF1TO9[_mem.settings.rpste]))
1321
            basic.append(rpste)
1322

    
1323
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1324
                                 RadioSettingValueList(
1325
                                     LIST_RPTDL,
1326
                                     LIST_RPTDL[_mem.settings.rptdl]))
1327
            basic.append(rptdl)
1328

    
1329
        if str(_mem.fingerprint.fp) in BTECH3:
1330
            mgain = RadioSetting("settings.mgain", "Mic gain",
1331
                                 RadioSettingValueInteger(0, 120,
1332
                                     _mem.settings.mgain))
1333
            basic.append(mgain)
1334

    
1335
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1336
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1337
                                 RadioSettingValueInteger(0, 60,
1338
                                     _mem.settings.dtmfg))
1339
            basic.append(dtmfg)
1340

    
1341
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1342
            mgain = RadioSetting("settings.mgain", "Mic gain",
1343
                                 RadioSettingValueInteger(0, 120,
1344
                                     _mem.settings.mgain))
1345
            basic.append(mgain)
1346

    
1347
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1348
                                  RadioSettingValueList(
1349
                                      LIST_SKIPTX,
1350
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1351
            basic.append(skiptx)
1352

    
1353
            scmode = RadioSetting("settings.scmode", "Scan mode",
1354
                                  RadioSettingValueList(
1355
                                      LIST_SCMODE,
1356
                                      LIST_SCMODE[_mem.settings.scmode]))
1357
            basic.append(scmode)
1358

    
1359
        # Advanced
1360
        def _filter(name):
1361
            filtered = ""
1362
            for char in str(name):
1363
                if char in VALID_CHARS:
1364
                    filtered += char
1365
                else:
1366
                    filtered += " "
1367
            return filtered
1368

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

    
1422
        if self.MODEL in ("UV-2501", "UV-5001"):
1423
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1424
                                   RadioSettingValueBoolean(
1425
                                       _mem.settings2.vfomren))
1426
            advanced.append(vfomren)
1427

    
1428
            reseten = RadioSetting("settings2.reseten", "RESET",
1429
                                   RadioSettingValueBoolean(
1430
                                       _mem.settings2.reseten))
1431
            advanced.append(reseten)
1432

    
1433
            menuen = RadioSetting("settings2.menuen", "Menu",
1434
                                  RadioSettingValueBoolean(
1435
                                      _mem.settings2.menuen))
1436
            advanced.append(menuen)
1437

    
1438
        # Other
1439
        def convert_bytes_to_limit(bytes):
1440
            limit = ""
1441
            for byte in bytes:
1442
                if byte < 10:
1443
                    limit += chr(byte + 0x30)
1444
                else:
1445
                    break
1446
            return limit
1447

    
1448
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1449
            _ranges = self._memobj.ranges220
1450
            ranges = "ranges220"
1451
        else:
1452
            _ranges = self._memobj.ranges
1453
            ranges = "ranges"
1454

    
1455
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1456
        val = RadioSettingValueString(0, 3, _limit)
1457
        val.set_mutable(False)
1458
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1459
        other.append(vhf_low)
1460

    
1461
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1462
        val = RadioSettingValueString(0, 3, _limit)
1463
        val.set_mutable(False)
1464
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1465
        other.append(vhf_high)
1466

    
1467
        if self.BANDS == 3 or self.BANDS == 4:
1468
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1469
            val = RadioSettingValueString(0, 3, _limit)
1470
            val.set_mutable(False)
1471
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1472
            other.append(vhf2_low)
1473

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

    
1480
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1481
        val = RadioSettingValueString(0, 3, _limit)
1482
        val.set_mutable(False)
1483
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1484
        other.append(uhf_low)
1485

    
1486
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1487
        val = RadioSettingValueString(0, 3, _limit)
1488
        val.set_mutable(False)
1489
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1490
        other.append(uhf_high)
1491

    
1492
        if self.BANDS == 4:
1493
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1494
            val = RadioSettingValueString(0, 3, _limit)
1495
            val.set_mutable(False)
1496
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1497
            other.append(uhf2_low)
1498

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

    
1505
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1506
        val.set_mutable(False)
1507
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1508
        other.append(fp)
1509

    
1510

    
1511
        # Work
1512
        if self.COLOR_LCD:
1513
            dispab = RadioSetting("settings2.dispab", "Display",
1514
                                  RadioSettingValueList(
1515
                                      LIST_ABCD,
1516
                                      LIST_ABCD[_mem.settings2.dispab]))
1517
            work.append(dispab)
1518
        else:
1519
            dispab = RadioSetting("settings2.dispab", "Display",
1520
                                  RadioSettingValueList(
1521
                                      LIST_AB,
1522
                                      LIST_AB[_mem.settings2.dispab]))
1523
            work.append(dispab)
1524

    
1525
        if self.COLOR_LCD:
1526
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1527
                                  RadioSettingValueList(
1528
                                      LIST_VFOMR,
1529
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1530
            work.append(vfomra)
1531

    
1532
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1533
                                  RadioSettingValueList(
1534
                                      LIST_VFOMR,
1535
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1536
            work.append(vfomrb)
1537

    
1538
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1539
                                  RadioSettingValueList(
1540
                                      LIST_VFOMR,
1541
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1542
            work.append(vfomrc)
1543

    
1544
            vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1545
                                  RadioSettingValueList(
1546
                                      LIST_VFOMR,
1547
                                      LIST_VFOMR[_mem.settings2.vfomrd]))
1548
            work.append(vfomrd)
1549
        else:
1550
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1551
                                 RadioSettingValueList(
1552
                                     LIST_VFOMR,
1553
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1554
            work.append(vfomr)
1555

    
1556

    
1557
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1558
                           RadioSettingValueBoolean(_mem.settings2.keylock))
1559
        work.append(keylock)
1560

    
1561
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1562
                             RadioSettingValueInteger(0, 199,
1563
                                 _mem.settings2.mrcha))
1564
        work.append(mrcha)
1565

    
1566
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1567
                             RadioSettingValueInteger(0, 199,
1568
                                 _mem.settings2.mrchb))
1569
        work.append(mrchb)
1570

    
1571
        if self.COLOR_LCD:
1572
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1573
                                 RadioSettingValueInteger(0, 199,
1574
                                     _mem.settings2.mrchc))
1575
            work.append(mrchc)
1576

    
1577
            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1578
                                 RadioSettingValueInteger(0, 199,
1579
                                     _mem.settings2.mrchd))
1580
            work.append(mrchd)
1581

    
1582
        def convert_bytes_to_freq(bytes):
1583
            real_freq = 0
1584
            for byte in bytes:
1585
                real_freq = (real_freq * 10) + byte
1586
            return chirp_common.format_freq(real_freq * 10)
1587

    
1588
        def my_validate(value):
1589
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1590
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1591
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1592
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1593
            if self.BANDS == 3 or self.BANDS == 4:
1594
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1595
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1596
            if self.BANDS == 4:
1597
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1598
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1599

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

    
1631
        def apply_freq(setting, obj):
1632
            value = chirp_common.parse_freq(str(setting.value)) / 10
1633
            for i in range(7, -1, -1):
1634
                obj.freq[i] = value % 10
1635
                value /= 10
1636

    
1637
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1638
                                        _mem.vfo.a.freq))
1639
        val1a.set_validate_callback(my_validate)
1640
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1641
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1642
        work.append(vfoafreq)
1643

    
1644
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1645
                                        _mem.vfo.b.freq))
1646
        val1b.set_validate_callback(my_validate)
1647
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1648
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1649
        work.append(vfobfreq)
1650

    
1651
        if self.COLOR_LCD:
1652
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1653
                                            _mem.vfo.c.freq))
1654
            val1c.set_validate_callback(my_validate)
1655
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
1656
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
1657
            work.append(vfocfreq)
1658

    
1659
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1660
                                            _mem.vfo.d.freq))
1661
            val1d.set_validate_callback(my_validate)
1662
            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
1663
            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
1664
            work.append(vfodfreq)
1665

    
1666
        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1667
                                  RadioSettingValueList(
1668
                                      LIST_SHIFT,
1669
                                      LIST_SHIFT[_mem.vfo.a.shiftd]))
1670
        work.append(vfoashiftd)
1671

    
1672
        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1673
                                  RadioSettingValueList(
1674
                                      LIST_SHIFT,
1675
                                      LIST_SHIFT[_mem.vfo.b.shiftd]))
1676
        work.append(vfobshiftd)
1677

    
1678
        if self.COLOR_LCD:
1679
            vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
1680
                                      RadioSettingValueList(
1681
                                          LIST_SHIFT,
1682
                                          LIST_SHIFT[_mem.vfo.c.shiftd]))
1683
            work.append(vfocshiftd)
1684

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

    
1691
        def convert_bytes_to_offset(bytes):
1692
            real_offset = 0
1693
            for byte in bytes:
1694
                real_offset = (real_offset * 10) + byte
1695
            return chirp_common.format_freq(real_offset * 1000)
1696

    
1697
        def apply_offset(setting, obj):
1698
            value = chirp_common.parse_freq(str(setting.value)) / 1000
1699
            for i in range(5, -1, -1):
1700
                obj.offset[i] = value % 10
1701
                value /= 10
1702

    
1703
        if self.COLOR_LCD:
1704
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1705
                                            _mem.vfo.a.offset))
1706
            vfoaoffset = RadioSetting("vfo.a.offset",
1707
                                      "VFO A offset (0.000-999.999)", val1a)
1708
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1709
            work.append(vfoaoffset)
1710

    
1711
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1712
                                            _mem.vfo.b.offset))
1713
            vfoboffset = RadioSetting("vfo.b.offset",
1714
                                      "VFO B offset (0.000-999.999)", val1b)
1715
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1716
            work.append(vfoboffset)
1717

    
1718
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1719
                                            _mem.vfo.c.offset))
1720
            vfocoffset = RadioSetting("vfo.c.offset",
1721
                                      "VFO C offset (0.000-999.999)", val1c)
1722
            vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
1723
            work.append(vfocoffset)
1724

    
1725
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1726
                                            _mem.vfo.d.offset))
1727
            vfodoffset = RadioSetting("vfo.d.offset",
1728
                                      "VFO D offset (0.000-999.999)", val1d)
1729
            vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
1730
            work.append(vfodoffset)
1731
        else:
1732
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1733
                                            _mem.vfo.a.offset))
1734
            vfoaoffset = RadioSetting("vfo.a.offset",
1735
                                      "VFO A offset (0.000-99.999)", val1a)
1736
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1737
            work.append(vfoaoffset)
1738

    
1739
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1740
                                            _mem.vfo.b.offset))
1741
            vfoboffset = RadioSetting("vfo.b.offset",
1742
                                      "VFO B offset (0.000-99.999)", val1b)
1743
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1744
            work.append(vfoboffset)
1745

    
1746

    
1747
        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
1748
                               RadioSettingValueList(
1749
                                   LIST_TXP,
1750
                                   LIST_TXP[_mem.vfo.a.power]))
1751
        work.append(vfoatxp)
1752

    
1753
        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
1754
                               RadioSettingValueList(
1755
                                   LIST_TXP,
1756
                                   LIST_TXP[_mem.vfo.b.power]))
1757
        work.append(vfobtxp)
1758

    
1759
        if self.COLOR_LCD:
1760
            vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
1761
                                   RadioSettingValueList(
1762
                                       LIST_TXP,
1763
                                       LIST_TXP[_mem.vfo.c.power]))
1764
            work.append(vfoctxp)
1765

    
1766
            vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
1767
                                   RadioSettingValueList(
1768
                                       LIST_TXP,
1769
                                       LIST_TXP[_mem.vfo.d.power]))
1770
            work.append(vfodtxp)
1771

    
1772
        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
1773
                                RadioSettingValueList(
1774
                                    LIST_WIDE,
1775
                                    LIST_WIDE[_mem.vfo.a.wide]))
1776
        work.append(vfoawide)
1777

    
1778
        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
1779
                                RadioSettingValueList(
1780
                                    LIST_WIDE,
1781
                                    LIST_WIDE[_mem.vfo.b.wide]))
1782
        work.append(vfobwide)
1783

    
1784
        if self.COLOR_LCD:
1785
            vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
1786
                                    RadioSettingValueList(
1787
                                        LIST_WIDE,
1788
                                        LIST_WIDE[_mem.vfo.c.wide]))
1789
            work.append(vfocwide)
1790

    
1791
            vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
1792
                                    RadioSettingValueList(
1793
                                        LIST_WIDE,
1794
                                        LIST_WIDE[_mem.vfo.d.wide]))
1795
            work.append(vfodwide)
1796

    
1797
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
1798
                                RadioSettingValueList(
1799
                                    LIST_STEP,
1800
                                    LIST_STEP[_mem.vfo.a.step]))
1801
        work.append(vfoastep)
1802

    
1803
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
1804
                                RadioSettingValueList(
1805
                                    LIST_STEP,
1806
                                    LIST_STEP[_mem.vfo.b.step]))
1807
        work.append(vfobstep)
1808

    
1809
        if self.COLOR_LCD:
1810
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
1811
                                    RadioSettingValueList(
1812
                                        LIST_STEP,
1813
                                        LIST_STEP[_mem.vfo.c.step]))
1814
            work.append(vfocstep)
1815

    
1816
            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
1817
                                    RadioSettingValueList(
1818
                                        LIST_STEP,
1819
                                        LIST_STEP[_mem.vfo.d.step]))
1820
            work.append(vfodstep)
1821

    
1822
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
1823
                                  RadioSettingValueList(
1824
                                      OPTSIG_LIST,
1825
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
1826
        work.append(vfoaoptsig)
1827

    
1828
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
1829
                                  RadioSettingValueList(
1830
                                      OPTSIG_LIST,
1831
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
1832
        work.append(vfoboptsig)
1833

    
1834
        if self.COLOR_LCD:
1835
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
1836
                                      RadioSettingValueList(
1837
                                          OPTSIG_LIST,
1838
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
1839
            work.append(vfocoptsig)
1840

    
1841
            vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
1842
                                      RadioSettingValueList(
1843
                                          OPTSIG_LIST,
1844
                                          OPTSIG_LIST[_mem.vfo.d.optsig]))
1845
            work.append(vfodoptsig)
1846

    
1847
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
1848
                                  RadioSettingValueList(
1849
                                      SPMUTE_LIST,
1850
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
1851
        work.append(vfoaspmute)
1852

    
1853
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
1854
                                  RadioSettingValueList(
1855
                                      SPMUTE_LIST,
1856
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
1857
        work.append(vfobspmute)
1858

    
1859
        if self.COLOR_LCD:
1860
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
1861
                                      RadioSettingValueList(
1862
                                          SPMUTE_LIST,
1863
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
1864
            work.append(vfocspmute)
1865

    
1866
            vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
1867
                                      RadioSettingValueList(
1868
                                          SPMUTE_LIST,
1869
                                          SPMUTE_LIST[_mem.vfo.d.spmute]))
1870
            work.append(vfodspmute)
1871

    
1872
        if not self.COLOR_LCD or \
1873
            (self.COLOR_LCD and not self.VENDOR == "BTECH"):
1874
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
1875
                                   RadioSettingValueBoolean(
1876
                                       _mem.vfo.a.scramble))
1877
            work.append(vfoascr)
1878

    
1879
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
1880
                                   RadioSettingValueBoolean(
1881
                                       _mem.vfo.b.scramble))
1882
            work.append(vfobscr)
1883

    
1884
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
1885
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
1886
                                   RadioSettingValueBoolean(
1887
                                       _mem.vfo.c.scramble))
1888
            work.append(vfocscr)
1889

    
1890
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
1891
                                   RadioSettingValueBoolean(
1892
                                       _mem.vfo.d.scramble))
1893
            work.append(vfodscr)
1894

    
1895
        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
1896
                                 RadioSettingValueList(
1897
                                     PTTIDCODE_LIST,
1898
                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
1899
        work.append(vfoascode)
1900

    
1901
        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
1902
                                 RadioSettingValueList(
1903
                                     PTTIDCODE_LIST,
1904
                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
1905
        work.append(vfobscode)
1906

    
1907
        if self.COLOR_LCD:
1908
            vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
1909
                                     RadioSettingValueList(
1910
                                         PTTIDCODE_LIST,
1911
                                         PTTIDCODE_LIST[_mem.vfo.c.scode]))
1912
            work.append(vfocscode)
1913

    
1914
            vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
1915
                                     RadioSettingValueList(
1916
                                         PTTIDCODE_LIST,
1917
                                         PTTIDCODE_LIST[_mem.vfo.d.scode]))
1918
            work.append(vfodscode)
1919

    
1920
        pttid = RadioSetting("settings.pttid", "PTT ID",
1921
                             RadioSettingValueList(
1922
                                 PTTID_LIST,
1923
                                 PTTID_LIST[_mem.settings.pttid]))
1924
        work.append(pttid)
1925

    
1926
        if not self.COLOR_LCD:
1927
            #FM presets
1928
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
1929
            top.append(fm_presets)
1930

    
1931
            def fm_validate(value):
1932
                if value == 0:
1933
                    return chirp_common.format_freq(value)
1934
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
1935
                    msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
1936
                    raise InvalidValueError(msg)
1937
                return value
1938

    
1939
            def apply_fm_preset_name(setting, obj):
1940
                valstring = str (setting.value)
1941
                for i in range(0,6):
1942
                    if valstring[i] in VALID_CHARS:
1943
                        obj[i] = valstring[i]
1944
                    else:
1945
                        obj[i] = '0xff'
1946

    
1947
            def apply_fm_freq(setting, obj):
1948
                value = chirp_common.parse_freq(str(setting.value)) / 10
1949
                for i in range(7, -1, -1):
1950
                    obj.freq[i] = value % 10
1951
                    value /= 10
1952
        
1953
            _presets = self._memobj.fm_radio_preset
1954
            i = 1
1955
            for preset in _presets:
1956
                line = RadioSetting("fm_presets_"+ str(i), 
1957
                                    "Station name " + str(i),
1958
                                        RadioSettingValueString(0, 6, _filter(
1959
                                            preset.broadcast_station_name)))
1960
                line.set_apply_callback(apply_fm_preset_name, 
1961
                                        preset.broadcast_station_name)
1962
            
1963
                val = RadioSettingValueFloat(0, 108,
1964
                                             convert_bytes_to_freq(
1965
                                                 preset.freq))
1966
                fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq",
1967
                                      "Frequency "+ str(i), val)
1968
                val.set_validate_callback(fm_validate)
1969
                fmfreq.set_apply_callback(apply_fm_freq, preset)
1970
                fm_presets.append(line)
1971
                fm_presets.append(fmfreq)
1972
            
1973
                i = i + 1
1974

    
1975
         # DTMF-Setting
1976
        dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
1977
                                               "DTMF Encoding Settings")
1978
        dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
1979
                                               "DTMF Decoding Settings")
1980
        top.append(dtmf_enc_settings)
1981
        top.append(dtmf_dec_settings)
1982
        txdisable = RadioSetting("dtmf_settings.txdisable", 
1983
                                 "TX-Disable",
1984
                                 RadioSettingValueBoolean(
1985
                                     _mem.dtmf_settings.txdisable))
1986
        dtmf_enc_settings.append(txdisable)
1987

    
1988
        rxdisable = RadioSetting("dtmf_settings.rxdisable", 
1989
                                 "RX-Disable",
1990
                                 RadioSettingValueBoolean(
1991
                                     _mem.dtmf_settings.rxdisable))
1992
        dtmf_enc_settings.append(rxdisable)
1993

    
1994
        dtmfspeed_on = RadioSetting(
1995
            "dtmf_settings.dtmfspeed_on",
1996
            "DTMF Speed (On Time)",
1997
            RadioSettingValueList(LIST_DTMF_SPEED,
1998
                                  LIST_DTMF_SPEED[
1999
                                      _mem.dtmf_settings.dtmfspeed_on]))
2000
        dtmf_enc_settings.append(dtmfspeed_on)
2001

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

    
2010
        def memory2string(dmtf_mem):
2011
            dtmf_string = ""
2012
            for digit in dmtf_mem:
2013
                if digit != 255:
2014
                    index = LIST_DTMF_VALUES.index(digit)
2015
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2016
            return dtmf_string
2017

    
2018
        def apply_dmtf_frame(setting, obj):
2019
            LOG.debug("Setting DTMF-Code: " + str(setting.value) )
2020
            val_string = str(setting.value)
2021
            for i in range(0,16):
2022
                obj[i] = 255
2023
            i = 0
2024
            for current_char in val_string:
2025
                current_char = current_char.upper()
2026
                index = LIST_DTMF_DIGITS.index(current_char)
2027
                obj[i] = LIST_DTMF_VALUES[ index ]
2028
                i = i + 1
2029

    
2030
        codes = self._memobj.dtmf_codes
2031
        i = 1
2032
        for dtmfcode in codes:
2033
            val = RadioSettingValueString(0, 16, 
2034
                                          memory2string(dtmfcode.code),
2035
                                          False, CHARSET_DTMF_DIGITS)
2036
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
2037
                                "DMTF Code " + str(i), val)
2038
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
2039
            dtmf_enc_settings.append(line)
2040
            i = i + 1
2041

    
2042
        line = RadioSetting("dtmf_settings.mastervice", 
2043
                            "Master and Vice ID",
2044
                            RadioSettingValueBoolean(
2045
                                _mem.dtmf_settings.mastervice))
2046
        dtmf_dec_settings.append(line)
2047

    
2048
        val = RadioSettingValueString(0, 16, 
2049
                                      memory2string(
2050
                                          _mem.dtmf_settings.masterid),
2051
                                          False, CHARSET_DTMF_DIGITS)
2052
        line = RadioSetting("dtmf_settings.masterid",
2053
                            "Master Control ID ", val)
2054
        line.set_apply_callback(apply_dmtf_frame,
2055
                                _mem.dtmf_settings.masterid)
2056
        dtmf_dec_settings.append(line)
2057

    
2058
        line = RadioSetting("dtmf_settings.minspection", 
2059
                            "Master Inspection",
2060
                            RadioSettingValueBoolean(
2061
                                _mem.dtmf_settings.minspection))
2062
        dtmf_dec_settings.append(line)
2063

    
2064
        line = RadioSetting("dtmf_settings.mmonitor", 
2065
                            "Master Monitor",
2066
                            RadioSettingValueBoolean(
2067
                                _mem.dtmf_settings.mmonitor))
2068
        dtmf_dec_settings.append(line)
2069

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

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

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

    
2088
        val = RadioSettingValueString(0, 16, 
2089
                                      memory2string(
2090
                                          _mem.dtmf_settings.viceid),
2091
                                          False, CHARSET_DTMF_DIGITS)
2092
        line = RadioSetting("dtmf_settings.viceid",
2093
                            "Vice Control ID ", val)
2094
        line.set_apply_callback(apply_dmtf_frame,
2095
                                _mem.dtmf_settings.viceid)
2096
        dtmf_dec_settings.append(line)
2097

    
2098
        line = RadioSetting("dtmf_settings.vinspection", 
2099
                            "Vice Inspection",
2100
                            RadioSettingValueBoolean(
2101
                                _mem.dtmf_settings.vinspection))
2102
        dtmf_dec_settings.append(line)
2103

    
2104
        line = RadioSetting("dtmf_settings.vmonitor", 
2105
                            "Vice Monitor",
2106
                            RadioSettingValueBoolean(
2107
                                _mem.dtmf_settings.vmonitor))
2108
        dtmf_dec_settings.append(line)
2109

    
2110
        line = RadioSetting("dtmf_settings.vstun", 
2111
                            "Vice Stun",
2112
                            RadioSettingValueBoolean(
2113
                                _mem.dtmf_settings.vstun))
2114
        dtmf_dec_settings.append(line)
2115

    
2116
        line = RadioSetting("dtmf_settings.vkill", 
2117
                            "Vice Kill",
2118
                            RadioSettingValueBoolean(
2119
                                _mem.dtmf_settings.vkill))
2120
        dtmf_dec_settings.append(line)
2121

    
2122
        line = RadioSetting("dtmf_settings.vrevive", 
2123
                            "Vice Revive",
2124
                            RadioSettingValueBoolean(
2125
                                _mem.dtmf_settings.vrevive))
2126
        dtmf_dec_settings.append(line)
2127

    
2128
        val = RadioSettingValueString(0, 16, 
2129
                                      memory2string(
2130
                                          _mem.dtmf_settings.inspection),
2131
                                          False, CHARSET_DTMF_DIGITS)
2132
        line = RadioSetting("dtmf_settings.inspection",
2133
                            "Inspection", val)
2134
        line.set_apply_callback(apply_dmtf_frame,
2135
                                _mem.dtmf_settings.inspection)
2136
        dtmf_dec_settings.append(line)
2137

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

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

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

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

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

    
2188
        def apply_dmtf_listvalue(setting, obj):
2189
            LOG.debug("Setting value: "+ str(setting.value) + " from list")
2190
            val = str(setting.value)
2191
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2192
            val = LIST_DTMF_SPECIAL_VALUES[index]
2193
            obj.set_value(val)
2194

    
2195
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
2196
        line = RadioSetting(
2197
            "dtmf_settings.groupcode",
2198
            "Group Code",
2199
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2200
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2201
        line.set_apply_callback(apply_dmtf_listvalue,
2202
                                _mem.dtmf_settings.groupcode)
2203
        dtmf_dec_settings.append(line)
2204

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

    
2215
        if self.COLOR_LCD:
2216
            line = RadioSetting(
2217
                "dtmf_settings.resettime",
2218
                "Reset time",
2219
                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
2220
                                      LIST_5TONE_RESET_COLOR[
2221
                                          _mem.dtmf_settings.resettime]))
2222
            dtmf_dec_settings.append(line)
2223
        else:
2224
            line = RadioSetting(
2225
                "dtmf_settings.resettime",
2226
                "Reset time",
2227
                RadioSettingValueList(LIST_5TONE_RESET,
2228
                                      LIST_5TONE_RESET[
2229
                                          _mem.dtmf_settings.resettime]))
2230
            dtmf_dec_settings.append(line)
2231

    
2232
        line = RadioSetting(
2233
            "dtmf_settings.delayproctime",
2234
            "Delay processing time",
2235
            RadioSettingValueList(LIST_DTMF_DELAY,
2236
                                  LIST_DTMF_DELAY[
2237
                                      _mem.dtmf_settings.delayproctime]))
2238
        dtmf_dec_settings.append(line)
2239

    
2240

    
2241
        # 5 Tone Settings
2242
        stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
2243
        codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
2244

    
2245
        group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
2246
        group_5tone.append(stds_5tone)
2247
        group_5tone.append(codes_5tone)
2248

    
2249
        top.append(group_5tone)
2250

    
2251
        def apply_list_value(setting, obj):
2252
            options = setting.value.get_options()
2253
            obj.set_value ( options.index(str(setting.value)) )
2254

    
2255
        _5tone_standards = self._memobj._5tone_std_settings
2256
        i = 0
2257
        for standard in _5tone_standards:
2258
            std_5tone = RadioSettingGroup ("std_5tone_" + str(i), 
2259
                                           LIST_5TONE_STANDARDS[i])
2260
            stds_5tone.append(std_5tone)
2261
 
2262
            period = standard.period
2263
            if period == 255:
2264
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] + 
2265
                          " is not yet configured. Setting to 70ms.")
2266
                period = 5
2267

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

    
2279
            group_tone = standard.group_tone
2280
            if group_tone == 255:
2281
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2282
                          " is not yet configured. Setting to A.")
2283
                group_tone = 10
2284

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

    
2298
            repeat_tone = standard.repeat_tone
2299
            if repeat_tone == 255:
2300
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] + 
2301
                          " is not yet configured. Setting to E.")
2302
                repeat_tone = 14
2303

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

    
2318
        def my_apply_5tonestdlist_value(setting, obj):
2319
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2320
                obj.set_value(0xFF)
2321
            else:
2322
                obj.set_value( LIST_5TONE_STANDARDS.
2323
                              index(str(setting.value)) )
2324

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

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

    
2355
        def frame2string(frame):
2356
            frameString = ""
2357
            for digit in frame:
2358
                if digit != 255:
2359
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2360
            return frameString
2361

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

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

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

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

    
2406
        _5_tone_decode1 = RadioSetting(
2407
            "_5tone_settings._5tone_decode_call_frame1",
2408
            "5 Tone decode call Frame 1",
2409
            RadioSettingValueBoolean(
2410
                _mem._5tone_settings._5tone_decode_call_frame1))
2411
        group_5tone.append(_5_tone_decode1)
2412

    
2413
        _5_tone_decode2 = RadioSetting(
2414
            "_5tone_settings._5tone_decode_call_frame2",
2415
            "5 Tone decode call Frame 2",
2416
            RadioSettingValueBoolean(
2417
                _mem._5tone_settings._5tone_decode_call_frame2))
2418
        group_5tone.append(_5_tone_decode2)
2419

    
2420
        _5_tone_decode3 = RadioSetting(
2421
            "_5tone_settings._5tone_decode_call_frame3",
2422
            "5 Tone decode call Frame 3",
2423
            RadioSettingValueBoolean(
2424
                _mem._5tone_settings._5tone_decode_call_frame3))
2425
        group_5tone.append(_5_tone_decode3)
2426

    
2427
        _5_tone_decode_disp1 = RadioSetting(
2428
            "_5tone_settings._5tone_decode_disp_frame1",
2429
            "5 Tone decode disp Frame 1",
2430
            RadioSettingValueBoolean(
2431
                _mem._5tone_settings._5tone_decode_disp_frame1))
2432
        group_5tone.append(_5_tone_decode_disp1)
2433

    
2434
        _5_tone_decode_disp2 = RadioSetting(
2435
            "_5tone_settings._5tone_decode_disp_frame2",
2436
            "5 Tone decode disp Frame 2",
2437
            RadioSettingValueBoolean(
2438
                _mem._5tone_settings._5tone_decode_disp_frame2))
2439
        group_5tone.append(_5_tone_decode_disp2)
2440

    
2441
        _5_tone_decode_disp3 = RadioSetting(
2442
            "_5tone_settings._5tone_decode_disp_frame3",
2443
            "5 Tone decode disp Frame 3",
2444
            RadioSettingValueBoolean(
2445
                _mem._5tone_settings._5tone_decode_disp_frame3))
2446
        group_5tone.append(_5_tone_decode_disp3)
2447

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

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

    
2476
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2477
        if _5tone_delay2 == 255:
2478
            _5tone_delay2 = 20
2479
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2480

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

    
2491
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2492
        if _5tone_delay3 == 255:
2493
            _5tone_delay3 = 20
2494
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2495

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

    
2506
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2507
        if ext_length == 255:
2508
            ext_length = 0
2509
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2510

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

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

    
2539
        # 2 Tone
2540
        encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
2541
        decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
2542

    
2543
        top.append(encode_2tone)
2544
        top.append(decode_2tone)
2545

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

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

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

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

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

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

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

    
2597
        def apply_2tone_freq(setting, obj):
2598
            val = int(setting.value)
2599
            if (val == 0) or (val == 65535):
2600
                obj.set_value(65535)
2601
            else:
2602
                obj.set_value(val)
2603

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

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

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

    
2630
            i = i + 1
2631

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

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

    
2653
            if (val == 0):
2654
                val = 65535
2655
                derived_val = 65535
2656
            else:
2657
                derived_val = int(round(2304000.0/val))
2658

    
2659
            obj[frqname].set_value( val )
2660
            obj[derivedname].set_value( derived_val )
2661

    
2662
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " 
2663
                      + derivedname + ": " + str(derived_val))
2664

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

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

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

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

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

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

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

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

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

    
2747
            i = i + 1
2748

    
2749
        return top
2750

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

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

    
2788
    @classmethod
2789
    def match_model(cls, filedata, filename):
2790
        match_size = False
2791
        match_model = False
2792

    
2793
        # testing the file data size
2794
        if len(filedata) == MEM_SIZE:
2795
            match_size = True
2796

    
2797
        # testing the firmware model fingerprint
2798
        match_model = model_match(cls, filedata)
2799

    
2800
        if match_size and match_model:
2801
            return True
2802
        else:
2803
            return False
2804

    
2805

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

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

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

    
2895
#seekto 0x0EC0;
2896
struct {
2897
  char line1[6];
2898
  char line2[6];
2899
} poweron_msg;
2900

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

    
2918
#seekto 0x0F00;
2919
struct {
2920
  struct settings_vfo a;
2921
  struct settings_vfo b;
2922
} vfo;
2923

    
2924
#seekto 0x1000;
2925
struct {
2926
  char name[6];
2927
  u8 unknown1[10];
2928
} names[200];
2929

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

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

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

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

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

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

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

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

    
3060
#seekto 0x3C90;
3061
struct {
3062
  u8 vhf_low[3];
3063
  u8 vhf_high[3];
3064
  u8 uhf_low[3];
3065
  u8 uhf_high[3];
3066
} ranges;
3067

    
3068
// the UV-2501+220 & KT8900R has different zones for storing ranges
3069

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

    
3084
#seekto 0x3F70;
3085
struct {
3086
  char fp[6];
3087
} fingerprint;
3088

    
3089
"""
3090

    
3091

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

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

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

    
3112
        # the normal dual bands
3113
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3114
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3115

    
3116
        # DEBUG
3117
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3118
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3119

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

    
3126
        # set the class with the real data
3127
        self._vhf_range = vhf
3128
        self._uhf_range = uhf
3129

    
3130
    def process_mmap(self):
3131
        """Process the mem map into the mem object"""
3132

    
3133
        # Get it
3134
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3135

    
3136
        # load specific parameters from the radio image
3137
        self.set_options()
3138

    
3139

    
3140
# Declaring Aliases (Clones of the real radios)
3141
class JT2705M(chirp_common.Alias):
3142
    VENDOR = "Jetstream"
3143
    MODEL = "JT2705M"
3144

    
3145

    
3146
class JT6188Mini(chirp_common.Alias):
3147
    VENDOR = "Juentai"
3148
    MODEL = "JT-6188 Mini"
3149

    
3150

    
3151
class JT6188Plus(chirp_common.Alias):
3152
    VENDOR = "Juentai"
3153
    MODEL = "JT-6188 Plus"
3154

    
3155

    
3156
class SSGT890(chirp_common.Alias):
3157
    VENDOR = "Sainsonic"
3158
    MODEL = "GT-890"
3159

    
3160

    
3161
class ZastoneMP300(chirp_common.Alias):
3162
    VENDOR = "Zastone"
3163
    MODEL = "MP-300"
3164

    
3165

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

    
3176

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

    
3189

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

    
3202

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

    
3213

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

    
3226
# Please note that there is a version of this radios that is a clone of the
3227
# Waccom Mini8900, maybe an early version?
3228
@directory.register
3229
class KT9800(BTech):
3230
    """QYT KT8900"""
3231
    VENDOR = "QYT"
3232
    MODEL = "KT8900"
3233
    _vhf_range = (136000000, 175000000)
3234
    _uhf_range = (400000000, 481000000)
3235
    _magic = MSTRING_KT8900
3236
    _fileid = [KT8900_fp,
3237
               KT8900_fp1,
3238
               KT8900_fp2,
3239
               KT8900_fp3,
3240
               KT8900_fp4,
3241
               KT8900_fp5]
3242
    _id2 = KT8900_id
3243
    # Clones
3244
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3245

    
3246

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

    
3264

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

    
3278

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

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

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

    
3386
struct settings_vfo {
3387
  u8 freq[8];
3388
  u8 offset[6];
3389
  u8 unknown2[2];
3390
  ul16 rxtone;
3391
  ul16 txtone;
3392
  u8 scode;
3393
  u8 spmute;
3394
  u8 optsig;
3395
  u8 scramble;
3396
  u8 wide;
3397
  u8 power;
3398
  u8 shiftd;
3399
  u8 step;
3400
  u8 unknown3[4];
3401
};
3402

    
3403
#seekto 0x0F00;
3404
struct {
3405
  struct settings_vfo a;
3406
  struct settings_vfo b;
3407
  struct settings_vfo c;
3408
  struct settings_vfo d;
3409
} vfo;
3410

    
3411
#seekto 0x0F80;
3412
struct {
3413
  char line1[8];
3414
  char line2[8];
3415
  char line3[8];
3416
  char line4[8];
3417
  char line5[8];
3418
  char line6[8];
3419
  char line7[8];
3420
  char line8[8];
3421
} poweron_msg;
3422

    
3423
#seekto 0x1000;
3424
struct {
3425
  char name[8];
3426
  u8 unknown1[8];
3427
} names[200];
3428

    
3429
#seekto 0x2400;
3430
struct {
3431
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3432
  u8 group_tone;
3433
  u8 repeat_tone;
3434
  u8 unused[13];
3435
} _5tone_std_settings[15];
3436

    
3437
#seekto 0x2500;
3438
struct {
3439
  u8 frame1[5];
3440
  u8 frame2[5];
3441
  u8 frame3[5];
3442
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3443
} _5tone_codes[15];
3444

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

    
3467
#seekto 0x2900;
3468
struct {
3469
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3470
} dtmf_codes[15];
3471

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

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

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

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

    
3570
#seekto 0x3F70;
3571
struct {
3572
  char fp[6];
3573
} fingerprint;
3574

    
3575
"""
3576

    
3577

    
3578
class BTechColor(BTechMobileCommon):
3579
    """BTECH's Color LCD Mobile and alike radios"""
3580
    COLOR_LCD = True
3581
    NAME_LENGTH = 8
3582

    
3583
    def process_mmap(self):
3584
        """Process the mem map into the mem object"""
3585

    
3586
        # Get it
3587
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3588

    
3589
        # load specific parameters from the radio image
3590
        self.set_options()
3591

    
3592
    def set_options(self):
3593
        """This is to read the options from the image and set it in the
3594
        environment, for now just the limits of the freqs in the VHF/UHF
3595
        ranges"""
3596

    
3597
        # setting the correct ranges for each radio type
3598
        ranges = self._memobj.ranges
3599

    
3600
        # the normal dual bands
3601
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3602
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3603

    
3604
        # DEBUG
3605
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3606
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3607

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

    
3615
            # 350Mhz band
3616
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
3617
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
3618
            self._350_range = uhf2
3619

    
3620
        # set the class with the real data
3621
        self._vhf_range = vhf
3622
        self._uhf_range = uhf
3623
    
3624

    
3625
# Declaring Aliases (Clones of the real radios)
3626
class SKT8900D(chirp_common.Alias):
3627
    VENDOR = "Surecom"
3628
    MODEL = "S-KT8900D"
3629

    
3630

    
3631
class QB25(chirp_common.Alias):
3632
    VENDOR = "Radioddity"
3633
    MODEL = "QB25"
3634

    
3635

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

    
3647

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

    
3660

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

    
3673

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

    
3689

    
3690
@directory.register
3691
class KT8900D(BTechColor):
3692
    """QYT KT8900D"""
3693
    VENDOR = "QYT"
3694
    MODEL = "KT8900D"
3695
    BANDS = 2
3696
    _vhf_range = (136000000, 175000000)
3697
    _uhf_range = (400000000, 481000000)
3698
    _magic = MSTRING_KT8900D
3699
    _fileid = [KT8900D_fp, ]
    (1-1/1)