Project

General

Profile

New Model #4269 » btech_kt8900d_test5.py

address "ringt" problem - Jim Unroe, 02/10/2017 03:36 AM

 
1
# Copyright 2016-2017:
2
# * Pavel Milanes CO7WT, <pavelmc@gmail.com>
3
# * Jim Unroe KC9HI, <rock.unroe@gmail.com>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17

    
18
import time
19
import struct
20
import logging
21

    
22
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_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
108
LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
109
                    "C", "D", "#", "*"]
110
LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
111
                    0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B ]
112
LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
113
LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
114
LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
115
CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
116
LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
117
                  "B-A", "B-C", "B-D",
118
                  "C-A", "C-B", "C-D",
119
                  "D-A", "D-B", "D-C"]
120
LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
121

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

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

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

    
134

    
135
##### ID strings #####################################################
136

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

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

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

    
169
# special var to know when we found a BTECH Gen 3
170
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
171

    
172

    
173
# WACCOM Mini-8900
174
MINI8900_fp = "M28854"
175

    
176

    
177
# QYT KT-UV980
178
KTUV980_fp = "H28854"
179

    
180
# QYT KT8900
181
KT8900_fp = "M29154"
182
# New generations KT8900
183
KT8900_fp1 = "M2C234"
184
KT8900_fp2 = "M2G1F4"
185
KT8900_fp3 = "M2G2F4"
186
KT8900_fp4 = "M2G304"
187
KT8900_fp5 = "M2G314"
188
# this radio has an extra ID
189
KT8900_id = "      303688"
190

    
191
# KT8900R
192
KT8900R_fp = "M3G1F4"
193
# Second Generation
194
KT8900R_fp1 = "M3G214"
195
# another model
196
KT8900R_fp2 = "M3C234"
197
# another model G4?
198
KT8900R_fp3 = "M39164"
199
# another model
200
KT8900R_fp4 = "M3G314"
201
# this radio has an extra ID
202
KT8900R_id = "280528"
203

    
204
# KT7900D
205
KT7900D_fp = "VC4004"
206

    
207
# KT8900D
208
KT8900D_fp = "VC2002"
209

    
210
# LUITON LT-588UV
211
LT588UV_fp = "V2G1F4"
212
# Added by rstrickoff gen 2 id
213
LT588UV_fp1 = "V2G214"
214

    
215

    
216
#### MAGICS
217
# for the Waccom Mini-8900
218
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
219
# for the B-TECH UV-2501+220 (including pre production ones)
220
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
221
# for the QYT KT8900 & R
222
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
223
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
224
# magic string for all other models
225
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
226
# for the QYT KT8900D
227
MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
228

    
229

    
230
def _clean_buffer(radio):
231
    """Cleaning the read serial buffer, hard timeout to survive an infinite
232
    data stream"""
233

    
234
    # touching the serial timeout to optimize the flushing
235
    # restored at the end to the default value
236
    radio.pipe.timeout = 0.1
237
    dump = "1"
238
    datacount = 0
239

    
240
    try:
241
        while len(dump) > 0:
242
            dump = radio.pipe.read(100)
243
            datacount += len(dump)
244
            # hard limit to survive a infinite serial data stream
245
            # 5 times bigger than a normal rx block (69 bytes)
246
            if datacount > 345:
247
                seriale = "Please check your serial port selection."
248
                raise errors.RadioError(seriale)
249

    
250
        # restore the default serial timeout
251
        radio.pipe.timeout = STIMEOUT
252

    
253
    except Exception:
254
        raise errors.RadioError("Unknown error cleaning the serial buffer")
255

    
256

    
257
def _rawrecv(radio, amount):
258
    """Raw read from the radio device, less intensive way"""
259

    
260
    data = ""
261

    
262
    try:
263
        data = radio.pipe.read(amount)
264

    
265
        # DEBUG
266
        if debug is True:
267
            LOG.debug("<== (%d) bytes:\n\n%s" %
268
                      (len(data), util.hexprint(data)))
269

    
270
        # fail if no data is received
271
        if len(data) == 0:
272
            raise errors.RadioError("No data received from radio")
273

    
274
        # notice on the logs if short
275
        if len(data) < amount:
276
            LOG.warn("Short reading %d bytes from the %d requested." %
277
                     (len(data), amount))
278

    
279
    except:
280
        raise errors.RadioError("Error reading data from radio")
281

    
282
    return data
283

    
284

    
285
def _send(radio, data):
286
    """Send data to the radio device"""
287

    
288
    try:
289
        for byte in data:
290
            radio.pipe.write(byte)
291
            # Some OS (mainly Linux ones) are too fast on the serial and
292
            # get the MCU inside the radio stuck in the early stages, this
293
            # hits some models more than others.
294
            #
295
            # To cope with that we introduce a delay on the writes.
296
            # Many option have been tested (delaying only after error occures, 
297
            # after short reads, only for linux, ...)
298
            # Finally, a static delay was chosen as simplest of all solutions
299
            # (Michael Wagner, OE4AMW)
300
            # (for details, see issue 3993)
301
            sleep(0.002)
302

    
303
        # DEBUG
304
        if debug is True:
305
            LOG.debug("==> (%d) bytes:\n\n%s" %
306
                      (len(data), util.hexprint(data)))
307
    except:
308
        raise errors.RadioError("Error sending data to radio")
309

    
310

    
311
def _make_frame(cmd, addr, length, data=""):
312
    """Pack the info in the headder format"""
313
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
314
    # add the data if set
315
    if len(data) != 0:
316
        frame += data
317

    
318
    return frame
319

    
320

    
321
def _recv(radio, addr):
322
    """Get data from the radio all at once to lower syscalls load"""
323

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

    
327
    # get the whole block
328
    block = _rawrecv(radio, BLOCK_SIZE + 5)
329

    
330
    # basic check
331
    if len(block) < (BLOCK_SIZE + 5):
332
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
333

    
334
    # checking for the ack
335
    if block[0] != ACK_CMD:
336
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
337

    
338
    # header validation
339
    c, a, l = struct.unpack(">BHB", block[1:5])
340
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
341
        LOG.debug("Invalid header for block 0x%04x" % addr)
342
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
343
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
344

    
345
    # return the data
346
    return block[5:]
347

    
348

    
349
def _start_clone_mode(radio, status):
350
    """Put the radio in clone mode and get the ident string, 3 tries"""
351

    
352
    # cleaning the serial buffer
353
    _clean_buffer(radio)
354

    
355
    # prep the data to show in the UI
356
    status.cur = 0
357
    status.msg = "Identifying the radio..."
358
    status.max = 3
359
    radio.status_fn(status)
360

    
361
    try:
362
        for a in range(0, status.max):
363
            # Update the UI
364
            status.cur = a + 1
365
            radio.status_fn(status)
366

    
367
            # send the magic word
368
            _send(radio, radio._magic)
369

    
370
            # Now you get a x06 of ACK if all goes well
371
            ack = radio.pipe.read(1)
372

    
373
            if ack == "\x06":
374
                # DEBUG
375
                LOG.info("Magic ACK received")
376
                status.cur = status.max
377
                radio.status_fn(status)
378

    
379
                return True
380

    
381
        return False
382

    
383
    except errors.RadioError:
384
        raise
385
    except Exception, e:
386
        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
387

    
388

    
389
def _do_ident(radio, status, upload=False):
390
    """Put the radio in PROGRAM mode & identify it"""
391
    #  set the serial discipline
392
    radio.pipe.baudrate = 9600
393
    radio.pipe.parity = "N"
394

    
395
    # open the radio into program mode
396
    if _start_clone_mode(radio, status) is False:
397
        msg = "Radio did not enter clone mode"
398
        # warning about old versions of QYT KT8900
399
        if radio.MODEL == "KT8900":
400
            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
401
            msg += " known variant of this radios that is a clone of it."
402
        raise errors.RadioError(msg)
403

    
404
    # Ok, get the ident string
405
    ident = _rawrecv(radio, 49)
406

    
407
    # basic check for the ident
408
    if len(ident) != 49:
409
        raise errors.RadioError("Radio send a short ident block.")
410

    
411
    # check if ident is OK
412
    itis = False
413
    for fp in radio._fileid:
414
        if fp in ident:
415
            # got it!
416
            itis = True
417
            # checking if we are dealing with a Gen 3 BTECH
418
            if radio.VENDOR == "BTECH" and fp in BTECH3:
419
                radio.btech3 = True
420

    
421
            break
422

    
423
    if itis is False:
424
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
425
        raise errors.RadioError("Radio identification failed.")
426

    
427
    # some radios needs a extra read and check for a code on it, this ones
428
    # has the check value in the _id2 var, others simply False
429
    if radio._id2 is not False:
430
        # lower the timeout here as this radios are reseting due to timeout
431
        radio.pipe.timeout = 0.05
432

    
433
        # query & receive the extra ID
434
        _send(radio, _make_frame("S", 0x3DF0, 16))
435
        id2 = _rawrecv(radio, 21)
436

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

    
444
        # ok, the correct string must be in the received data
445
        if radio._id2 not in id2:
446
            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
447
                      (radio.MODEL, util.hexprint(id2)))
448
            raise errors.RadioError("The extra ID is wrong, aborting.")
449

    
450
        # this radios need a extra request/answer here on the upload
451
        # the amount of data received depends of the radio type
452
        #
453
        # also the first block of TX must no have the ACK at the beginning
454
        # see _upload for this.
455
        if upload is True:
456
            # send an ACK
457
            _send(radio, ACK_CMD)
458

    
459
            # the amount of data depend on the radio, so far we have two radios
460
            # reading two bytes with an ACK at the end and just ONE with just
461
            # one byte (QYT KT8900)
462
            # the JT-6188 appears a clone of the last, but reads TWO bytes.
463
            #
464
            # we will read two bytes with a custom timeout to not penalize the
465
            # users for this.
466
            #
467
            # we just check for a response and last byte being a ACK, that is
468
            # the common stone for all radios (3 so far)
469
            ack = _rawrecv(radio, 2)
470

    
471
            # checking
472
            if len(ack) == 0 or ack[-1:] != ACK_CMD:
473
                raise errors.RadioError("Radio didn't ACK the upload")
474

    
475
            # restore the default serial timeout
476
            radio.pipe.timeout = STIMEOUT
477

    
478
    # DEBUG
479
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
480

    
481
    return True
482

    
483

    
484
def _download(radio):
485
    """Get the memory map"""
486

    
487
    # UI progress
488
    status = chirp_common.Status()
489

    
490
    # put radio in program mode and identify it
491
    _do_ident(radio, status)
492

    
493
    # the models that doesn't have the extra ID have to make a dummy read here
494
    if radio._id2 is False:
495
        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
496
        discard = _rawrecv(radio, BLOCK_SIZE + 5)
497

    
498
        if debug is True:
499
            LOG.info("Dummy first block read done, got this:\n\n %s",
500
                     util.hexprint(discard))
501

    
502
    # reset the progress bar in the UI
503
    status.max = MEM_SIZE / BLOCK_SIZE
504
    status.msg = "Cloning from radio..."
505
    status.cur = 0
506
    radio.status_fn(status)
507

    
508
    # cleaning the serial buffer
509
    _clean_buffer(radio)
510

    
511
    data = ""
512
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
513
        # sending the read request
514
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
515

    
516
        # read
517
        d = _recv(radio, addr)
518

    
519
        # aggregate the data
520
        data += d
521

    
522
        # UI Update
523
        status.cur = addr / BLOCK_SIZE
524
        status.msg = "Cloning from radio..."
525
        radio.status_fn(status)
526

    
527
    return data
528

    
529

    
530
def _upload(radio):
531
    """Upload procedure"""
532

    
533
    # The UPLOAD mem is restricted to lower than 0x3100,
534
    # so we will overide that here localy
535
    MEM_SIZE = 0x3100
536

    
537
    # UI progress
538
    status = chirp_common.Status()
539

    
540
    # put radio in program mode and identify it
541
    _do_ident(radio, status, True)
542

    
543
    # get the data to upload to radio
544
    data = radio.get_mmap()
545

    
546
    # Reset the UI progress
547
    status.max = MEM_SIZE / TX_BLOCK_SIZE
548
    status.cur = 0
549
    status.msg = "Cloning to radio..."
550
    radio.status_fn(status)
551

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

    
556
    # cleaning the serial buffer
557
    _clean_buffer(radio)
558

    
559
    # the fun start here
560
    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
561
        # getting the block of data to send
562
        d = data[addr:addr + TX_BLOCK_SIZE]
563

    
564
        # build the frame to send
565
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
566

    
567
        # first block must not send the ACK at the beginning for the
568
        # ones that has the extra id, since this have to do a extra step
569
        if addr == 0 and radio._id2 is not False:
570
            frame = frame[1:]
571

    
572
        # send the frame
573
        _send(radio, frame)
574

    
575
        # receiving the response
576
        ack = _rawrecv(radio, 1)
577

    
578
        # basic check
579
        if len(ack) != 1:
580
            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
581

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

    
585
         # UI Update
586
        status.cur = addr / TX_BLOCK_SIZE
587
        status.msg = "Cloning to radio..."
588
        radio.status_fn(status)
589

    
590

    
591
def model_match(cls, data):
592
    """Match the opened/downloaded image to the correct version"""
593
    rid = data[0x3f70:0x3f76]
594

    
595
    if rid in cls._fileid:
596
        return True
597

    
598
    return False
599

    
600

    
601
def _decode_ranges(low, high):
602
    """Unpack the data in the ranges zones in the memmap and return
603
    a tuple with the integer corresponding to the Mhz it means"""
604
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
605
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
606
    ilow *= 1000000
607
    ihigh *= 1000000
608

    
609
    return (ilow, ihigh)
610

    
611

    
612
def _split(rf, f1, f2):
613
    """Returns False if the two freqs are in the same band (no split)
614
    or True otherwise"""
615

    
616
    # determine if the two freqs are in the same band
617
    for low, high in rf.valid_bands:
618
        if f1 >= low and f1 <= high and \
619
                f2 >= low and f2 <= high:
620
            # if the two freqs are on the same Band this is not a split
621
            return False
622

    
623
    # if you get here is because the freq pairs are split
624
    return False
625

    
626

    
627
class BTechMobileCommon(chirp_common.CloneModeRadio,
628
                        chirp_common.ExperimentalRadio):
629
    """BTECH's UV-5001 and alike radios"""
630
    VENDOR = "BTECH"
631
    MODEL = ""
632
    IDENT = ""
633
    BANDS = 2
634
    COLOR_LCD = False
635
    NAME_LENGTH = 6
636
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
637
                     chirp_common.PowerLevel("Low", watts=10)]
638
    _vhf_range = (130000000, 180000000)
639
    _220_range = (200000000, 271000000)
640
    _uhf_range = (400000000, 521000000)
641
    _350_range = (350000000, 391000000)
642
    _upper = 199
643
    _magic = MSTRING
644
    _fileid = None
645
    _id2 = False
646
    btech3 = False
647

    
648
    @classmethod
649
    def get_prompts(cls):
650
        rp = chirp_common.RadioPrompts()
651
        rp.experimental = \
