Project

General

Profile

New Model #4269 » btech_kt8900d_test3.py

3rd QYT KT7900D/KT8900D test driver module - Jim Unroe, 02/07/2017 05:22 PM

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

    
18
import time
19
import struct
20
import logging
21

    
22
LOG = logging.getLogger(__name__)
23

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

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

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

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

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

    
92
# lists related to DTMF, 2TONE and 5TONE settings
93
LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
94
                        "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
95
                        "CCITT", "NATEL", "MODAT", "none"]
96
LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
97
                                     "ZVEI2", "ZVEI3",
98
                                     "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
99
                                     "EURO", "CCITT", "NATEL", "MODAT"]
100
LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
101
                               "100", "110", "120", "130", "140", "150", "160",
102
                               "170", "180", "190", "200"]
103
LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
104
                     "B", "C", "D", "E", "F"]
105
LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
106
LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
107
LIST_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
KT8900D_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
        ringt = RadioSetting("settings.ringt", "Ring time",
1075
                             RadioSettingValueList(
1076
                                 LIST_OFF1TO9,
1077
                                 LIST_OFF1TO9[_mem.settings.ringt]))
1078
        basic.append(ringt)
1079

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1433

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

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

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

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

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

    
1479

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1669

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2152

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

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

    
2161
        top.append(group_5tone)
2162

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2542
            i = i + 1
2543

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2659
            i = i + 1
2660

    
2661
        return top
2662

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

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

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

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

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

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

    
2717

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3001
"""
3002

    
3003

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

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

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

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

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

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

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

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

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

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

    
3051

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

    
3057

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

    
3062

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

    
3067

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

    
3072

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

    
3077

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

    
3088

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

    
3101

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

    
3114

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

    
3125

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

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

    
3158

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

    
3176

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

    
3190

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
3500
"""
3501

    
3502

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

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

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

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

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

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

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

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

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

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

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

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

    
3563

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

    
(15-15/31)