652
            ('This driver is experimental.\n'
653
             '\n'
654
             'Please keep a copy of your memories with the original software '
655
             'if you treasure them, this driver is new and may contain'
656
             ' bugs.\n'
657
             '\n'
658
             )
659
        rp.pre_download = _(dedent("""\
660
            Follow these instructions to download your info:
661

    
662
            1 - Turn off your radio
663
            2 - Connect your interface cable
664
            3 - Turn on your radio
665
            4 - Do the download of your radio data
666

    
667
            """))
668
        rp.pre_upload = _(dedent("""\
669
            Follow these instructions to upload your info:
670

    
671
            1 - Turn off your radio
672
            2 - Connect your interface cable
673
            3 - Turn on your radio
674
            4 - Do the upload of your radio data
675

    
676
            """))
677
        return rp
678

    
679
    def get_features(self):
680
        """Get the radio's features"""
681

    
682
        # we will use the following var as global
683
        global POWER_LEVELS
684

    
685
        rf = chirp_common.RadioFeatures()
686
        rf.has_settings = True
687
        rf.has_bank = False
688
        rf.has_tuning_step = False
689
        rf.can_odd_split = True
690
        rf.has_name = True
691
        rf.has_offset = True
692
        rf.has_mode = True
693
        rf.has_dtcs = True
694
        rf.has_rx_dtcs = True
695
        rf.has_dtcs_polarity = True
696
        rf.has_ctone = True
697
        rf.has_cross = True
698
        rf.valid_modes = MODES
699
        rf.valid_characters = VALID_CHARS
700
        rf.valid_name_length = self.NAME_LENGTH
701
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
702
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
703
        rf.valid_cross_modes = [
704
            "Tone->Tone",
705
            "DTCS->",
706
            "->DTCS",
707
            "Tone->DTCS",
708
            "DTCS->Tone",
709
            "->Tone",
710
            "DTCS->DTCS"]
711
        rf.valid_skips = SKIP_VALUES
712
        rf.valid_dtcs_codes = DTCS
713
        rf.memory_bounds = (0, self._upper)
714

    
715
        # power levels
716
        POWER_LEVELS = self._power_levels
717
        rf.valid_power_levels = POWER_LEVELS
718

    
719
        # normal dual bands
720
        rf.valid_bands = [self._vhf_range, self._uhf_range]
721

    
722
        # 220 band
723
        if self.BANDS == 3 or self.BANDS == 4:
724
            rf.valid_bands.append(self._220_range)
725

    
726
        # 350 band
727
        if self.BANDS == 4:
728
            rf.valid_bands.append(self._350_range)
729

    
730
        return rf
731

    
732
    def sync_in(self):
733
        """Download from radio"""
734
        data = _download(self)
735
        self._mmap = memmap.MemoryMap(data)
736
        self.process_mmap()
737

    
738
    def sync_out(self):
739
        """Upload to radio"""
740
        try:
741
            _upload(self)
742
        except errors.RadioError:
743
            raise
744
        except Exception, e:
745
            raise errors.RadioError("Error: %s" % e)
746

    
747
    def get_raw_memory(self, number):
748
        return repr(self._memobj.memory[number])
749

    
750
    def _decode_tone(self, val):
751
        """Parse the tone data to decode from mem, it returns:
752
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
753
        pol = None
754

    
755
        if val in [0, 65535]:
756
            return '', None, None
757
        elif val > 0x0258:
758
            a = val / 10.0
759
            return 'Tone', a, pol
760
        else:
761
            if val > 0x69:
762
                index = val - 0x6A
763
                pol = "R"
764
            else:
765
                index = val - 1
766
                pol = "N"
767

    
768
            tone = DTCS[index]
769
            return 'DTCS', tone, pol
770

    
771
    def _encode_tone(self, memval, mode, val, pol):
772
        """Parse the tone data to encode from UI to mem"""
773
        if mode == '' or mode is None:
774
            memval.set_raw("\x00\x00")
775
        elif mode == 'Tone':
776
            memval.set_value(val * 10)
777
        elif mode == 'DTCS':
778
            # detect the index in the DTCS list
779
            try:
780
                index = DTCS.index(val)
781
                if pol == "N":
782
                    index += 1
783
                else:
784
                    index += 0x6A
785
                memval.set_value(index)
786
            except:
787
                msg = "Digital Tone '%d' is not supported" % value
788
                LOG.error(msg)
789
                raise errors.RadioError(msg)
790
        else:
791
            msg = "Internal error: invalid mode '%s'" % mode
792
            LOG.error(msg)
793
            raise errors.InvalidDataError(msg)
794

    
795
    def get_memory(self, number):
796
        """Get the mem representation from the radio image"""
797
        _mem = self._memobj.memory[number]
798
        _names = self._memobj.names[number]
799

    
800
        # Create a high-level memory object to return to the UI
801
        mem = chirp_common.Memory()
802

    
803
        # Memory number
804
        mem.number = number
805

    
806
        if _mem.get_raw()[0] == "\xFF":
807
            mem.empty = True
808
            return mem
809

    
810
        # Freq and offset
811
        mem.freq = int(_mem.rxfreq) * 10
812
        # tx freq can be blank
813
        if _mem.get_raw()[4] == "\xFF":
814
            # TX freq not set
815
            mem.offset = 0
816
            mem.duplex = "off"
817
        else:
818
            # TX freq set
819
            offset = (int(_mem.txfreq) * 10) - mem.freq
820
            if offset != 0:
821
                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
822
                    mem.duplex = "split"
823
                    mem.offset = int(_mem.txfreq) * 10
824
                elif offset < 0:
825
                    mem.offset = abs(offset)
826
                    mem.duplex = "-"
827
                elif offset > 0:
828
                    mem.offset = offset
829
                    mem.duplex = "+"
830
            else:
831
                mem.offset = 0
832

    
833
        # name TAG of the channel
834
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
835

    
836
        # power
837
        mem.power = POWER_LEVELS[int(_mem.power)]
838

    
839
        # wide/narrow
840
        mem.mode = MODES[int(_mem.wide)]
841

    
842
        # skip
843
        mem.skip = SKIP_VALUES[_mem.add]
844

    
845
        # tone data
846
        rxtone = txtone = None
847
        txtone = self._decode_tone(_mem.txtone)
848
        rxtone = self._decode_tone(_mem.rxtone)
849
        chirp_common.split_tone_decode(mem, txtone, rxtone)
850

    
851
        # Extra
852
        mem.extra = RadioSettingGroup("extra", "Extra")
853

    
854
        scramble = RadioSetting("scramble", "Scramble",
855
                                RadioSettingValueBoolean(bool(
856
                                    _mem.scramble)))
857
        mem.extra.append(scramble)
858

    
859
        bcl = RadioSetting("bcl", "Busy channel lockout",
860
                           RadioSettingValueBoolean(bool(_mem.bcl)))
861
        mem.extra.append(bcl)
862

    
863
        pttid = RadioSetting("pttid", "PTT ID",
864
                             RadioSettingValueList(PTTID_LIST,
865
                                                   PTTID_LIST[_mem.pttid]))
866
        mem.extra.append(pttid)
867

    
868
        # validating scode
869
        scode = _mem.scode if _mem.scode != 15 else 0
870
        pttidcode = RadioSetting("scode", "PTT ID signal code",
871
                                 RadioSettingValueList(
872
                                     PTTIDCODE_LIST,
873
                                     PTTIDCODE_LIST[scode]))
874
        mem.extra.append(pttidcode)
875

    
876
        optsig = RadioSetting("optsig", "Optional signaling",
877
                              RadioSettingValueList(
878
                                  OPTSIG_LIST,
879
                                  OPTSIG_LIST[_mem.optsig]))
880
        mem.extra.append(optsig)
881

    
882
        spmute = RadioSetting("spmute", "Speaker mute",
883
                              RadioSettingValueList(
884
                                  SPMUTE_LIST,
885
                                  SPMUTE_LIST[_mem.spmute]))
886
        mem.extra.append(spmute)
887

    
888
        return mem
889

    
890
    def set_memory(self, mem):
891
        """Set the memory data in the eeprom img from the UI"""
892
        # get the eprom representation of this channel
893
        _mem = self._memobj.memory[mem.number]
894
        _names = self._memobj.names[mem.number]
895

    
896
        mem_was_empty = False
897
        # same method as used in get_memory for determining if mem is empty
898
        # doing this BEFORE overwriting it with new values ...
899
        if _mem.get_raw()[0] == "\xFF":
900
            LOG.debug("This mem was empty before")
901
            mem_was_empty = True
902
        
903
        # if empty memmory
904
        if mem.empty:
905
            # the channel itself
906
            _mem.set_raw("\xFF" * 16)
907
            # the name tag
908
            _names.set_raw("\xFF" * 16)
909
            return
910

    
911
        # frequency
912
        _mem.rxfreq = mem.freq / 10
913

    
914
        # duplex
915
        if mem.duplex == "+":
916
            _mem.txfreq = (mem.freq + mem.offset) / 10
917
        elif mem.duplex == "-":
918
            _mem.txfreq = (mem.freq - mem.offset) / 10
919
        elif mem.duplex == "off":
920
            for i in _mem.txfreq:
921
                i.set_raw("\xFF")
922
        elif mem.duplex == "split":
923
            _mem.txfreq = mem.offset / 10
924
        else:
925
            _mem.txfreq = mem.freq / 10
926

    
927
        # tone data
928
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
929
            chirp_common.split_tone_encode(mem)
930
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
931
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
932

    
933
        # name TAG of the channel
934
        if len(mem.name) < self.NAME_LENGTH:
935
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
936
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
937
        _names.name = str(mem.name).replace(" ", "\xFF")
938

    
939
        # power, # default power level is high
940
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
941

    
942
        # wide/narrow
943
        _mem.wide = MODES.index(mem.mode)
944

    
945
        # scan add property
946
        _mem.add = SKIP_VALUES.index(mem.skip)
947

    
948
        # reseting unknowns, this have to be set by hand
949
        _mem.unknown0 = 0
950
        _mem.unknown1 = 0
951
        _mem.unknown2 = 0
952
        _mem.unknown3 = 0
953
        _mem.unknown4 = 0
954
        _mem.unknown5 = 0
955
        _mem.unknown6 = 0
956

    
957
        # extra settings
958
        if len(mem.extra) > 0:
959
            # there are setting, parse
960
            LOG.debug("Extra-Setting supplied. Setting them.")
961
            for setting in mem.extra:
962
                setattr(_mem, setting.get_name(), setting.value)
963
        else:
964
            if mem.empty:
965
                LOG.debug("New mem is empty.")
966
            else:
967
                LOG.debug("New mem is NOT empty")
968
                # set extra-settings to default ONLY when apreviously empty or
969
                # deleted memory was edited to prevent errors such as #4121
970
                if mem_was_empty :
971
                    LOG.debug("old mem was empty. Setting default for extras.")
972
                    _mem.spmute = 0
973
                    _mem.optsig = 0
974
                    _mem.scramble = 0
975
                    _mem.bcl = 0
976
                    _mem.pttid = 0
977
                    _mem.scode = 0
978

    
979
        return mem
980

    
981
    def get_settings(self):
982
        """Translate the bit in the mem_struct into settings in the UI"""
983
        _mem = self._memobj
984
        basic = RadioSettingGroup("basic", "Basic Settings")
985
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
986
        other = RadioSettingGroup("other", "Other Settings")
987
        work = RadioSettingGroup("work", "Work Mode Settings")
988
        top = RadioSettings(basic, advanced, other, work)
989

    
990
        # Basic
991
        if self.COLOR_LCD:
992
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
993
                               RadioSettingValueList(
994
                                   LIST_TMR,
995
                                   LIST_TMR[_mem.settings.tmr]))
996
            basic.append(tmr)
997
        else:
998
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
999
                               RadioSettingValueBoolean(_mem.settings.tdr))
1000
            basic.append(tdr)
1001

    
1002
        sql = RadioSetting("settings.sql", "Squelch level",
1003
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1004
        basic.append(sql)
1005

    
1006
        tot = RadioSetting("settings.tot", "Time out timer",
1007
                           RadioSettingValueList(
1008
                               LIST_TOT,
1009
                               LIST_TOT[_mem.settings.tot]))
1010
        basic.append(tot)
1011

    
1012
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1013
            apo = RadioSetting("settings.apo", "Auto power off timer",
1014
                               RadioSettingValueList(
1015
                                   LIST_APO,
1016
                                   LIST_APO[_mem.settings.apo]))
1017
            basic.append(apo)
1018
        else:
1019
            toa = RadioSetting("settings.apo", "Time out alert timer",
1020
                               RadioSettingValueList(
1021
                                   LIST_OFF1TO10, 
1022
                                   LIST_OFF1TO10[_mem.settings.apo]))
1023
            basic.append(toa)
1024

    
1025
        abr = RadioSetting("settings.abr", "Backlight timer",
1026
                           RadioSettingValueList(
1027
                               LIST_OFF1TO50,
1028
                               LIST_OFF1TO50[_mem.settings.abr]))
1029
        basic.append(abr)
1030

    
1031
        beep = RadioSetting("settings.beep", "Key beep",
1032
                            RadioSettingValueBoolean(_mem.settings.beep))
1033
        basic.append(beep)
1034

    
1035
        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1036
                              RadioSettingValueList(
1037
                                  LIST_DTMFST,
1038
                                  LIST_DTMFST[_mem.settings.dtmfst]))
1039
        basic.append(dtmfst)
1040

    
1041
        if not self.COLOR_LCD:
1042
            prisc = RadioSetting("settings.prisc", "Priority scan",
1043
                                 RadioSettingValueBoolean(
1044
                                     _mem.settings.prisc))
1045
            basic.append(prisc)
1046

    
1047
            prich = RadioSetting("settings.prich", "Priority channel",
1048
                                 RadioSettingValueInteger(0, 199,
1049
                                     _mem.settings.prich))
1050
            basic.append(prich)
1051

    
1052
        screv = RadioSetting("settings.screv", "Scan resume method",
1053
                             RadioSettingValueList(
1054
                                 LIST_SCREV,
1055
                                 LIST_SCREV[_mem.settings.screv]))
1056
        basic.append(screv)
1057

    
1058
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1059
                             RadioSettingValueInteger(0, 30,
1060
                                 _mem.settings.pttlt))
1061
        basic.append(pttlt)
1062

    
1063
        emctp = RadioSetting("settings.emctp", "Alarm mode",
1064
                             RadioSettingValueList(
1065
                                 LIST_EMCTP,
1066
                                 LIST_EMCTP[_mem.settings.emctp]))
1067
        basic.append(emctp)
1068

    
1069
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1070
                             RadioSettingValueInteger(0, 199,
1071
                                 _mem.settings.emcch))
1072
        basic.append(emcch)
1073

    
1074
        if not self.COLOR_LCD:
1075
            ringt = RadioSetting("settings.ringt", "Ring time",
1076
                                 RadioSettingValueList(
1077
                                     LIST_OFF1TO9,
1078
                                     LIST_OFF1TO9[_mem.settings.ringt]))
1079
            basic.append(ringt)
1080

    
1081
        camdf = RadioSetting("settings.camdf", "Display mode A",
1082
                             RadioSettingValueList(
1083
                                 LIST_MDF,
1084
                                 LIST_MDF[_mem.settings.camdf]))
1085
        basic.append(camdf)
1086

    
1087
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1088
                             RadioSettingValueList(
1089
                                 LIST_MDF,
1090
                                 LIST_MDF[_mem.settings.cbmdf]))
1091
        basic.append(cbmdf)
1092

    
1093
        if self.COLOR_LCD:
1094
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1095
                                 RadioSettingValueList(
1096
                                     LIST_MDF,
1097
                                     LIST_MDF[_mem.settings.ccmdf]))
1098
            basic.append(ccmdf)
1099

    
1100
            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1101
                                 RadioSettingValueList(
1102
                                     LIST_MDF,
1103
                                     LIST_MDF[_mem.settings.cdmdf]))
1104
            basic.append(cdmdf)
1105

    
1106
            langua = RadioSetting("settings.langua", "Language",
1107
                                  RadioSettingValueList(
1108
                                      LIST_LANGUA,
1109
                                      LIST_LANGUA[_mem.settings.langua]))
1110
            basic.append(langua)
1111

    
1112
        if self.VENDOR == "BTECH":
1113
            sync = RadioSetting("settings.sync", "A/B channel sync",
1114
                                RadioSettingValueBoolean(
1115
                                    _mem.settings.sync))
1116
            basic.append(sync)
1117
        else:
1118
            autolk = RadioSetting("settings.sync", "Auto keylock",
1119
                                  RadioSettingValueBoolean(
1120
                                      _mem.settings.sync))
1121
            basic.append(autolk)
1122

    
1123
        if not self.COLOR_LCD:
1124
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1125
                                  RadioSettingValueList(
1126
                                      LIST_PONMSG,
1127
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1128
            basic.append(ponmsg)
1129

    
1130
        if self.COLOR_LCD:
1131
            mainfc = RadioSetting("settings.mainfc", 
1132
                                  "Main LCD foreground color",
1133
                                      RadioSettingValueList(
1134
                                          LIST_COLOR8,
1135
                                          LIST_COLOR8[_mem.settings.mainfc]))
1136
            basic.append(mainfc)
1137

    
1138
            mainbc = RadioSetting("settings.mainbc",
1139
                                  "Main LCD background color",
1140
                                      RadioSettingValueList(
1141
                                          LIST_COLOR8,
1142
                                          LIST_COLOR8[_mem.settings.mainbc]))
1143
            basic.append(mainbc)
1144

    
1145
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1146
                                  RadioSettingValueList(
1147
                                      LIST_COLOR8,
1148
                                      LIST_COLOR8[_mem.settings.menufc]))
1149
            basic.append(menufc)
1150

    
1151
            menubc = RadioSetting("settings.menubc", "Menu background color",
1152
                                  RadioSettingValueList(
1153
                                      LIST_COLOR8,
1154
                                      LIST_COLOR8[_mem.settings.menubc]))
1155
            basic.append(menubc)
1156

    
1157
            stafc = RadioSetting("settings.stafc",
1158
                                 "Top status foreground color",
1159
                                     RadioSettingValueList(
1160
                                         LIST_COLOR8,
1161
                                         LIST_COLOR8[_mem.settings.stafc]))
1162
            basic.append(stafc)
1163

    
1164
            stabc = RadioSetting("settings.stabc",
1165
                                 "Top status background color",
1166
                                     RadioSettingValueList(
1167
                                         LIST_COLOR8,
1168
                                         LIST_COLOR8[_mem.settings.stabc]))
1169
            basic.append(stabc)
1170

    
1171
            sigfc = RadioSetting("settings.sigfc",
1172
                                 "Bottom status foreground color",
1173
                                     RadioSettingValueList(
1174
                                         LIST_COLOR8,
1175
                                         LIST_COLOR8[_mem.settings.sigfc]))
1176
            basic.append(sigfc)
1177

    
1178
            sigbc = RadioSetting("settings.sigbc",
1179
                                 "Bottom status background color",
1180
                                     RadioSettingValueList(
1181
                                         LIST_COLOR8,
1182
                                         LIST_COLOR8[_mem.settings.sigbc]))
1183
            basic.append(sigbc)
1184

    
1185
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1186
                                RadioSettingValueList(
1187
                                    LIST_COLOR8,
1188
                                    LIST_COLOR8[_mem.settings.rxfc]))
1189
            basic.append(rxfc)
1190

    
1191
            txfc = RadioSetting("settings.txfc",
1192
                                "Transmitting character color",
1193
                                    RadioSettingValueList(
1194
                                        LIST_COLOR8,
1195
                                        LIST_COLOR8[_mem.settings.txfc]))
1196
            basic.append(txfc)
1197

    
1198
            txdisp = RadioSetting("settings.txdisp",
1199
                                  "Transmitting status display",
1200
                                      RadioSettingValueList(
1201
                                          LIST_TXDISP,
1202
                                          LIST_TXDISP[_mem.settings.txdisp]))
1203
            basic.append(txdisp)
1204
        else:
1205
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1206
                                 RadioSettingValueList(
1207
                                     LIST_COLOR4,
1208
                                     LIST_COLOR4[_mem.settings.wtled]))
1209
            basic.append(wtled)
1210

    
1211
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1212
                                 RadioSettingValueList(
1213
                                     LIST_COLOR4,
1214
                                     LIST_COLOR4[_mem.settings.rxled]))
1215
            basic.append(rxled)
1216

    
1217
            txled = RadioSetting("settings.txled", "TX backlight Color",
1218
                                 RadioSettingValueList(
1219
                                     LIST_COLOR4,
1220
                                     LIST_COLOR4[_mem.settings.txled]))
1221
            basic.append(txled)
1222

    
1223
        anil = RadioSetting("settings.anil", "ANI length",
1224
                            RadioSettingValueList(
1225
                                LIST_ANIL,
1226
                                LIST_ANIL[_mem.settings.anil]))
1227
        basic.append(anil)
1228

    
1229
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1230
                            RadioSettingValueList(
1231
                                LIST_REPS,
1232
                                LIST_REPS[_mem.settings.reps]))
1233
        basic.append(reps)
1234

    
1235
        repm = RadioSetting("settings.repm", "Relay condition",
1236
                            RadioSettingValueList(
1237
                                LIST_REPM,
1238
                                LIST_REPM[_mem.settings.repm]))
1239
        basic.append(repm)
1240

    
1241
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1242
            if self.COLOR_LCD:
1243
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1244
                                     RadioSettingValueList(
1245
                                         LIST_OFF1TO50,
1246
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1247
                basic.append(tmrmr)
1248
            else:
1249
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1250
                                     RadioSettingValueList(
1251
                                         LIST_OFF1TO50, 
1252
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1253
                basic.append(tdrab)
1254

    
1255
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1256
                               RadioSettingValueBoolean(_mem.settings.ste))
1257
            basic.append(ste)
1258

    
1259
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1260
                                 RadioSettingValueList(
1261
                                     LIST_OFF1TO9,
1262
                                     LIST_OFF1TO9[_mem.settings.rpste]))
1263
            basic.append(rpste)
1264

    
1265
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1266
                                 RadioSettingValueList(
1267
                                     LIST_RPTDL,
1268
                                     LIST_RPTDL[_mem.settings.rptdl]))
1269
            basic.append(rptdl)
1270

    
1271
        if str(_mem.fingerprint.fp) in BTECH3:
1272
            mgain = RadioSetting("settings.mgain", "Mic gain",
1273
                                 RadioSettingValueInteger(0, 120,
1274
                                     _mem.settings.mgain))
1275
            basic.append(mgain)
1276

    
1277
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1278
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1279
                                 RadioSettingValueInteger(0, 60,
1280
                                     _mem.settings.dtmfg))
1281
            basic.append(dtmfg)
1282

    
1283
        # Advanced
1284
        def _filter(name):
1285
            filtered = ""
1286
            for char in str(name):
1287
                if char in VALID_CHARS:
1288
                    filtered += char
1289
                else:
1290
                    filtered += " "
1291
            return filtered
1292

    
1293
        _msg = self._memobj.poweron_msg
1294
        if self.COLOR_LCD:
1295
            line1 = RadioSetting("poweron_msg.line1",
1296
                                 "Power-on message line 1",
1297
                                     RadioSettingValueString(0, 8, _filter(
1298
                                         _msg.line1)))
1299
            advanced.append(line1)
1300
            line2 = RadioSetting("poweron_msg.line2",
1301
                                 "Power-on message line 2",
1302
                                     RadioSettingValueString(0, 8, _filter(
1303
                                         _msg.line2)))
1304
            advanced.append(line2)
1305
            line3 = RadioSetting("poweron_msg.line3",
1306
                                 "Power-on message line 3",
1307
                                     RadioSettingValueString(0, 8, _filter(
1308
                                         _msg.line3)))
1309
            advanced.append(line3)
1310
            line4 = RadioSetting("poweron_msg.line4",
1311
                                 "Power-on message line 4",
1312
                                     RadioSettingValueString(0, 8, _filter(
1313
                                         _msg.line4)))
1314
            advanced.append(line4)
1315
            line5 = RadioSetting("poweron_msg.line5",
1316
                                 "Power-on message line 5",
1317
                                     RadioSettingValueString(0, 8, _filter(
1318
                                         _msg.line5)))
1319
            advanced.append(line5)
1320
            line6 = RadioSetting("poweron_msg.line6",
1321
                                 "Power-on message line 6",
1322
                                     RadioSettingValueString(0, 8, _filter(
1323
                                         _msg.line6)))
1324
            advanced.append(line6)
1325
            line7 = RadioSetting("poweron_msg.line7",
1326
                                 "Power-on message line 7",
1327
                                     RadioSettingValueString(0, 8, _filter(
1328
                                         _msg.line7)))
1329
            advanced.append(line7)
1330
            line8 = RadioSetting("poweron_msg.line8", "Static message",
1331
                                 RadioSettingValueString(0, 8, _filter(
1332
                                     _msg.line8)))
1333
            advanced.append(line8)
1334
        else:
1335
            line1 = RadioSetting("poweron_msg.line1",
1336
                                 "Power-on message line 1",
1337
                                     RadioSettingValueString(0, 6, _filter(
1338
                                         _msg.line1)))
1339
            advanced.append(line1)
1340
            line2 = RadioSetting("poweron_msg.line2",
1341
                                 "Power-on message line 2",
1342
                                     RadioSettingValueString(0, 6, _filter(
1343
                                         _msg.line2)))
1344
            advanced.append(line2)
1345

    
1346
        if self.MODEL in ("UV-2501", "UV-5001"):
1347
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1348
                                   RadioSettingValueBoolean(
1349
                                       _mem.settings2.vfomren))
1350
            advanced.append(vfomren)
1351

    
1352
            reseten = RadioSetting("settings2.reseten", "RESET",
1353
                                   RadioSettingValueBoolean(
1354
                                       _mem.settings2.reseten))
1355
            advanced.append(reseten)
1356

    
1357
            menuen = RadioSetting("settings2.menuen", "Menu",
1358
                                  RadioSettingValueBoolean(
1359
                                      _mem.settings2.menuen))
1360
            advanced.append(menuen)
1361

    
1362
        # Other
1363
        def convert_bytes_to_limit(bytes):
1364
            limit = ""
1365
            for byte in bytes:
1366
                if byte < 10:
1367
                    limit += chr(byte + 0x30)
1368
                else:
1369
                    break
1370
            return limit
1371

    
1372
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1373
            _ranges = self._memobj.ranges220
1374
            ranges = "ranges220"
1375
        else:
1376
            _ranges = self._memobj.ranges
1377
            ranges = "ranges"
1378

    
1379
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1380
        val = RadioSettingValueString(0, 3, _limit)
1381
        val.set_mutable(False)
1382
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1383
        other.append(vhf_low)
1384

    
1385
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1386
        val = RadioSettingValueString(0, 3, _limit)
1387
        val.set_mutable(False)
1388
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1389
        other.append(vhf_high)
1390

    
1391
        if self.BANDS == 3 or self.BANDS == 4:
1392
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1393
            val = RadioSettingValueString(0, 3, _limit)
1394
            val.set_mutable(False)
1395
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1396
            other.append(vhf2_low)
1397

    
1398
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1399
            val = RadioSettingValueString(0, 3, _limit)
1400
            val.set_mutable(False)
1401
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1402
            other.append(vhf2_high)
1403

    
1404
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1405
        val = RadioSettingValueString(0, 3, _limit)
1406
        val.set_mutable(False)
1407
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1408
        other.append(uhf_low)
1409

    
1410
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1411
        val = RadioSettingValueString(0, 3, _limit)
1412
        val.set_mutable(False)
1413
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1414
        other.append(uhf_high)
1415

    
1416
        if self.BANDS == 4:
1417
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1418
            val = RadioSettingValueString(0, 3, _limit)
1419
            val.set_mutable(False)
1420
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1421
            other.append(uhf2_low)
1422

    
1423
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1424
            val = RadioSettingValueString(0, 3, _limit)
1425
            val.set_mutable(False)
1426
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1427
            other.append(uhf2_high)
1428

    
1429
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1430
        val.set_mutable(False)
1431
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1432
        other.append(fp)
1433

    
1434

    
1435
        # Work
1436
        if self.COLOR_LCD:
1437
            dispab = RadioSetting("settings2.dispab", "Display",
1438
                                  RadioSettingValueList(
1439
                                      LIST_ABCD,
1440
                                      LIST_ABCD[_mem.settings2.dispab]))
1441
            work.append(dispab)
1442
        else:
1443
            dispab = RadioSetting("settings2.dispab", "Display",
1444
                                  RadioSettingValueList(
1445
                                      LIST_AB,
1446
                                      LIST_AB[_mem.settings2.dispab]))
1447
            work.append(dispab)
1448

    
1449
        if self.COLOR_LCD:
1450
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1451
                                  RadioSettingValueList(
1452
                                      LIST_VFOMR,
1453
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1454
            work.append(vfomra)
1455

    
1456
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1457
                                  RadioSettingValueList(
1458
                                      LIST_VFOMR,
1459
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1460
            work.append(vfomrb)
1461

    
1462
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1463
                                  RadioSettingValueList(
1464
                                      LIST_VFOMR,
1465
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1466
            work.append(vfomrc)
1467

    
1468
            vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1469
                                  RadioSettingValueList(
1470
                                      LIST_VFOMR,
1471
                                      LIST_VFOMR[_mem.settings2.vfomrd]))
1472
            work.append(vfomrd)
1473
        else:
1474
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1475
                                 RadioSettingValueList(
1476
                                     LIST_VFOMR,
1477
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1478
            work.append(vfomr)
1479

    
1480

    
1481
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1482
                           RadioSettingValueBoolean(_mem.settings2.keylock))
1483
        work.append(keylock)
1484

    
1485
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1486
                             RadioSettingValueInteger(0, 199,
1487
                                 _mem.settings2.mrcha))
1488
        work.append(mrcha)
1489

    
1490
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1491
                             RadioSettingValueInteger(0, 199,
1492
                                 _mem.settings2.mrchb))
1493
        work.append(mrchb)
1494

    
1495
        if self.COLOR_LCD:
1496
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1497
                                 RadioSettingValueInteger(0, 199,
1498
                                     _mem.settings2.mrchc))
1499
            work.append(mrchc)
1500

    
1501
            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1502
                                 RadioSettingValueInteger(0, 199,
1503
                                     _mem.settings2.mrchd))
1504
            work.append(mrchd)
1505

    
1506
        def convert_bytes_to_freq(bytes):
1507
            real_freq = 0
1508
            for byte in bytes:
1509
                real_freq = (real_freq * 10) + byte
1510
            return chirp_common.format_freq(real_freq * 10)
1511

    
1512
        def my_validate(value):
1513
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1514
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1515
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1516
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1517
            if self.BANDS == 3 or self.BANDS == 4:
1518
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1519
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1520
            if self.BANDS == 4:
1521
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1522
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1523

    
1524
            value = chirp_common.parse_freq(value)
1525
            msg = ("Can't be less then %i.0000")
1526
            if value > 99000000 and value < _vhf_lower * 1000000:
1527
                raise InvalidValueError(msg % (_vhf_lower))
1528
            msg = ("Can't be betweeb %i.9975-%i.0000")
1529
            if self.BANDS == 2:
1530
                if (_vhf_upper + 1) * 1000000 <= value and \
1531
                    value < _uhf_lower * 1000000:
1532
                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
1533
            if self.BANDS == 3:
1534
                if (_vhf_upper + 1) * 1000000 <= value and \
1535
                    value < _vhf2_lower * 1000000:
1536
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1537
                if (_vhf2_upper + 1) * 1000000 <= value and \
1538
                    value < _uhf_lower * 1000000:
1539
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
1540
            if self.BANDS == 4:
1541
                if (_vhf_upper + 1) * 1000000 <= value and \
1542
                    value < _vhf2_lower * 1000000:
1543
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1544
                if (_vhf2_upper + 1) * 1000000 <= value and \
1545
                    value < _uhf2_lower * 1000000:
1546
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
1547
                if (_uhf2_upper + 1) * 1000000 <= value and \
1548
                    value < _uhf_lower * 1000000:
1549
                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
1550
            msg = ("Can't be greater then %i.9975")
1551
            if value > 99000000 and value >= _uhf_upper * 1000000:
1552
                raise InvalidValueError(msg % (_uhf_upper))
1553
            return chirp_common.format_freq(value)
1554

    
1555
        def apply_freq(setting, obj):
1556
            value = chirp_common.parse_freq(str(setting.value)) / 10
1557
            for i in range(7, -1, -1):
1558
                obj.freq[i] = value % 10
1559
                value /= 10
1560

    
1561
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1562
                                        _mem.vfo.a.freq))
1563
        val1a.set_validate_callback(my_validate)
1564
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1565
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1566
        work.append(vfoafreq)
1567

    
1568
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1569
                                        _mem.vfo.b.freq))
1570
        val1b.set_validate_callback(my_validate)
1571
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1572
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1573
        work.append(vfobfreq)
1574

    
1575
        if self.COLOR_LCD:
1576
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1577
                                            _mem.vfo.c.freq))
1578
            val1c.set_validate_callback(my_validate)
1579
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
1580
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
1581
            work.append(vfocfreq)
1582

    
1583
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1584
                                            _mem.vfo.d.freq))
1585
            val1d.set_validate_callback(my_validate)
1586
            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
1587
            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
1588
            work.append(vfodfreq)
1589

    
1590
        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1591
                                  RadioSettingValueList(
1592
                                      LIST_SHIFT,
1593
                                      LIST_SHIFT[_mem.vfo.a.shiftd]))
1594
        work.append(vfoashiftd)
1595

    
1596
        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1597
                                  RadioSettingValueList(
1598
                                      LIST_SHIFT,
1599
                                      LIST_SHIFT[_mem.vfo.b.shiftd]))
1600
        work.append(vfobshiftd)
1601

    
1602
        if self.COLOR_LCD:
1603
            vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
1604
                                      RadioSettingValueList(
1605
                                          LIST_SHIFT,
1606
                                          LIST_SHIFT[_mem.vfo.c.shiftd]))
1607
            work.append(vfocshiftd)
1608

    
1609
            vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
1610
                                      RadioSettingValueList(
1611
                                          LIST_SHIFT,
1612
                                          LIST_SHIFT[_mem.vfo.d.shiftd]))
1613
            work.append(vfodshiftd)
1614

    
1615
        def convert_bytes_to_offset(bytes):
1616
            real_offset = 0
1617
            for byte in bytes:
1618
                real_offset = (real_offset * 10) + byte
1619
            return chirp_common.format_freq(real_offset * 1000)
1620

    
1621
        def apply_offset(setting, obj):
1622
            value = chirp_common.parse_freq(str(setting.value)) / 1000
1623
            for i in range(5, -1, -1):
1624
                obj.offset[i] = value % 10
1625
                value /= 10
1626

    
1627
        if self.COLOR_LCD:
1628
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1629
                                            _mem.vfo.a.offset))
1630
            vfoaoffset = RadioSetting("vfo.a.offset",
1631
                                      "VFO A offset (0.000-999.999)", val1a)
1632
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1633
            work.append(vfoaoffset)
1634

    
1635
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1636
                                            _mem.vfo.b.offset))
1637
            vfoboffset = RadioSetting("vfo.b.offset",
1638
                                      "VFO B offset (0.000-999.999)", val1b)
1639
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1640
            work.append(vfoboffset)
1641

    
1642
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1643
                                            _mem.vfo.c.offset))
1644
            vfocoffset = RadioSetting("vfo.c.offset",
1645
                                      "VFO C offset (0.000-999.999)", val1c)
1646
            vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
1647
            work.append(vfocoffset)
1648

    
1649
            val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1650
                                            _mem.vfo.d.offset))
1651
            vfodoffset = RadioSetting("vfo.d.offset",
1652
                                      "VFO D offset (0.000-999.999)", val1d)
1653
            vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
1654
            work.append(vfodoffset)
1655
        else:
1656
            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1657
                                            _mem.vfo.a.offset))
1658
            vfoaoffset = RadioSetting("vfo.a.offset",
1659
                                      "VFO A offset (0.000-99.999)", val1a)
1660
            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1661
            work.append(vfoaoffset)
1662

    
1663
            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1664
                                            _mem.vfo.b.offset))
1665
            vfoboffset = RadioSetting("vfo.b.offset",
1666
                                      "VFO B offset (0.000-99.999)", val1b)
1667
            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1668
            work.append(vfoboffset)
1669

    
1670

    
1671
        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
1672
                               RadioSettingValueList(
1673
                                   LIST_TXP,
1674
                                   LIST_TXP[_mem.vfo.a.power]))
1675
        work.append(vfoatxp)
1676

    
1677
        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
1678
                               RadioSettingValueList(
1679
                                   LIST_TXP,
1680
                                   LIST_TXP[_mem.vfo.b.power]))
1681
        work.append(vfobtxp)
1682

    
1683
        if self.COLOR_LCD:
1684
            vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
1685
                                   RadioSettingValueList(
1686
                                       LIST_TXP,
1687
                                       LIST_TXP[_mem.vfo.c.power]))
1688
            work.append(vfoctxp)
1689

    
1690
            vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
1691
                                   RadioSettingValueList(
1692
                                       LIST_TXP,
1693
                                       LIST_TXP[_mem.vfo.d.power]))
1694
            work.append(vfodtxp)
1695

    
1696
        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
1697
                                RadioSettingValueList(
1698
                                    LIST_WIDE,
1699
                                    LIST_WIDE[_mem.vfo.a.wide]))
1700
        work.append(vfoawide)
1701

    
1702
        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
1703
                                RadioSettingValueList(
1704
                                    LIST_WIDE,
1705
                                    LIST_WIDE[_mem.vfo.b.wide]))
1706
        work.append(vfobwide)
1707

    
1708
        if self.COLOR_LCD:
1709
            vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
1710
                                    RadioSettingValueList(
1711
                                        LIST_WIDE,
1712
                                        LIST_WIDE[_mem.vfo.c.wide]))
1713
            work.append(vfocwide)
1714

    
1715
            vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
1716
                                    RadioSettingValueList(
1717
                                        LIST_WIDE,
1718
                                        LIST_WIDE[_mem.vfo.d.wide]))
1719
            work.append(vfodwide)
1720

    
1721
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
1722
                                RadioSettingValueList(
1723
                                    LIST_STEP,
1724
                                    LIST_STEP[_mem.vfo.a.step]))
1725
        work.append(vfoastep)
1726

    
1727
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
1728
                                RadioSettingValueList(
1729
                                    LIST_STEP,
1730
                                    LIST_STEP[_mem.vfo.b.step]))
1731
        work.append(vfobstep)
1732

    
1733
        if self.COLOR_LCD:
1734
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
1735
                                    RadioSettingValueList(
1736
                                        LIST_STEP,
1737
                                        LIST_STEP[_mem.vfo.c.step]))
1738
            work.append(vfocstep)
1739

    
1740
            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
1741
                                    RadioSettingValueList(
1742
                                        LIST_STEP,
1743
                                        LIST_STEP[_mem.vfo.d.step]))
1744
            work.append(vfodstep)
1745

    
1746
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
1747
                                  RadioSettingValueList(
1748
                                      OPTSIG_LIST,
1749
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
1750
        work.append(vfoaoptsig)
1751

    
1752
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
1753
                                  RadioSettingValueList(
1754
                                      OPTSIG_LIST,
1755
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
1756
        work.append(vfoboptsig)
1757

    
1758
        if self.COLOR_LCD:
1759
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
1760
                                      RadioSettingValueList(
1761
                                          OPTSIG_LIST,
1762
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
1763
            work.append(vfocoptsig)
1764

    
1765
            vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
1766
                                      RadioSettingValueList(
1767
                                          OPTSIG_LIST,
1768
                                          OPTSIG_LIST[_mem.vfo.d.optsig]))
1769
            work.append(vfodoptsig)
1770

    
1771
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
1772
                                  RadioSettingValueList(
1773
                                      SPMUTE_LIST,
1774
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
1775
        work.append(vfoaspmute)
1776

    
1777
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
1778
                                  RadioSettingValueList(
1779
                                      SPMUTE_LIST,
1780
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
1781
        work.append(vfobspmute)
1782

    
1783
        if self.COLOR_LCD:
1784
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
1785
                                      RadioSettingValueList(
1786
                                          SPMUTE_LIST,
1787
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
1788
            work.append(vfocspmute)
1789

    
1790
            vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
1791
                                      RadioSettingValueList(
1792
                                          SPMUTE_LIST,
1793
                                          SPMUTE_LIST[_mem.vfo.d.spmute]))
1794
            work.append(vfodspmute)
1795

    
1796
        vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
1797
                               RadioSettingValueBoolean(
1798
                                   _mem.vfo.a.scramble))
1799
        work.append(vfoascr)
1800

    
1801
        vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
1802
                               RadioSettingValueBoolean(
1803
                                   _mem.vfo.b.scramble))
1804
        work.append(vfobscr)
1805

    
1806
        if self.COLOR_LCD:
1807
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
1808
                                   RadioSettingValueBoolean(
1809
                                       _mem.vfo.c.scramble))
1810
            work.append(vfocscr)
1811

    
1812
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
1813
                                   RadioSettingValueBoolean(
1814
                                       _mem.vfo.d.scramble))
1815
            work.append(vfodscr)
1816

    
1817
        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
1818
                                 RadioSettingValueList(
1819
                                     PTTIDCODE_LIST,
1820
                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
1821
        work.append(vfoascode)
1822

    
1823
        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
1824
                                 RadioSettingValueList(
1825
                                     PTTIDCODE_LIST,
1826
                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
1827
        work.append(vfobscode)
1828

    
1829
        if self.COLOR_LCD:
1830
            vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
1831
                                     RadioSettingValueList(
1832
                                         PTTIDCODE_LIST,
1833
                                         PTTIDCODE_LIST[_mem.vfo.c.scode]))
1834
            work.append(vfocscode)
1835

    
1836
            vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
1837
                                     RadioSettingValueList(
1838
                                         PTTIDCODE_LIST,
1839
                                         PTTIDCODE_LIST[_mem.vfo.d.scode]))
1840
            work.append(vfodscode)
1841

    
1842
        pttid = RadioSetting("settings.pttid", "PTT ID",
1843
                             RadioSettingValueList(
1844
                                 PTTID_LIST,
1845
                                 PTTID_LIST[_mem.settings.pttid]))
1846
        work.append(pttid)
1847

    
1848
        if not self.COLOR_LCD:
1849
            #FM presets
1850
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
1851
            top.append(fm_presets)
1852

    
1853
            def fm_validate(value):
1854
                if value == 0:
1855
                    return chirp_common.format_freq(value)
1856
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
1857
                    msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
1858
                    raise InvalidValueError(msg)
1859
                return value
1860

    
1861
            def apply_fm_preset_name(setting, obj):
1862
                valstring = str (setting.value)
1863
                for i in range(0,6):
1864
                    if valstring[i] in VALID_CHARS:
1865
                        obj[i] = valstring[i]
1866
                    else:
1867
                        obj[i] = '0xff'
1868

    
1869
            def apply_fm_freq(setting, obj):
1870
                value = chirp_common.parse_freq(str(setting.value)) / 10
1871
                for i in range(7, -1, -1):
1872
                    obj.freq[i] = value % 10
1873
                    value /= 10
1874
        
1875
            _presets = self._memobj.fm_radio_preset
1876
            i = 1
1877
            for preset in _presets:
1878
                line = RadioSetting("fm_presets_"+ str(i), 
1879
                                    "Station name " + str(i),
1880
                                        RadioSettingValueString(0, 6, _filter(
1881
                                            preset.broadcast_station_name)))
1882
                line.set_apply_callback(apply_fm_preset_name, 
1883
                                        preset.broadcast_station_name)
1884
            
1885
                val = RadioSettingValueFloat(0, 108,
1886
                                             convert_bytes_to_freq(
1887
                                                 preset.freq))
1888
                fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq",
1889
                                      "Frequency "+ str(i), val)
1890
                val.set_validate_callback(fm_validate)
1891
                fmfreq.set_apply_callback(apply_fm_freq, preset)
1892
                fm_presets.append(line)
1893
                fm_presets.append(fmfreq)
1894
            
1895
                i = i + 1
1896

    
1897
         # DTMF-Setting
1898
        dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
1899
                                               "DTMF Encoding Settings")
1900
        dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
1901
                                               "DTMF Decoding Settings")
1902
        top.append(dtmf_enc_settings)
1903
        top.append(dtmf_dec_settings)
1904
        txdisable = RadioSetting("dtmf_settings.txdisable", 
1905
                                 "TX-Disable",
1906
                                 RadioSettingValueBoolean(
1907
                                     _mem.dtmf_settings.txdisable))
1908
        dtmf_enc_settings.append(txdisable)
1909

    
1910
        rxdisable = RadioSetting("dtmf_settings.rxdisable", 
1911
                                 "RX-Disable",
1912
                                 RadioSettingValueBoolean(
1913
                                     _mem.dtmf_settings.rxdisable))
1914
        dtmf_enc_settings.append(rxdisable)
1915

    
1916
        dtmfspeed_on = RadioSetting(
1917
            "dtmf_settings.dtmfspeed_on",
1918
            "DTMF Speed (On Time)",
1919
            RadioSettingValueList(LIST_DTMF_SPEED,
1920
                                  LIST_DTMF_SPEED[
1921
                                      _mem.dtmf_settings.dtmfspeed_on]))
1922
        dtmf_enc_settings.append(dtmfspeed_on)
1923

    
1924
        dtmfspeed_off = RadioSetting(
1925
            "dtmf_settings.dtmfspeed_off",
1926
            "DTMF Speed (Off Time)",
1927
            RadioSettingValueList(LIST_DTMF_SPEED,
1928
                                  LIST_DTMF_SPEED[
1929
                                      _mem.dtmf_settings.dtmfspeed_off]))
1930
        dtmf_enc_settings.append(dtmfspeed_off)
1931

    
1932
        def memory2string(dmtf_mem):
1933
            dtmf_string = ""
1934
            for digit in dmtf_mem:
1935
                if digit != 255:
1936
                    index = LIST_DTMF_VALUES.index(digit)
1937
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
1938
            return dtmf_string
1939

    
1940
        def apply_dmtf_frame(setting, obj):
1941
            LOG.debug("Setting DTMF-Code: " + str(setting.value) )
1942
            val_string = str(setting.value)
1943
            for i in range(0,16):
1944
                obj[i] = 255
1945
            i = 0
1946
            for current_char in val_string:
1947
                current_char = current_char.upper()
1948
                index = LIST_DTMF_DIGITS.index(current_char)
1949
                obj[i] = LIST_DTMF_VALUES[ index ]
1950
                i = i + 1
1951

    
1952
        codes = self._memobj.dtmf_codes
1953
        i = 1
1954
        for dtmfcode in codes:
1955
            val = RadioSettingValueString(0, 16, 
1956
                                          memory2string(dtmfcode.code),
1957
                                          False, CHARSET_DTMF_DIGITS)
1958
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
1959
                                "DMTF Code " + str(i), val)
1960
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
1961
            dtmf_enc_settings.append(line)
1962
            i = i + 1
1963

    
1964
        line = RadioSetting("dtmf_settings.mastervice", 
1965
                            "Master and Vice ID",
1966
                            RadioSettingValueBoolean(
1967
                                _mem.dtmf_settings.mastervice))
1968
        dtmf_dec_settings.append(line)
1969

    
1970
        val = RadioSettingValueString(0, 16, 
1971
                                      memory2string(
1972
                                          _mem.dtmf_settings.masterid),
1973
                                          False, CHARSET_DTMF_DIGITS)
1974
        line = RadioSetting("dtmf_settings.masterid",
1975
                            "Master Control ID ", val)
1976
        line.set_apply_callback(apply_dmtf_frame,
1977
                                _mem.dtmf_settings.masterid)
1978
        dtmf_dec_settings.append(line)
1979

    
1980
        line = RadioSetting("dtmf_settings.minspection", 
1981
                            "Master Inspection",
1982
                            RadioSettingValueBoolean(
1983
                                _mem.dtmf_settings.minspection))
1984
        dtmf_dec_settings.append(line)
1985

    
1986
        line = RadioSetting("dtmf_settings.mmonitor", 
1987
                            "Master Monitor",
1988
                            RadioSettingValueBoolean(
1989
                                _mem.dtmf_settings.mmonitor))
1990
        dtmf_dec_settings.append(line)
1991

    
1992
        line = RadioSetting("dtmf_settings.mstun", 
1993
                            "Master Stun",
1994
                            RadioSettingValueBoolean(
1995
                                _mem.dtmf_settings.mstun))
1996
        dtmf_dec_settings.append(line)
1997

    
1998
        line = RadioSetting("dtmf_settings.mkill", 
1999
                            "Master Kill",
2000
                            RadioSettingValueBoolean(
2001
                                _mem.dtmf_settings.mkill))
2002
        dtmf_dec_settings.append(line)
2003

    
2004
        line = RadioSetting("dtmf_settings.mrevive", 
2005
                            "Master Revive",
2006
                            RadioSettingValueBoolean(
2007
                                _mem.dtmf_settings.mrevive))
2008
        dtmf_dec_settings.append(line)
2009

    
2010
        val = RadioSettingValueString(0, 16, 
2011
                                      memory2string(
2012
                                          _mem.dtmf_settings.viceid),
2013
                                          False, CHARSET_DTMF_DIGITS)
2014
        line = RadioSetting("dtmf_settings.viceid",
2015
                            "Vice Control ID ", val)
2016
        line.set_apply_callback(apply_dmtf_frame,
2017
                                _mem.dtmf_settings.viceid)
2018
        dtmf_dec_settings.append(line)
2019

    
2020
        line = RadioSetting("dtmf_settings.vinspection", 
2021
                            "Vice Inspection",
2022
                            RadioSettingValueBoolean(
2023
                                _mem.dtmf_settings.vinspection))
2024
        dtmf_dec_settings.append(line)
2025

    
2026
        line = RadioSetting("dtmf_settings.vmonitor", 
2027
                            "Vice Monitor",
2028
                            RadioSettingValueBoolean(
2029
                                _mem.dtmf_settings.vmonitor))
2030
        dtmf_dec_settings.append(line)
2031

    
2032
        line = RadioSetting("dtmf_settings.vstun", 
2033
                            "Vice Stun",
2034
                            RadioSettingValueBoolean(
2035
                                _mem.dtmf_settings.vstun))
2036
        dtmf_dec_settings.append(line)
2037

    
2038
        line = RadioSetting("dtmf_settings.vkill", 
2039
                            "Vice Kill",
2040
                            RadioSettingValueBoolean(
2041
                                _mem.dtmf_settings.vkill))
2042
        dtmf_dec_settings.append(line)
2043

    
2044
        line = RadioSetting("dtmf_settings.vrevive", 
2045
                            "Vice Revive",
2046
                            RadioSettingValueBoolean(
2047
                                _mem.dtmf_settings.vrevive))
2048
        dtmf_dec_settings.append(line)
2049

    
2050
        val = RadioSettingValueString(0, 16, 
2051
                                      memory2string(
2052
                                          _mem.dtmf_settings.inspection),
2053
                                          False, CHARSET_DTMF_DIGITS)
2054
        line = RadioSetting("dtmf_settings.inspection",
2055
                            "Inspection", val)
2056
        line.set_apply_callback(apply_dmtf_frame,
2057
                                _mem.dtmf_settings.inspection)
2058
        dtmf_dec_settings.append(line)
2059

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

    
2070
        val = RadioSettingValueString(0, 16, 
2071
                                      memory2string(
2072
                                          _mem.dtmf_settings.kill),
2073
                                          False, CHARSET_DTMF_DIGITS)
2074
        line = RadioSetting("dtmf_settings.kill",
2075
                            "Kill", val)
2076
        line.set_apply_callback(apply_dmtf_frame,
2077
                                _mem.dtmf_settings.kill)
2078
        dtmf_dec_settings.append(line)
2079

    
2080
        val = RadioSettingValueString(0, 16, 
2081
                                      memory2string(
2082
                                          _mem.dtmf_settings.monitor),
2083
                                          False, CHARSET_DTMF_DIGITS)
2084
        line = RadioSetting("dtmf_settings.monitor",
2085
                            "Monitor", val)
2086
        line.set_apply_callback(apply_dmtf_frame,
2087
                                _mem.dtmf_settings.monitor)
2088
        dtmf_dec_settings.append(line)
2089

    
2090
        val = RadioSettingValueString(0, 16, 
2091
                                      memory2string(
2092
                                          _mem.dtmf_settings.stun),
2093
                                          False, CHARSET_DTMF_DIGITS)
2094
        line = RadioSetting("dtmf_settings.stun",
2095
                            "Stun", val)
2096
        line.set_apply_callback(apply_dmtf_frame,
2097
                                _mem.dtmf_settings.stun)
2098
        dtmf_dec_settings.append(line)
2099

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

    
2110
        def apply_dmtf_listvalue(setting, obj):
2111
            LOG.debug("Setting value: "+ str(setting.value) + " from list")
2112
            val = str(setting.value)
2113
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2114
            val = LIST_DTMF_SPECIAL_VALUES[index]
2115
            obj.set_value(val)
2116

    
2117
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
2118
        line = RadioSetting(
2119
            "dtmf_settings.groupcode",
2120
            "Group Code",
2121
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2122
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2123
        line.set_apply_callback(apply_dmtf_listvalue,
2124
                                _mem.dtmf_settings.groupcode)
2125
        dtmf_dec_settings.append(line)
2126

    
2127
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
2128
        line = RadioSetting(
2129
            "dtmf_settings.spacecode",
2130
            "Space Code",
2131
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2132
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2133
        line.set_apply_callback(apply_dmtf_listvalue,
2134
                                _mem.dtmf_settings.spacecode)
2135
        dtmf_dec_settings.append(line)
2136

    
2137
        line = RadioSetting(
2138
            "dtmf_settings.resettime",
2139
            "Reset time",
2140
            RadioSettingValueList(LIST_5TONE_RESET,
2141
                                  LIST_5TONE_RESET[
2142
                                      _mem.dtmf_settings.resettime]))
2143
        dtmf_dec_settings.append(line)
2144

    
2145
        line = RadioSetting(
2146
            "dtmf_settings.delayproctime",
2147
            "Delay processing time",
2148
            RadioSettingValueList(LIST_DTMF_DELAY,
2149
                                  LIST_DTMF_DELAY[
2150
                                      _mem.dtmf_settings.delayproctime]))
2151
        dtmf_dec_settings.append(line)
2152

    
2153

    
2154
        # 5 Tone Settings
2155
        stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
2156
        codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
2157

    
2158
        group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
2159
        group_5tone.append(stds_5tone)
2160
        group_5tone.append(codes_5tone)
2161

    
2162
        top.append(group_5tone)
2163

    
2164
        def apply_list_value(setting, obj):
2165
            options = setting.value.get_options()
2166
            obj.set_value ( options.index(str(setting.value)) )
2167

    
2168
        _5tone_standards = self._memobj._5tone_std_settings
2169
        i = 0
2170
        for standard in _5tone_standards:
2171
            std_5tone = RadioSettingGroup ("std_5tone_" + str(i), 
2172
                                           LIST_5TONE_STANDARDS[i])
2173
            stds_5tone.append(std_5tone)
2174
 
2175
            period = standard.period
2176
            if period == 255:
2177
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] + 
2178
                          " is not yet configured. Setting to 70ms.")
2179
                period = 5
2180

    
2181
            if period <= len( LIST_5TONE_STANDARD_PERIODS ):
2182
                line = RadioSetting(
2183
                    "_5tone_std_settings_" + str(i) + "_period",
2184
                    "Period (ms)", RadioSettingValueList
2185
                    (LIST_5TONE_STANDARD_PERIODS,
2186
                     LIST_5TONE_STANDARD_PERIODS[period]))
2187
                line.set_apply_callback(apply_list_value, standard.period)
2188
                std_5tone.append(line)
2189
            else:
2190
                LOG.debug("Invalid value for 5tone period! Disabling.")
2191

    
2192
            group_tone = standard.group_tone
2193
            if group_tone == 255:
2194
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2195
                          " is not yet configured. Setting to A.")
2196
                group_tone = 10
2197

    
2198
            if group_tone <= len( LIST_5TONE_DIGITS ):
2199
                line = RadioSetting(
2200
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2201
                    "Group Tone",
2202
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2203
                                          LIST_5TONE_DIGITS[
2204
                                              group_tone]))
2205
                line.set_apply_callback(apply_list_value,
2206
                                        standard.group_tone)
2207
                std_5tone.append(line)
2208
            else:
2209
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2210

    
2211
            repeat_tone = standard.repeat_tone
2212
            if repeat_tone == 255:
2213
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] + 
2214
                          " is not yet configured. Setting to E.")
2215
                repeat_tone = 14
2216

    
2217
            if repeat_tone <= len( LIST_5TONE_DIGITS ):
2218
                line = RadioSetting(
2219
                    "_5tone_std_settings_" + str(i) + "_repttone",
2220
                    "Repeat Tone",
2221
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2222
                                          LIST_5TONE_DIGITS[
2223
                                              repeat_tone]))
2224
                line.set_apply_callback(apply_list_value,
2225
                                        standard.repeat_tone)
2226
                std_5tone.append(line)
2227
            else:
2228
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2229
            i = i + 1
2230

    
2231
        def my_apply_5tonestdlist_value(setting, obj):
2232
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2233
                obj.set_value(0xFF)
2234
            else:
2235
                obj.set_value( LIST_5TONE_STANDARDS.
2236
                              index(str(setting.value)) )
2237

    
2238
        def apply_5tone_frame(setting, obj):
2239
            LOG.debug("Setting 5 Tone: " + str(setting.value) )
2240
            valstring = str(setting.value)
2241
            if len(valstring) == 0:
2242
                for i in range(0,5):
2243
                    obj[i] = 255
2244
            else:
2245
                validFrame = True
2246
                for i in range(0,5):
2247
                    currentChar = valstring[i].upper()
2248
                    if currentChar in LIST_5TONE_DIGITS:
2249
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2250
                    else:
2251
                        validFrame = False
2252
                        LOG.debug("invalid char: " + str(currentChar))
2253
                if not validFrame:
2254
                    LOG.debug("setting whole frame to FF" )
2255
                    for i in range(0,5):
2256
                        obj[i] = 255
2257

    
2258
        def validate_5tone_frame(value):
2259
            if (len(str(value)) != 5) and (len(str(value)) != 0) :
2260
                msg = ("5 Tone must have 5 digits or 0 digits")
2261
                raise InvalidValueError(msg)
2262
            for digit in str(value):
2263
                if digit.upper() not in LIST_5TONE_DIGITS:
2264
                    msg = (str(digit) + " is not a valid digit for 5tones")
2265
                    raise InvalidValueError(msg)
2266
            return value
2267

    
2268
        def frame2string(frame):
2269
            frameString = ""
2270
            for digit in frame:
2271
                if digit != 255:
2272
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2273
            return frameString
2274

    
2275
        _5tone_codes = self._memobj._5tone_codes
2276
        i = 1
2277
        for code in _5tone_codes:
2278
            code_5tone = RadioSettingGroup ("code_5tone_" + str(i),
2279
                                            "5 Tone code " + str(i))
2280
            codes_5tone.append(code_5tone)
2281
            if (code.standard == 255 ):
2282
                currentVal = 15
2283
            else:
2284
                currentVal = code.standard
2285
            line = RadioSetting("_5tone_code_" + str(i) + "_std", 
2286
                                " Standard",
2287
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2288
                                                      LIST_5TONE_STANDARDS[
2289
                                                          currentVal]) )
2290
            line.set_apply_callback(my_apply_5tonestdlist_value,
2291
                                    code.standard)
2292
            code_5tone.append(line)
2293

    
2294
            val = RadioSettingValueString(0, 6,
2295
                                          frame2string(code.frame1), False)
2296
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1", 
2297
                                " Frame 1", val)
2298
            val.set_validate_callback(validate_5tone_frame)
2299
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2300
            code_5tone.append(line)
2301

    
2302
            val = RadioSettingValueString(0, 6,
2303
                                          frame2string(code.frame2), False)
2304
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2305
                                " Frame 2", val)
2306
            val.set_validate_callback(validate_5tone_frame)
2307
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2308
            code_5tone.append(line)
2309

    
2310
            val = RadioSettingValueString(0, 6,
2311
                                          frame2string(code.frame3), False)
2312
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2313
                                " Frame 3", val)
2314
            val.set_validate_callback(validate_5tone_frame)
2315
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2316
            code_5tone.append(line)
2317
            i = i + 1
2318

    
2319
        _5_tone_decode1 = RadioSetting(
2320
            "_5tone_settings._5tone_decode_call_frame1",
2321
            "5 Tone decode call Frame 1",
2322
            RadioSettingValueBoolean(
2323
                _mem._5tone_settings._5tone_decode_call_frame1))
2324
        group_5tone.append(_5_tone_decode1)
2325

    
2326
        _5_tone_decode2 = RadioSetting(
2327
            "_5tone_settings._5tone_decode_call_frame2",
2328
            "5 Tone decode call Frame 2",
2329
            RadioSettingValueBoolean(
2330
                _mem._5tone_settings._5tone_decode_call_frame2))
2331
        group_5tone.append(_5_tone_decode2)
2332

    
2333
        _5_tone_decode3 = RadioSetting(
2334
            "_5tone_settings._5tone_decode_call_frame3",
2335
            "5 Tone decode call Frame 3",
2336
            RadioSettingValueBoolean(
2337
                _mem._5tone_settings._5tone_decode_call_frame3))
2338
        group_5tone.append(_5_tone_decode3)
2339

    
2340
        _5_tone_decode_disp1 = RadioSetting(
2341
            "_5tone_settings._5tone_decode_disp_frame1",
2342
            "5 Tone decode disp Frame 1",
2343
            RadioSettingValueBoolean(
2344
                _mem._5tone_settings._5tone_decode_disp_frame1))
2345
        group_5tone.append(_5_tone_decode_disp1)
2346

    
2347
        _5_tone_decode_disp2 = RadioSetting(
2348
            "_5tone_settings._5tone_decode_disp_frame2",
2349
            "5 Tone decode disp Frame 2",
2350
            RadioSettingValueBoolean(
2351
                _mem._5tone_settings._5tone_decode_disp_frame2))
2352
        group_5tone.append(_5_tone_decode_disp2)
2353

    
2354
        _5_tone_decode_disp3 = RadioSetting(
2355
            "_5tone_settings._5tone_decode_disp_frame3",
2356
            "5 Tone decode disp Frame 3",
2357
            RadioSettingValueBoolean(
2358
                _mem._5tone_settings._5tone_decode_disp_frame3))
2359
        group_5tone.append(_5_tone_decode_disp3)
2360

    
2361
        decode_standard = _mem._5tone_settings.decode_standard
2362
        if decode_standard == 255:
2363
            decode_standard = 0
2364
        if decode_standard <= len (LIST_5TONE_STANDARDS_without_none) :
2365
            line = RadioSetting("_5tone_settings.decode_standard", 
2366
                                "5 Tone-decode Standard",
2367
                                RadioSettingValueList(
2368
                                    LIST_5TONE_STANDARDS_without_none,
2369
                                    LIST_5TONE_STANDARDS_without_none[
2370
                                        decode_standard]))
2371
            group_5tone.append(line)
2372
        else:
2373
            LOG.debug("Invalid decode std...")
2374
            
2375
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2376
        if _5tone_delay1 == 255:
2377
            _5tone_delay1 = 20
2378

    
2379
        if _5tone_delay1 <= len( LIST_5TONE_DELAY ):
2380
            list = RadioSettingValueList(LIST_5TONE_DELAY, 
2381
                                         LIST_5TONE_DELAY[
2382
                                             _5tone_delay1])
2383
            line = RadioSetting("_5tone_settings._5tone_delay1",
2384
                                "5 Tone Delay Frame 1", list)
2385
            group_5tone.append(line)
2386
        else:
2387
            LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
2388

    
2389
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2390
        if _5tone_delay2 == 255:
2391
            _5tone_delay2 = 20
2392
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2393

    
2394
        if _5tone_delay2 <= len( LIST_5TONE_DELAY ):
2395
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2396
                                         LIST_5TONE_DELAY[
2397
                                             _5tone_delay2])
2398
            line = RadioSetting("_5tone_settings._5tone_delay2",
2399
                                "5 Tone Delay Frame 2", list)
2400
            group_5tone.append(line)
2401
        else:
2402
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2403

    
2404
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2405
        if _5tone_delay3 == 255:
2406
            _5tone_delay3 = 20
2407
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2408

    
2409
        if _5tone_delay3 <= len( LIST_5TONE_DELAY ):
2410
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2411
                                         LIST_5TONE_DELAY[
2412
                                             _5tone_delay3])
2413
            line = RadioSetting("_5tone_settings._5tone_delay3",
2414
                                "5 Tone Delay Frame 3", list )
2415
            group_5tone.append(line)
2416
        else:
2417
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2418

    
2419
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2420
        if ext_length == 255:
2421
            ext_length = 0
2422
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2423

    
2424
        if ext_length <= len(
2425
            LIST_5TONE_DELAY ):
2426
            list = RadioSettingValueList(
2427
                LIST_5TONE_DELAY, 
2428
                LIST_5TONE_DELAY[
2429
                    ext_length])
2430
            line = RadioSetting(
2431
                "_5tone_settings._5tone_first_digit_ext_length",
2432
                "First digit extend length", list)
2433
            group_5tone.append(line)
2434
        else:
2435
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2436

    
2437
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2438
        if decode_reset_time == 255:
2439
            decode_reset_time = 59
2440
            LOG.debug("Decode reset time unconfigured. resetting.")
2441
        if decode_reset_time <= len(LIST_5TONE_RESET):
2442
            list = RadioSettingValueList(
2443
                LIST_5TONE_RESET,
2444
                LIST_5TONE_RESET[
2445
                    decode_reset_time])
2446
            line = RadioSetting("_5tone_settings.decode_reset_time",
2447
                                "Decode reset time", list)
2448
            group_5tone.append(line)
2449
        else:
2450
            LOG.debug("Invalid value decode reset time! Disabling.")
2451

    
2452
        # 2 Tone
2453
        encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
2454
        decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
2455

    
2456
        top.append(encode_2tone)
2457
        top.append(decode_2tone)
2458

    
2459
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2460
        if duration_1st_tone == 255:
2461
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2462
                      "configured. Setting to 600ms")
2463
            duration_1st_tone = 60
2464

    
2465
        if duration_1st_tone <= len( LIST_5TONE_DELAY ):
2466
            line = RadioSetting("_2tone.duration_1st_tone", 
2467
                                "Duration 1st Tone",
2468
                                RadioSettingValueList(LIST_5TONE_DELAY,
2469
                                                      LIST_5TONE_DELAY[
2470
                                                          duration_1st_tone]))
2471
            encode_2tone.append(line)
2472

    
2473
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2474
        if duration_2nd_tone == 255:
2475
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2476
                      "configured. Setting to 600ms")
2477
            duration_2nd_tone = 60
2478

    
2479
        if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
2480
            line = RadioSetting("_2tone.duration_2nd_tone", 
2481
                                "Duration 2nd Tone",
2482
                                RadioSettingValueList(LIST_5TONE_DELAY,
2483
                                                      LIST_5TONE_DELAY[
2484
                                                          duration_2nd_tone]))
2485
            encode_2tone.append(line)
2486

    
2487
        duration_gap = self._memobj._2tone.duration_gap
2488
        if duration_gap == 255:
2489
            LOG.debug("Duration of gap is not yet " +
2490
                      "configured. Setting to 300ms")
2491
            duration_gap = 30
2492

    
2493
        if duration_gap <= len( LIST_5TONE_DELAY ):
2494
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2495
                                RadioSettingValueList(LIST_5TONE_DELAY,
2496
                                                      LIST_5TONE_DELAY[
2497
                                                          duration_gap]))
2498
            encode_2tone.append(line)
2499

    
2500
        def _2tone_validate(value):
2501
            if value == 0:
2502
                return 65535
2503
            if value == 65535:
2504
                return value
2505
            if not (300 <= value and value <= 3000):
2506
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2507
                raise InvalidValueError(msg)
2508
            return value
2509

    
2510
        def apply_2tone_freq(setting, obj):
2511
            val = int(setting.value)
2512
            if (val == 0) or (val == 65535):
2513
                obj.set_value(65535)
2514
            else:
2515
                obj.set_value(val)
2516

    
2517
        i = 1
2518
        for code in  self._memobj._2tone._2tone_encode:
2519
            code_2tone = RadioSettingGroup ("code_2tone_" + str(i), 
2520
                                           "Encode Code " + str(i))
2521
            encode_2tone.append(code_2tone)
2522

    
2523
            tmp = code.freq1
2524
            if tmp == 65535:
2525
                tmp = 0
2526
            val1 = RadioSettingValueInteger(0, 65535, tmp)
2527
            freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1", 
2528
                                 "Frequency 1", val1)
2529
            val1.set_validate_callback(_2tone_validate)
2530
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
2531
            code_2tone.append(freq1)
2532

    
2533
            tmp = code.freq2
2534
            if tmp == 65535:
2535
                tmp = 0
2536
            val2 = RadioSettingValueInteger(0, 65535, tmp)
2537
            freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2", 
2538
                                 "Frequency 2", val2)
2539
            val2.set_validate_callback(_2tone_validate)
2540
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
2541
            code_2tone.append(freq2)
2542

    
2543
            i = i + 1
2544

    
2545
        decode_reset_time = _mem._2tone.reset_time
2546
        if decode_reset_time == 255:
2547
            decode_reset_time = 59
2548
            LOG.debug("Decode reset time unconfigured. resetting.")
2549
        if decode_reset_time <= len(LIST_5TONE_RESET):
2550
            list = RadioSettingValueList(
2551
                LIST_5TONE_RESET,
2552
                LIST_5TONE_RESET[
2553
                    decode_reset_time])
2554
            line = RadioSetting("_2tone.reset_time",
2555
                                "Decode reset time", list)
2556
            decode_2tone.append(line)
2557
        else:
2558
            LOG.debug("Invalid value decode reset time! Disabling.")
2559

    
2560
        def apply_2tone_freq_pair(setting, obj):
2561
            val = int(setting.value)
2562
            derived_val = 65535
2563
            frqname = str(setting._name[-5:])
2564
            derivedname = "derived_from_" + frqname
2565

    
2566
            if (val == 0):
2567
                val = 65535
2568
                derived_val = 65535
2569
            else:
2570
                derived_val = int(round(2304000.0/val))
2571

    
2572
            obj[frqname].set_value( val )
2573
            obj[derivedname].set_value( derived_val )
2574

    
2575
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " 
2576
                      + derivedname + ": " + str(derived_val))
2577

    
2578
        i = 1
2579
        for decode_code in  self._memobj._2tone._2tone_decode:
2580
            _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
2581
                                           "Decode Code " + str(i))
2582
            decode_2tone.append(_2tone_dec_code)
2583

    
2584
            j = 1
2585
            for dec in decode_code.decs:
2586
                val = dec.dec
2587
                if val == 255:
2588
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +  
2589
                              " is not yet configured. Setting to 0.")
2590
                    val = 0
2591

    
2592
                if val <= len( LIST_2TONE_DEC ):
2593
                    line = RadioSetting(
2594
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
2595
                        "Dec " + str(j), RadioSettingValueList
2596
                        (LIST_2TONE_DEC,
2597
                         LIST_2TONE_DEC[val]))
2598
                    line.set_apply_callback(apply_list_value, dec.dec)
2599
                    _2tone_dec_code.append(line)
2600
                else:
2601
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
2602

    
2603
                val = dec.response
2604
                if val == 255:
2605
                    LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
2606
                              " is not yet configured. Setting to 0.")
2607
                    val = 0
2608

    
2609
                if val <= len( LIST_2TONE_RESPONSE ):
2610
                    line = RadioSetting(
2611
                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
2612
                        "Response " + str(j), RadioSettingValueList
2613
                        (LIST_2TONE_RESPONSE,
2614
                         LIST_2TONE_RESPONSE[val]))
2615
                    line.set_apply_callback(apply_list_value, dec.response)
2616
                    _2tone_dec_code.append(line)
2617
                else:
2618
                    LOG.debug("Invalid value for 2tone response! Disabling.")
2619

    
2620
                val = dec.alert
2621
                if val == 255:
2622
                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +  
2623
                              " is not yet configured. Setting to 0.")
2624
                    val = 0
2625

    
2626
                if val <= len( PTTIDCODE_LIST ):
2627
                    line = RadioSetting(
2628
                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
2629
                        "Alert " + str(j), RadioSettingValueList
2630
                        (PTTIDCODE_LIST,
2631
                         PTTIDCODE_LIST[val]))
2632
                    line.set_apply_callback(apply_list_value, dec.alert)
2633
                    _2tone_dec_code.append(line)
2634
                else:
2635
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
2636
                j = j + 1
2637

    
2638
            freq = self._memobj._2tone.freqs[i-1]
2639
            for char in ['A', 'B', 'C', 'D']:
2640
                setting_name = "freq" + str(char)
2641

    
2642
                tmp = freq[setting_name]
2643
                if tmp == 65535:
2644
                    tmp = 0
2645
                if tmp != 0:
2646
                    expected = int(round(2304000.0/tmp))
2647
                    from_mem = freq["derived_from_" + setting_name]
2648
                    if expected != from_mem:
2649
                        LOG.error("Expected " + str(expected) + 
2650
                                  " but read " + str(from_mem ) + 
2651
                                  ". Disabling 2Tone Decode Freqs!")
2652
                        break
2653
                val = RadioSettingValueInteger(0, 65535, tmp)
2654
                frq = RadioSetting("2tone_dec_"+ str(i) + "_freq" + str(char),
2655
                                         ("Decode Frequency " +str(char)), val)
2656
                val.set_validate_callback(_2tone_validate)
2657
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
2658
                _2tone_dec_code.append(frq)
2659

    
2660
            i = i + 1
2661

    
2662
        return top
2663

    
2664
    def set_settings(self, settings):
2665
        _settings = self._memobj.settings
2666
        for element in settings:
2667
            if not isinstance(element, RadioSetting):
2668
                if element.get_name() == "fm_preset":
2669
                    self._set_fm_preset(element)
2670
                else:
2671
                    self.set_settings(element)
2672
                    continue
2673
            else:
2674
                try:
2675
                    name = element.get_name()
2676
                    if "." in name:
2677
                        bits = name.split(".")
2678
                        obj = self._memobj
2679
                        for bit in bits[:-1]:
2680
                            if "/" in bit:
2681
                                bit, index = bit.split("/", 1)
2682
                                index = int(index)
2683
                                obj = getattr(obj, bit)[index]
2684
                            else:
2685
                                obj = getattr(obj, bit)
2686
                        setting = bits[-1]
2687
                    else:
2688
                        obj = _settings
2689
                        setting = element.get_name()
2690

    
2691
                    if element.has_apply_callback():
2692
                        LOG.debug("Using apply callback")
2693
                        element.run_apply_callback()
2694
                    elif element.value.get_mutable():
2695
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2696
                        setattr(obj, setting, element.value)
2697
                except Exception, e:
2698
                    LOG.debug(element.get_name())
2699
                    raise
2700

    
2701
    @classmethod
2702
    def match_model(cls, filedata, filename):
2703
        match_size = False
2704
        match_model = False
2705

    
2706
        # testing the file data size
2707
        if len(filedata) == MEM_SIZE:
2708
            match_size = True
2709

    
2710
        # testing the firmware model fingerprint
2711
        match_model = model_match(cls, filedata)
2712

    
2713
        if match_size and match_model:
2714
            return True
2715
        else:
2716
            return False
2717

    
2718

    
2719
MEM_FORMAT = """
2720
#seekto 0x0000;
2721
struct {
2722
  lbcd rxfreq[4];
2723
  lbcd txfreq[4];
2724
  ul16 rxtone;
2725
  ul16 txtone;
2726
  u8 unknown0:4,
2727
     scode:4;
2728
  u8 unknown1:2,
2729
     spmute:1,
2730
     unknown2:3,
2731
     optsig:2;
2732
  u8 unknown3:3,
2733
     scramble:1,
2734
     unknown4:3,
2735
     power:1;
2736
  u8 unknown5:1,
2737
     wide:1,
2738
     unknown6:2,
2739
     bcl:1,
2740
     add:1,
2741
     pttid:2;
2742
} memory[200];
2743

    
2744
#seekto 0x0E00;
2745
struct {
2746
  u8 tdr;
2747
  u8 unknown1;
2748
  u8 sql;
2749
  u8 unknown2[2];
2750
  u8 tot;
2751
  u8 apo;           // BTech radios use this as the Auto Power Off time
2752
                    // other radios use this as pre-Time Out Alert
2753
  u8 unknown3;
2754
  u8 abr;
2755
  u8 beep;
2756
  u8 unknown4[4];
2757
  u8 dtmfst;
2758
  u8 unknown5[2];
2759
  u8 prisc;
2760
  u8 prich;
2761
  u8 screv;
2762
  u8 unknown6[2];
2763
  u8 pttid;
2764
  u8 pttlt;
2765
  u8 unknown7;
2766
  u8 emctp;
2767
  u8 emcch;
2768
  u8 ringt;
2769
  u8 unknown8;
2770
  u8 camdf;
2771
  u8 cbmdf;
2772
  u8 sync;          // BTech radios use this as the display sync setting
2773
                    // other radios use this as the auto keypad lock setting
2774
  u8 ponmsg;
2775
  u8 wtled;
2776
  u8 rxled;
2777
  u8 txled;
2778
  u8 unknown9[5];
2779
  u8 anil;
2780
  u8 reps;
2781
  u8 repm;
2782
  u8 tdrab;
2783
  u8 ste;
2784
  u8 rpste;
2785
  u8 rptdl;
2786
  u8 mgain;
2787
  u8 dtmfg;
2788
} settings;
2789

    
2790
#seekto 0x0E80;
2791
struct {
2792
  u8 unknown1;
2793
  u8 vfomr;
2794
  u8 keylock;
2795
  u8 unknown2;
2796
  u8 unknown3:4,
2797
     vfomren:1,
2798
     unknown4:1,
2799
     reseten:1,
2800
     menuen:1;
2801
  u8 unknown5[11];
2802
  u8 dispab;
2803
  u8 mrcha;
2804
  u8 mrchb;
2805
  u8 menu;
2806
} settings2;
2807

    
2808
#seekto 0x0EC0;
2809
struct {
2810
  char line1[6];
2811
  char line2[6];
2812
} poweron_msg;
2813

    
2814
struct settings_vfo {
2815
  u8 freq[8];
2816
  u8 offset[6];
2817
  u8 unknown2[2];
2818
  ul16 rxtone;
2819
  ul16 txtone;
2820
  u8 scode;
2821
  u8 spmute;
2822
  u8 optsig;
2823
  u8 scramble;
2824
  u8 wide;
2825
  u8 power;
2826
  u8 shiftd;
2827
  u8 step;
2828
  u8 unknown3[4];
2829
};
2830

    
2831
#seekto 0x0F00;
2832
struct {
2833
  struct settings_vfo a;
2834
  struct settings_vfo b;
2835
} vfo;
2836

    
2837
#seekto 0x1000;
2838
struct {
2839
  char name[6];
2840
  u8 unknown1[10];
2841
} names[200];
2842

    
2843
#seekto 0x2400;
2844
struct {
2845
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2846
  u8 group_tone;
2847
  u8 repeat_tone;
2848
  u8 unused[13];
2849
} _5tone_std_settings[15];
2850

    
2851
#seekto 0x2500;
2852
struct {
2853
  u8 frame1[5];
2854
  u8 frame2[5];
2855
  u8 frame3[5];
2856
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2857
} _5tone_codes[15];
2858

    
2859
#seekto 0x25F0;
2860
struct {
2861
  u8 _5tone_delay1; // * 10ms
2862
  u8 _5tone_delay2; // * 10ms
2863
  u8 _5tone_delay3; // * 10ms
2864
  u8 _5tone_first_digit_ext_length;
2865
  u8 unknown1;
2866
  u8 unknown2;
2867
  u8 unknown3;
2868
  u8 unknown4;
2869
  u8 decode_standard;
2870
  u8 unknown5:5,
2871
     _5tone_decode_call_frame3:1,
2872
     _5tone_decode_call_frame2:1,
2873
     _5tone_decode_call_frame1:1;
2874
  u8 unknown6:5,
2875
     _5tone_decode_disp_frame3:1,
2876
     _5tone_decode_disp_frame2:1,
2877
     _5tone_decode_disp_frame1:1;
2878
  u8 decode_reset_time; // * 100 + 100ms
2879
} _5tone_settings;
2880

    
2881
#seekto 0x2900;
2882
struct {
2883
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2884
} dtmf_codes[15];
2885

    
2886
#seekto 0x29F0;
2887
struct {
2888
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
2889
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
2890
  u8 unknown0[14];
2891
  u8 inspection[16];
2892
  u8 monitor[16];
2893
  u8 alarmcode[16];
2894
  u8 stun[16];
2895
  u8 kill[16];
2896
  u8 revive[16];
2897
  u8 unknown1[16];
2898
  u8 unknown2[16];
2899
  u8 unknown3[16];
2900
  u8 unknown4[16];
2901
  u8 unknown5[16];
2902
  u8 unknown6[16];
2903
  u8 unknown7[16];
2904
  u8 masterid[16];
2905
  u8 viceid[16];
2906
  u8 unused01:7,
2907
     mastervice:1;
2908
  u8 unused02:3,
2909
     mrevive:1,
2910
     mkill:1,
2911
     mstun:1,
2912
     mmonitor:1,
2913
     minspection:1;
2914
  u8 unused03:3,
2915
     vrevive:1,
2916
     vkill:1,
2917
     vstun:1,
2918
     vmonitor:1,
2919
     vinspection:1;
2920
  u8 unused04:6,
2921
     txdisable:1,
2922
     rxdisable:1;
2923
  u8 groupcode;
2924
  u8 spacecode;
2925
  u8 delayproctime; // * 100 + 100ms
2926
  u8 resettime;     // * 100 + 100ms
2927
} dtmf_settings;
2928

    
2929
#seekto 0x2D00;
2930
struct {
2931
  struct {
2932
    ul16 freq1;
2933
    u8 unused01[6];
2934
    ul16 freq2;
2935
    u8 unused02[6];
2936
  } _2tone_encode[15];
2937
  u8 duration_1st_tone; // *10ms
2938
  u8 duration_2nd_tone; // *10ms
2939
  u8 duration_gap;      // *10ms
2940
  u8 unused03[13];
2941
  struct {
2942
    struct {
2943
      u8 dec;      // one out of LIST_2TONE_DEC
2944
      u8 response; // one out of LIST_2TONE_RESPONSE
2945
      u8 alert;    // 1-16
2946
    } decs[4];
2947
    u8 unused04[4];
2948
  } _2tone_decode[15];
2949
  u8 unused05[16];
2950

    
2951
  struct {
2952
    ul16 freqA;
2953
    ul16 freqB;
2954
    ul16 freqC;
2955
    ul16 freqD;
2956
    // unknown what those values mean, but they are
2957
    // derived from configured frequencies
2958
    ul16 derived_from_freqA; // 2304000/freqA
2959
    ul16 derived_from_freqB; // 2304000/freqB
2960
    ul16 derived_from_freqC; // 2304000/freqC
2961
    ul16 derived_from_freqD; // 2304000/freqD
2962
  }freqs[15];
2963
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
2964
} _2tone;
2965

    
2966
#seekto 0x3000;
2967
struct {
2968
  u8 freq[8];
2969
  char broadcast_station_name[6];
2970
  u8 unknown[2];
2971
} fm_radio_preset[16];
2972

    
2973
#seekto 0x3C90;
2974
struct {
2975
  u8 vhf_low[3];
2976
  u8 vhf_high[3];
2977
  u8 uhf_low[3];
2978
  u8 uhf_high[3];
2979
} ranges;
2980

    
2981
// the UV-2501+220 & KT8900R has different zones for storing ranges
2982

    
2983
#seekto 0x3CD0;
2984
struct {
2985
  u8 vhf_low[3];
2986
  u8 vhf_high[3];
2987
  u8 unknown1[4];
2988
  u8 unknown2[6];
2989
  u8 vhf2_low[3];
2990
  u8 vhf2_high[3];
2991
  u8 unknown3[4];
2992
  u8 unknown4[6];
2993
  u8 uhf_low[3];
2994
  u8 uhf_high[3];
2995
} ranges220;
2996

    
2997
#seekto 0x3F70;
2998
struct {
2999
  char fp[6];
3000
} fingerprint;
3001

    
3002
"""
3003

    
3004

    
3005
class BTech(BTechMobileCommon):
3006
    """BTECH's UV-5001 and alike radios"""
3007
    BANDS = 2
3008
    COLOR_LCD = False
3009
    NAME_LENGTH = 6
3010

    
3011
    def set_options(self):
3012
        """This is to read the options from the image and set it in the
3013
        environment, for now just the limits of the freqs in the VHF/UHF
3014
        ranges"""
3015

    
3016
        # setting the correct ranges for each radio type
3017
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3018
            # the model 2501+220 has a segment in 220
3019
            # and a different position in the memmap
3020
            # also the QYT KT8900R
3021
            ranges = self._memobj.ranges220
3022
        else:
3023
            ranges = self._memobj.ranges
3024

    
3025
        # the normal dual bands
3026
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3027
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3028

    
3029
        # DEBUG
3030
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3031
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3032

    
3033
        # 220Mhz radios case
3034
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3035
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3036
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3037
            self._220_range = vhf2
3038

    
3039
        # set the class with the real data
3040
        self._vhf_range = vhf
3041
        self._uhf_range = uhf
3042

    
3043
    def process_mmap(self):
3044
        """Process the mem map into the mem object"""
3045

    
3046
        # Get it
3047
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3048

    
3049
        # load specific parameters from the radio image
3050
        self.set_options()
3051

    
3052

    
3053
# Declaring Aliases (Clones of the real radios)
3054
class JT2705M(chirp_common.Alias):
3055
    VENDOR = "Jetstream"
3056
    MODEL = "JT2705M"
3057

    
3058

    
3059
class JT6188Mini(chirp_common.Alias):
3060
    VENDOR = "Juentai"
3061
    MODEL = "JT-6188 Mini"
3062

    
3063

    
3064
class JT6188Plus(chirp_common.Alias):
3065
    VENDOR = "Juentai"
3066
    MODEL = "JT-6188 Plus"
3067

    
3068

    
3069
class SSGT890(chirp_common.Alias):
3070
    VENDOR = "Sainsonic"
3071
    MODEL = "GT-890"
3072

    
3073

    
3074
class ZastoneMP300(chirp_common.Alias):
3075
    VENDOR = "Zastone"
3076
    MODEL = "MP-300"
3077

    
3078

    
3079
# real radios
3080
@directory.register
3081
class UV2501(BTech):
3082
    """Baofeng Tech UV2501"""
3083
    MODEL = "UV-2501"
3084
    _fileid = [UV2501G3_fp,
3085
               UV2501G2_fp,
3086
               UV2501pp2_fp,
3087
               UV2501pp_fp]
3088

    
3089

    
3090
@directory.register
3091
class UV2501_220(BTech):
3092
    """Baofeng Tech UV2501+220"""
3093
    MODEL = "UV-2501+220"
3094
    BANDS = 3
3095
    _magic = MSTRING_220
3096
    _id2 = UV2501_220pp_id
3097
    _fileid = [UV2501_220G3_fp,
3098
               UV2501_220G2_fp,
3099
               UV2501_220_fp,
3100
               UV2501_220pp_fp]
3101

    
3102

    
3103
@directory.register
3104
class UV5001(BTech):
3105
    """Baofeng Tech UV5001"""
3106
    MODEL = "UV-5001"
3107
    _fileid = [UV5001G3_fp,
3108
               UV5001G22_fp,
3109
               UV5001G2_fp,
3110
               UV5001alpha_fp,
3111
               UV5001pp_fp]
3112
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3113
                     chirp_common.PowerLevel("Low", watts=10)]
3114

    
3115

    
3116
@directory.register
3117
class MINI8900(BTech):
3118
    """WACCOM MINI-8900"""
3119
    VENDOR = "WACCOM"
3120
    MODEL = "MINI-8900"
3121
    _magic = MSTRING_MINI8900
3122
    _fileid = [MINI8900_fp, ]
3123
    # Clones
3124
    ALIASES = [JT6188Plus, ]
3125

    
3126

    
3127
@directory.register
3128
class KTUV980(BTech):
3129
    """QYT KT-UV980"""
3130
    VENDOR = "QYT"
3131
    MODEL = "KT-UV980"
3132
    _vhf_range = (136000000, 175000000)
3133
    _uhf_range = (400000000, 481000000)
3134
    _magic = MSTRING_MINI8900
3135
    _fileid = [KTUV980_fp, ]
3136
    # Clones
3137
    ALIASES = [JT2705M, ]
3138

    
3139
# Please note that there is a version of this radios that is a clone of the
3140
# Waccom Mini8900, maybe an early version?
3141
@directory.register
3142
class KT9800(BTech):
3143
    """QYT KT8900"""
3144
    VENDOR = "QYT"
3145
    MODEL = "KT8900"
3146
    _vhf_range = (136000000, 175000000)
3147
    _uhf_range = (400000000, 481000000)
3148
    _magic = MSTRING_KT8900
3149
    _fileid = [KT8900_fp,
3150
               KT8900_fp1,
3151
               KT8900_fp2,
3152
               KT8900_fp3,
3153
               KT8900_fp4,
3154
               KT8900_fp5]
3155
    _id2 = KT8900_id
3156
    # Clones
3157
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3158

    
3159

    
3160
@directory.register
3161
class KT9800R(BTech):
3162
    """QYT KT8900R"""
3163
    VENDOR = "QYT"
3164
    MODEL = "KT8900R"
3165
    BANDS = 3
3166
    _vhf_range = (136000000, 175000000)
3167
    _220_range = (240000000, 271000000)
3168
    _uhf_range = (400000000, 481000000)
3169
    _magic = MSTRING_KT8900R
3170
    _fileid = [KT8900R_fp,
3171
               KT8900R_fp1,
3172
               KT8900R_fp2,
3173
               KT8900R_fp3,
3174
               KT8900R_fp4]
3175
    _id2 = KT8900R_id
3176

    
3177

    
3178
@directory.register
3179
class LT588UV(BTech):
3180
    """LUITON LT-588UV"""
3181
    VENDOR = "LUITON"
3182
    MODEL = "LT-588UV"
3183
    _vhf_range = (136000000, 175000000)
3184
    _uhf_range = (400000000, 481000000)
3185
    _magic = MSTRING_KT8900
3186
    _fileid = [LT588UV_fp,
3187
               LT588UV_fp1]
3188
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
3189
                     chirp_common.PowerLevel("Low", watts=10)]
3190

    
3191

    
3192
COLOR_MEM_FORMAT = """
3193
#seekto 0x0000;
3194
struct {
3195
  lbcd rxfreq[4];
3196
  lbcd txfreq[4];
3197
  ul16 rxtone;
3198
  ul16 txtone;
3199
  u8 unknown0:4,
3200
     scode:4;
3201
  u8 unknown1:2,
3202
     spmute:1,
3203
     unknown2:3,
3204
     optsig:2;
3205
  u8 unknown3:3,
3206
     scramble:1,
3207
     unknown4:3,
3208
     power:1;
3209
  u8 unknown5:1,
3210
     wide:1,
3211
     unknown6:2,
3212
     bcl:1,
3213
     add:1,
3214
     pttid:2;
3215
} memory[200];
3216

    
3217
#seekto 0x0E00;
3218
struct {
3219
  u8 tmr;           // e00 TMR
3220
  u8 unknown1;      // e01
3221
  u8 sql;           // e02 SQL
3222
  u8 unknown2[2];   // e03 e04
3223
  u8 tot;           // e05 TOT
3224
  u8 apo;           // e06 APO
3225
  u8 unknown3;      // e07
3226
  u8 abr;           // e08 ABR
3227
  u8 beep;          // e09 BEEP
3228
  u8 unknown4[4];   // e0a e0b e0c e0d
3229
  u8 dtmfst;        // e0e DTMFST
3230
  u8 unknown5[2];   // e0f e10
3231
  u8 screv;         // e11 SC-REV
3232
  u8 unknown_e12;   // e12
3233
  u8 unknown_e13;   // e13
3234
  u8 pttid;         // e14 PTT-ID
3235
  u8 pttlt;         // e15 PTT-LT
3236
  u8 unknown_e16;   // e16
3237
  u8 emctp;         // e17 EMC-TP
3238
  u8 emcch;         // e18 EMC-CH
3239
  u8 sigbp;         // e19 SIG-BP  BTech radios use this as the roger beep
3240
                    //             setting, other radios use this as ring rime
3241
  u8 unknown_e1a;   // e1a
3242
  u8 camdf;         // e1b CA-MDF
3243
  u8 cbmdf;         // e1c CB-MDF
3244
  u8 ccmdf;         // e1d CC-MDF
3245
  u8 cdmdf;         // e1e CD-MDF
3246
  u8 langua;        // e1f LANGUA
3247
  u8 sync;          // e20 SYNC    BTech radios use this as the display sync
3248
                    //             setting, other radios use this as the auto
3249
                    //             keypad lock setting
3250
  u8 mainfc;        // e21 MAINFC WHITE
3251
  u8 mainbc;        // e22 MAINBC BLUE
3252
  u8 menufc;        // e23 MENUFC YELLOW
3253
  u8 menubc;        // e24 MENUBC BLACK
3254
  u8 stafc;         // e25 STA-FC WHITE
3255
  u8 stabc;         // e26 STA-BC BLACK
3256
  u8 sigfc;         // e27 SIG-FC GREEN
3257
  u8 sigbc;         // e28 SIG-BC BLACK
3258
  u8 rxfc;          // e29 RX-FC GREEN
3259
  u8 txfc;          // e2a TX-FC RED
3260
  u8 txdisp;        // e2b TXDISP
3261
  u8 unknown_e2c;   // e2c
3262
  u8 unknown_e2d;   // e2d
3263
  u8 unknown_e2e;   // e2e
3264
  u8 unknown_e2f;   // e2f
3265
  u8 unknown_e30;   // e30
3266
  u8 anil;          // e31 ANI-L
3267
  u8 reps;          // e32 REP-S
3268
  u8 repm;          // e33 REP-M
3269
  u8 tmrmr;         // e34 TMR-MR
3270
  u8 ste;           // e35 STE
3271
  u8 rpste;         // e36 RP-STE
3272
  u8 rptdl;         // e37 RPT-DL
3273
  u8 dtmfg;         // e38 DTMF-G
3274
  u8 mgain;         // e39 M-GAIN BTech only
3275
  u8 skiptx;        // e3a SKIPTX BTech only
3276
  u8 scmode;        // e3b SC-MODE
3277
} settings;
3278

    
3279
#seekto 0x0E80;
3280
struct {
3281
  u8 unknown1;      // e80
3282
  u8 vfomr;         // e81
3283
  u8 keylock;       // e82
3284
  u8 unknown2;      // e83
3285
  u8 unknown3:4,    // e84
3286
     vfomren:1,
3287
     unknown4:1,
3288
     reseten:1,
3289
     menuen:1;
3290
  u8 unknown5[11];  // e85 e86 e87 e88 e89 e8a e8b e8c e8d e8e e8f
3291
  u8 dispab;        // e90
3292
  u8 unknown6[2];   // e91 e92
3293
  u8 menu;          // e93
3294
  u8 unknown7[7];   // e94 e95 e96 e97 e98 e99 e9a
3295
  u8 vfomra;        // e9b
3296
  u8 vfomrb;        // e9c
3297
  u8 vfomrc;        // e9d
3298
  u8 vfomrd;        // e9e
3299
  u8 mrcha;         // e9f
3300
  u8 mrchb;         // ea0
3301
  u8 mrchc;         // ea1
3302
  u8 mrchd;         // ea2
3303
} settings2;
3304

    
3305
struct settings_vfo {
3306
  u8 freq[8];
3307
  u8 offset[6];
3308
  u8 unknown2[2];
3309
  ul16 rxtone;
3310
  ul16 txtone;
3311
  u8 scode;
3312
  u8 spmute;
3313
  u8 optsig;
3314
  u8 scramble;
3315
  u8 wide;
3316
  u8 power;
3317
  u8 shiftd;
3318
  u8 step;
3319
  u8 unknown3[4];
3320
};
3321

    
3322
#seekto 0x0F00;
3323
struct {
3324
  struct settings_vfo a;
3325
  struct settings_vfo b;
3326
  struct settings_vfo c;
3327
  struct settings_vfo d;
3328
} vfo;
3329

    
3330
#seekto 0x0F80;
3331
struct {
3332
  char line1[8];
3333
  char line2[8];
3334
  char line3[8];
3335
  char line4[8];
3336
  char line5[8];
3337
  char line6[8];
3338
  char line7[8];
3339
  char line8[8];
3340
} poweron_msg;
3341

    
3342
#seekto 0x1000;
3343
struct {
3344
  char name[8];
3345
  u8 unknown1[8];
3346
} names[200];
3347

    
3348
#seekto 0x2400;
3349
struct {
3350
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3351
  u8 group_tone;
3352
  u8 repeat_tone;
3353
  u8 unused[13];
3354
} _5tone_std_settings[15];
3355

    
3356
#seekto 0x2500;
3357
struct {
3358
  u8 frame1[5];
3359
  u8 frame2[5];
3360
  u8 frame3[5];
3361
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3362
} _5tone_codes[15];
3363

    
3364
#seekto 0x25F0;
3365
struct {
3366
  u8 _5tone_delay1; // * 10ms
3367
  u8 _5tone_delay2; // * 10ms
3368
  u8 _5tone_delay3; // * 10ms
3369
  u8 _5tone_first_digit_ext_length;
3370
  u8 unknown1;
3371
  u8 unknown2;
3372
  u8 unknown3;
3373
  u8 unknown4;
3374
  u8 decode_standard;
3375
  u8 unknown5:5,
3376
     _5tone_decode_call_frame3:1,
3377
     _5tone_decode_call_frame2:1,
3378
     _5tone_decode_call_frame1:1;
3379
  u8 unknown6:5,
3380
     _5tone_decode_disp_frame3:1,
3381
     _5tone_decode_disp_frame2:1,
3382
     _5tone_decode_disp_frame1:1;
3383
  u8 decode_reset_time; // * 100 + 100ms
3384
} _5tone_settings;
3385

    
3386
#seekto 0x2900;
3387
struct {
3388
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3389
} dtmf_codes[15];
3390

    
3391
#seekto 0x29F0;
3392
struct {
3393
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3394
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3395
  u8 unknown0[14];
3396
  u8 inspection[16];
3397
  u8 monitor[16];
3398
  u8 alarmcode[16];
3399
  u8 stun[16];
3400
  u8 kill[16];
3401
  u8 revive[16];
3402
  u8 unknown1[16];
3403
  u8 unknown2[16];
3404
  u8 unknown3[16];
3405
  u8 unknown4[16];
3406
  u8 unknown5[16];
3407
  u8 unknown6[16];
3408
  u8 unknown7[16];
3409
  u8 masterid[16];
3410
  u8 viceid[16];
3411
  u8 unused01:7,
3412
     mastervice:1;
3413
  u8 unused02:3,
3414
     mrevive:1,
3415
     mkill:1,
3416
     mstun:1,
3417
     mmonitor:1,
3418
     minspection:1;
3419
  u8 unused03:3,
3420
     vrevive:1,
3421
     vkill:1,
3422
     vstun:1,
3423
     vmonitor:1,
3424
     vinspection:1;
3425
  u8 unused04:6,
3426
     txdisable:1,
3427
     rxdisable:1;
3428
  u8 groupcode;
3429
  u8 spacecode;
3430
  u8 delayproctime; // * 100 + 100ms
3431
  u8 resettime;     // * 100 + 100ms
3432
} dtmf_settings;
3433

    
3434
#seekto 0x2D00;
3435
struct {
3436
  struct {
3437
    ul16 freq1;
3438
    u8 unused01[6];
3439
    ul16 freq2;
3440
    u8 unused02[6];
3441
  } _2tone_encode[15];
3442
  u8 duration_1st_tone; // *10ms
3443
  u8 duration_2nd_tone; // *10ms
3444
  u8 duration_gap;      // *10ms
3445
  u8 unused03[13];
3446
  struct {
3447
    struct {
3448
      u8 dec;      // one out of LIST_2TONE_DEC
3449
      u8 response; // one out of LIST_2TONE_RESPONSE
3450
      u8 alert;    // 1-16
3451
    } decs[4];
3452
    u8 unused04[4];
3453
  } _2tone_decode[15];
3454
  u8 unused05[16];
3455

    
3456
  struct {
3457
    ul16 freqA;
3458
    ul16 freqB;
3459
    ul16 freqC;
3460
    ul16 freqD;
3461
    // unknown what those values mean, but they are
3462
    // derived from configured frequencies
3463
    ul16 derived_from_freqA; // 2304000/freqA
3464
    ul16 derived_from_freqB; // 2304000/freqB
3465
    ul16 derived_from_freqC; // 2304000/freqC
3466
    ul16 derived_from_freqD; // 2304000/freqD
3467
  }freqs[15];
3468
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3469
} _2tone;
3470

    
3471
#seekto 0x3000;
3472
struct {
3473
  u8 freq[8];
3474
  char broadcast_station_name[6];
3475
  u8 unknown[2];
3476
} fm_radio_preset[16];
3477

    
3478
#seekto 0x3D80;
3479
struct {
3480
  u8 vhf_low[3];
3481
  u8 vhf_high[3];
3482
  u8 unknown1[4];
3483
  u8 unknown2[6];
3484
  u8 vhf2_low[3];
3485
  u8 vhf2_high[3];
3486
  u8 unknown3[4];
3487
  u8 unknown4[6];
3488
  u8 uhf_low[3];
3489
  u8 uhf_high[3];
3490
  u8 unknown5[4];
3491
  u8 unknown6[6];
3492
  u8 uhf2_low[3];
3493
  u8 uhf2_high[3];
3494
} ranges;
3495

    
3496
#seekto 0x3F70;
3497
struct {
3498
  char fp[6];
3499
} fingerprint;
3500

    
3501
"""
3502

    
3503

    
3504
class BTechColor(BTechMobileCommon):
3505
    """BTECH's Color LCD Mobile and alike radios"""
3506
    COLOR_LCD = True
3507
    NAME_LENGTH = 8
3508

    
3509
    def process_mmap(self):
3510
        """Process the mem map into the mem object"""
3511

    
3512
        # Get it
3513
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3514

    
3515
        # load specific parameters from the radio image
3516
        self.set_options()
3517

    
3518
    def set_options(self):
3519
        """This is to read the options from the image and set it in the
3520
        environment, for now just the limits of the freqs in the VHF/UHF
3521
        ranges"""
3522

    
3523
        # setting the correct ranges for each radio type
3524
        ranges = self._memobj.ranges
3525

    
3526
        # the normal dual bands
3527
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3528
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3529

    
3530
        # DEBUG
3531
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3532
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3533

    
3534
        # the additional bands
3535
        if self.MODEL in ["KT8900D", ]:
3536
            # 200Mhz band
3537
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3538
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3539
            self._220_range = vhf2
3540

    
3541
            # 350Mhz band
3542
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
3543
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
3544
            self._350_range = uhf2
3545

    
3546
        # set the class with the real data
3547
        self._vhf_range = vhf
3548
        self._uhf_range = uhf
3549
    
3550

    
3551
@directory.register
3552
class KT7900D(BTechColor):
3553
    """QYT KT7900D"""
3554
    VENDOR = "QYT"
3555
    MODEL = "KT7900D"
3556
    BANDS = 4
3557
    _vhf_range = (136000000, 175000000)
3558
    _220_range = (200000000, 271000000)
3559
    _uhf_range = (400000000, 481000000)
3560
    _350_range = (350000000, 371000000)
3561
    _magic = MSTRING_KT8900D
3562
    _fileid = [KT7900D_fp, ]
3563

    
3564

    
3565
@directory.register
3566
class KT8900D(BTechColor):
3567
    """QYT KT8900D"""
3568
    VENDOR = "QYT"
3569
    MODEL = "KT8900D"
3570
    BANDS = 2
3571
    _vhf_range = (136000000, 175000000)
3572
    _uhf_range = (400000000, 481000000)
3573
    _magic = MSTRING_KT8900D
3574
    _fileid = [KT8900D_fp, ]
3575

    
(20-20/31)