Project

General

Profile

New Model #6211 ยป btech_vc5264.py

Adds support for MCU VC5302 and CRT CRT-279UV selection - Jim Unroe, 11/18/2022 07:15 PM

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

    
18
import time
19
import struct
20
import logging
21

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

    
31
LOG = logging.getLogger(__name__)
32

    
33
# A note about the memory 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_ABC = LIST_AB + ["C"]
59
LIST_ABCD = LIST_AB + ["C", "D"]
60
LIST_ANIL = ["3", "4", "5"]
61
LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
62
LIST_COLOR4 = ["Off", "Blue", "Orange", "Purple"]
63
LIST_COLOR8 = ["White", "Red", "Blue", "Green", "Yellow", "Indigo",
64
               "Purple", "Gray"]
65
LIST_COLOR9 = ["Black"] + LIST_COLOR8
66
LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboard + ANI"]
67
LIST_EARPH = ["Off", "Earphone", "Earphone + Speaker"]
68
LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
69
LIST_EMCTPX = ["Off"] + LIST_EMCTP
70
LIST_LANGUA = ["English", "Chinese"]
71
LIST_MDF = ["Frequency", "Channel", "Name"]
72
LIST_OFF1TO9 = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
73
LIST_OFF1TO10 = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
74
LIST_OFF1TO50 = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
75
LIST_OFF1TO60 = ["Off"] + ["%s seconds" % x for x in range(1, 61)]
76
LIST_PONMSG = ["Full", "Message", "Battery voltage"]
77
LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
78
LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
79
LIST_REPSW = ["Off", "RX", "TX"]
80
LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 11)]
81
LIST_SCMODE = ["Off", "PTT-SC", "MEM-SC", "PON-SC"]
82
LIST_SHIFT = ["Off", "+", "-"]
83
LIST_SKIPTX = ["Off", "Skip 1", "Skip 2"]
84
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
85
LIST_STEP = [str(x) for x in STEPS]
86
LIST_SYNC = ["Off", "AB", "CD", "AB+CD"]
87
LIST_SYNCV2 = ["Off", "AB", "AC", "BC", "ABC"]
88
# the first 12 TMR choices common to all 4-line color display mobile radios
89
LIST_TMR12 = ["OFF", "M+A", "M+B", "M+C", "M+D", "M+A+B", "M+A+C", "M+A+D",
90
              "M+B+C", "M+B+D", "M+C+D", "M+A+B+C"]
91
# the 16 choice list for color display mobile radios that correctly implement
92
# the full 16 TMR choices
93
LIST_TMR16 = LIST_TMR12 + ["M+A+B+D", "M+A+C+D", "M+B+C+D", "A+B+C+D"]
94
# the 15 choice list for color mobile radios that are missing the M+A+B+D
95
# choice in the TMR menu
96
LIST_TMR15 = LIST_TMR12 + ["M+A+C+D", "M+B+C+D", "A+B+C+D"]
97
# the 7 TMR choices for the 3-line color display mobile radios
98
LIST_TMR7 = ["OFF", "M+A", "M+B", "M+C", "M+AB", "M+AC", "M+BC", "M+ABC"]
99
LIST_TMRTX = ["Track", "Fixed"]
100
LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
101
LIST_TXDISP = ["Power", "Mic Volume"]
102
LIST_TXP = ["High", "Low"]
103
LIST_TXP3 = ["High", "Mid", "Low"]
104
LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
105
LIST_VFOMR = ["Frequency", "Channel"]
106
LIST_VOICE = ["Off"] + LIST_LANGUA
107
LIST_VOX = ["Off"] + ["%s" % x for x in range(1, 11)]
108
LIST_VOXT = ["%s seconds" % x for x in range(0, 21)]
109
LIST_WIDE = ["Wide", "Narrow"]
110

    
111
# lists related to DTMF, 2TONE and 5TONE settings
112
LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
113
                        "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
114
                        "CCITT", "NATEL", "MODAT", "none"]
115
LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
116
                                     "ZVEI2", "ZVEI3",
117
                                     "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
118
                                     "EURO", "CCITT", "NATEL", "MODAT"]
119
LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
120
                               "100", "110", "120", "130", "140", "150", "160",
121
                               "170", "180", "190", "200"]
122
LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
123
                     "B", "C", "D", "E", "F"]
124
LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
125
LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
126
LIST_5TONE_RESET_COLOR = ["%s ms" % x for x in range(100, 20100, 100)]
127
LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
128
LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
129
                    "C", "D", "#", "*"]
130
LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
131
                    0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B]
132
LIST_DTMF_SPECIAL_DIGITS = ["*", "#", "A", "B", "C", "D"]
133
LIST_DTMF_SPECIAL_VALUES = [0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
134
LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
135
CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
136
LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
137
                  "B-A", "B-C", "B-D",
138
                  "C-A", "C-B", "C-D",
139
                  "D-A", "D-B", "D-C"]
140
LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
141

    
142
# This is a general serial timeout for all serial read functions.
143
STIMEOUT = 0.25
144

    
145
# this var controls the verbosity in the debug and by default it's low (False)
146
# make it True and you will to get a very verbose debug.log
147
debug = False
148

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

    
153
GMRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
154
               462.6875, 462.7125]
155
GMRS_FREQS2 = [467.5625, 467.5875, 467.6125, 467.6375, 467.6625,
156
               467.6875, 467.7125]
157
GMRS_FREQS3 = [462.5500, 462.5750, 462.6000, 462.6250, 462.6500,
158
               462.6750, 462.7000, 462.7250]
159
GMRS_FREQS = GMRS_FREQS1 + GMRS_FREQS2 + GMRS_FREQS3 * 2
160

    
161

    
162
# #### ID strings #####################################################
163

    
164
# BTECH UV2501 pre-production units
165
UV2501pp_fp = "M2C294"
166
# BTECH UV2501 pre-production units 2 + and 1st Gen radios
167
UV2501pp2_fp = "M29204"
168
# B-TECH UV-2501 second generation (2G) radios
169
UV2501G2_fp = "BTG214"
170
# B-TECH UV-2501 third generation (3G) radios
171
UV2501G3_fp = "BTG324"
172

    
173
# B-TECH UV-2501+220 pre-production units
174
UV2501_220pp_fp = "M3C281"
175
# B-TECH UV-2501+220
176
UV2501_220_fp = "M3G201"
177
# new variant, let's call it Generation 2
178
UV2501_220G2_fp = "BTG211"
179
# B-TECH UV-2501+220 third generation (3G)
180
UV2501_220G3_fp = "BTG311"
181

    
182
# B-TECH UV-5001 pre-production units + 1st Gen radios
183
UV5001pp_fp = "V19204"
184
# B-TECH UV-5001 alpha units
185
UV5001alpha_fp = "V28204"
186
# B-TECH UV-5001 second generation (2G) radios
187
UV5001G2_fp = "BTG214"
188
# B-TECH UV-5001 second generation (2G2)
189
UV5001G22_fp = "V2G204"
190
# B-TECH UV-5001 third generation (3G)
191
UV5001G3_fp = "BTG304"
192

    
193
# B-TECH UV-25X2
194
UV25X2_fp = "UC2012"
195

    
196
# B-TECH UV-25X4
197
UV25X4_fp = "UC4014"
198

    
199
# B-TECH UV-50X2
200
UV50X2_fp = "UC2M12"
201

    
202
# B-TECH GMRS-50X1
203
GMRS50X1_fp = "NC1802"
204
GMRS50X1_fp1 = "NC1932"
205

    
206
# B-TECH GMRS-20V2
207
GMRS20V2_fp = "WP4204"
208
GMRS20V2_fp1 = "VW2112"
209
GMRS20V2_fp2 = "VWG728"
210

    
211
# special var to know when we found a BTECH Gen 3
212
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
213

    
214

    
215
# WACCOM Mini-8900
216
MINI8900_fp = "M28854"
217

    
218

    
219
# QYT KT-UV980
220
KTUV980_fp = "H28854"
221

    
222
# QYT KT8900
223
KT8900_fp = "M29154"
224
# New generations KT8900
225
KT8900_fp1 = "M2C234"
226
KT8900_fp2 = "M2G1F4"
227
KT8900_fp3 = "M2G2F4"
228
KT8900_fp4 = "M2G304"
229
KT8900_fp5 = "M2G314"
230
KT8900_fp6 = "M2G424"
231
KT8900_fp7 = "M27184"
232

    
233
# KT8900R
234
KT8900R_fp = "M3G1F4"
235
# Second Generation
236
KT8900R_fp1 = "M3G214"
237
# another model
238
KT8900R_fp2 = "M3C234"
239
# another model G4?
240
KT8900R_fp3 = "M39164"
241
# another model
242
KT8900R_fp4 = "M3G314"
243
# AC3MB: another id
244
KT8900R_fp5 = "M3B064"
245

    
246
# KT7900D (quad band)
247
KT7900D_fp = "VC4004"
248
KT7900D_fp1 = "VC4284"
249
KT7900D_fp2 = "VC4264"
250
KT7900D_fp3 = "VC4114"
251
KT7900D_fp4 = "VC4104"
252
KT7900D_fp5 = "VC4254"
253
KT7900D_fp6 = "VC5264"
254
KT7900D_fp7 = "VC9204"
255
KT7900D_fp8 = "VC9214"
256
KT7900D_fp9 = "VC5264"
257

    
258
# QB25 (quad band) - a clone of KT7900D
259
QB25_fp = "QB-25"
260

    
261
# KT8900D (dual band)
262
KT8900D_fp = "VC2002"
263
KT8900D_fp1 = "VC8632"
264
KT8900D_fp2 = "VC3402"
265
KT8900D_fp3 = "VC7062"
266

    
267
# LUITON LT-588UV
268
LT588UV_fp = "V2G1F4"
269
# Added by rstrickoff gen 2 id
270
LT588UV_fp1 = "V2G214"
271

    
272
# QYT KT-8R (quad band ht)
273
KT8R_fp = "MCB264"
274
KT8R_fp1 = "MCB284"
275
KT8R_fp2 = "MC5264"
276

    
277
# QYT KT5800 (dual band)
278
KT5800_fp = "VCB222"
279

    
280
# QYT KT980Plus (dual band)
281
KT980PLUS_fp = "VC2002"
282
KT980PLUS_fp1 = "VC6042"
283

    
284
# Radioddity DB25-G (gmrs)
285
DB25G_fp = "VC6182"
286
DB25G_fp1 = "VC7062"
287

    
288
# QYT KT-WP12 and KT-9900
289
KTWP12_fp = "WP3094"
290

    
291
# Anysecu WP-9900
292
WP9900_fp = "WP3094"
293

    
294

    
295
# ### MAGICS
296
# for the Waccom Mini-8900
297
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
298
# for the B-TECH UV-2501+220 (including pre production ones)
299
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
300
# for the QYT KT8900 & R
301
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
302
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
303
# magic string for all other models
304
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
305
# for the QYT KT7900D & KT8900D
306
MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
307
# for the BTECH UV-25X2 and UV-50X2
308
MSTRING_UV25X2 = "\x55\x20\x16\x12\x28\xFF\xDC\x02"
309
# for the BTECH UV-25X4
310
MSTRING_UV25X4 = "\x55\x20\x16\x11\x18\xFF\xDC\x02"
311
# for the BTECH GMRS-50X1
312
MSTRING_GMRS50X1 = "\x55\x20\x18\x10\x18\xFF\xDC\x02"
313
# for the BTECH GMRS-20V2
314
MSTRING_GMRS20V2 = "\x55\x20\x21\x03\x27\xFF\xDC\x02"
315
# for the QYT KT-8R
316
MSTRING_KT8R = "\x55\x20\x17\x07\x03\xFF\xDC\x02"
317
# for the QYT KT-WP12, KT-9900 and Anysecu WP-9900
318
MSTRING_KTWP12 = "\x55\x20\x18\x11\x02\xFF\xDC\x02"
319

    
320

    
321
def _clean_buffer(radio):
322
    """Cleaning the read serial buffer, hard timeout to survive an infinite
323
    data stream"""
324

    
325
    # touching the serial timeout to optimize the flushing
326
    # restored at the end to the default value
327
    radio.pipe.timeout = 0.1
328
    dump = "1"
329
    datacount = 0
330

    
331
    try:
332
        while len(dump) > 0:
333
            dump = radio.pipe.read(100)
334
            datacount += len(dump)
335
            # hard limit to survive a infinite serial data stream
336
            # 5 times bigger than a normal rx block (69 bytes)
337
            if datacount > 345:
338
                seriale = "Please check your serial port selection."
339
                raise errors.RadioError(seriale)
340

    
341
        # restore the default serial timeout
342
        radio.pipe.timeout = STIMEOUT
343

    
344
    except Exception:
345
        raise errors.RadioError("Unknown error cleaning the serial buffer")
346

    
347

    
348
def _rawrecv(radio, amount):
349
    """Raw read from the radio device, less intensive way"""
350

    
351
    data = ""
352

    
353
    try:
354
        data = radio.pipe.read(amount)
355

    
356
        # DEBUG
357
        if debug is True:
358
            LOG.debug("<== (%d) bytes:\n\n%s" %
359
                      (len(data), util.hexprint(data)))
360

    
361
        # fail if no data is received
362
        if len(data) == 0:
363
            raise errors.RadioError("No data received from radio")
364

    
365
        # notice on the logs if short
366
        if len(data) < amount:
367
            LOG.warn("Short reading %d bytes from the %d requested." %
368
                     (len(data), amount))
369

    
370
    except:
371
        raise errors.RadioError("Error reading data from radio")
372

    
373
    return data
374

    
375

    
376
def _send(radio, data):
377
    """Send data to the radio device"""
378

    
379
    try:
380
        radio.pipe.write(data)
381

    
382
        # DEBUG
383
        if debug is True:
384
            LOG.debug("==> (%d) bytes:\n\n%s" %
385
                      (len(data), util.hexprint(data)))
386
    except:
387
        raise errors.RadioError("Error sending data to radio")
388

    
389

    
390
def _make_frame(cmd, addr, length, data=""):
391
    """Pack the info in the headder format"""
392
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
393
    # add the data if set
394
    if len(data) != 0:
395
        frame += data
396

    
397
    return frame
398

    
399

    
400
def _recv(radio, addr):
401
    """Get data from the radio all at once to lower syscalls load"""
402

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

    
406
    # get the whole block
407
    block = _rawrecv(radio, BLOCK_SIZE + 5)
408

    
409
    # basic check
410
    if len(block) < (BLOCK_SIZE + 5):
411
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
412

    
413
    # checking for the ack
414
    if block[0] != ACK_CMD:
415
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
416

    
417
    # header validation
418
    c, a, l = struct.unpack(">BHB", block[1:5])
419
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
420
        LOG.debug("Invalid header for block 0x%04x" % addr)
421
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
422
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
423

    
424
    # return the data
425
    return block[5:]
426

    
427

    
428
def _do_ident(radio, status, upload=False):
429
    """Put the radio in PROGRAM mode & identify it"""
430
    #  set the serial discipline
431
    radio.pipe.baudrate = 9600
432
    radio.pipe.parity = "N"
433

    
434
    # lengthen the timeout here as these radios are resetting due to timeout
435
    radio.pipe.timeout = 0.75
436

    
437
    # send the magic word
438
    _send(radio, radio._magic)
439

    
440
    # Now you get a 50 byte reply if all goes well
441
    ident = _rawrecv(radio, 50)
442

    
443
    # checking for the ack
444
    if ident[0] != ACK_CMD:
445
        raise errors.RadioError("Bad ack from radio")
446

    
447
    # basic check for the ident block
448
    if len(ident) != 50:
449
        raise errors.RadioError("Radio send a short ident block.")
450

    
451
    # check if ident is OK
452
    itis = False
453
    for fp in radio._fileid:
454
        if fp in ident:
455
            # got it!
456
            itis = True
457
            # checking if we are dealing with a Gen 3 BTECH
458
            if radio.VENDOR == "BTECH" and fp in BTECH3:
459
                radio.btech3 = True
460

    
461
            break
462

    
463
    if itis is False:
464
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
465
        raise errors.RadioError("Radio identification failed.")
466

    
467
    # pause here for the radio to catch up
468
    sleep(0.1)
469

    
470
    # the OEM software reads this additional block, so we will, too
471

    
472
    # Get the full 21 bytes at a time to reduce load
473
    # 1 byte ACK + 4 bytes header + 16 bytes of data (BLOCK_SIZE)
474
    frame = _make_frame("S", 0x3DF0, 16)
475
    _send(radio, frame)
476
    id2 = _rawrecv(radio, 21)
477

    
478
    # restore the default serial timeout
479
    radio.pipe.timeout = STIMEOUT
480

    
481
    # checking for the ack
482
    if id2[0] not in "\x06\x05":
483
        raise errors.RadioError("Bad ack from radio")
484

    
485
    # basic check for the additional block
486
    if len(id2) < 21:
487
        raise errors.RadioError("The extra ID is short, aborting.")
488

    
489
    # this radios need a extra request/answer here on the upload
490
    # the amount of data received depends of the radio type
491
    #
492
    # also the first block of TX must no have the ACK at the beginning
493
    # see _upload for this.
494
    if upload is True:
495
        # send an ACK
496
        _send(radio, ACK_CMD)
497

    
498
        # the amount of data depend on the radio, so far we have two radios
499
        # reading two bytes with an ACK at the end and just ONE with just
500
        # one byte (QYT KT8900)
501
        # the JT-6188 appears a clone of the last, but reads TWO bytes.
502
        #
503
        # we will read two bytes with a custom timeout to not penalize the
504
        # users for this.
505
        #
506
        # we just check for a response and last byte being a ACK, that is
507
        # the common stone for all radios (3 so far)
508
        ack = _rawrecv(radio, 2)
509

    
510
        # checking
511
        if len(ack) == 0 or ack[-1:] != ACK_CMD:
512
            raise errors.RadioError("Radio didn't ACK the upload")
513

    
514
    # DEBUG
515
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
516

    
517
    return True
518

    
519

    
520
def _download(radio):
521
    """Get the memory map"""
522

    
523
    # UI progress
524
    status = chirp_common.Status()
525

    
526
    # put radio in program mode and identify it
527
    _do_ident(radio, status)
528

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

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

    
540
        # read
541
        d = _recv(radio, addr)
542

    
543
        # aggregate the data
544
        data += d
545

    
546
        # UI Update
547
        status.cur = addr / BLOCK_SIZE
548
        status.msg = "Cloning from radio..."
549
        radio.status_fn(status)
550

    
551
    return data
552

    
553

    
554
def _upload(radio):
555
    """Upload procedure"""
556

    
557
    # The UPLOAD mem is restricted to lower than 0x3100,
558
    # so we will override that here localy
559
    MEM_SIZE = radio.UPLOAD_MEM_SIZE
560

    
561
    # UI progress
562
    status = chirp_common.Status()
563

    
564
    # put radio in program mode and identify it
565
    _do_ident(radio, status, True)
566

    
567
    # get the data to upload to radio
568
    data = radio.get_mmap()
569

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

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

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

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

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

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

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

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

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

    
607

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

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

    
615
    return False
616

    
617

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

    
626
    return (ilow, ihigh)
627

    
628

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

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

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

    
643

    
644
class BTechMobileCommon(chirp_common.CloneModeRadio,
645
                        chirp_common.ExperimentalRadio):
646
    """BTECH's UV-5001 and alike radios"""
647
    VENDOR = "BTECH"
648
    MODEL = ""
649
    IDENT = ""
650
    BANDS = 2
651
    COLOR_LCD = False
652
    COLOR_LCD2 = False  # BTech Original GMRS Radios
653
    COLOR_LCD3 = False  # Color HT Radios
654
    COLOR_LCD4 = False  # Waterproof Mobile Radios
655
    NAME_LENGTH = 6
656
    UPLOAD_MEM_SIZE = 0X3100
657
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
658
                     chirp_common.PowerLevel("Low", watts=10)]
659
    _vhf_range = (130000000, 180000000)
660
    _220_range = (200000000, 271000000)
661
    _uhf_range = (400000000, 521000000)
662
    _350_range = (350000000, 391000000)
663
    _upper = 199
664
    _magic = MSTRING
665
    _fileid = None
666
    _id2 = False
667
    btech3 = False
668
    _gmrs = False
669

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

    
684
            1 - Turn off your radio
685
            2 - Connect your interface cable
686
            3 - Turn on your radio
687
            4 - Do the download of your radio data
688

    
689
            """))
690
        rp.pre_upload = _(dedent("""\
691
            Follow these instructions to upload your info:
692

    
693
            1 - Turn off your radio
694
            2 - Connect your interface cable
695
            3 - Turn on your radio
696
            4 - Do the upload of your radio data
697

    
698
            """))
699
        return rp
700

    
701
    def get_features(self):
702
        """Get the radio's features"""
703

    
704
        # we will use the following var as global
705
        global POWER_LEVELS
706

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

    
738
        # power levels
739
        POWER_LEVELS = self._power_levels
740
        rf.valid_power_levels = POWER_LEVELS
741

    
742
        # normal dual bands
743
        rf.valid_bands = [self._vhf_range, self._uhf_range]
744

    
745
        # 220 band
746
        if self.BANDS == 3 or self.BANDS == 4:
747
            rf.valid_bands.append(self._220_range)
748

    
749
        # 350 band
750
        if self.BANDS == 4:
751
            rf.valid_bands.append(self._350_range)
752

    
753
        return rf
754

    
755
    def validate_memory(self, mem):
756
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
757

    
758
        _msg_duplex1 = 'Memory location only supports "Low"'
759
        _msg_duplex2 = 'Memory location only supports "off"'
760
        _msg_duplex3 = 'Memory location only supports "(None)", "+" or "off"'
761

    
762
        return msgs
763

    
764
    def sync_in(self):
765
        """Download from radio"""
766
        data = _download(self)
767
        self._mmap = memmap.MemoryMap(data)
768
        self.process_mmap()
769

    
770
    def sync_out(self):
771
        """Upload to radio"""
772
        try:
773
            _upload(self)
774
        except errors.RadioError:
775
            raise
776
        except Exception, e:
777
            raise errors.RadioError("Error: %s" % e)
778

    
779
    def get_raw_memory(self, number):
780
        return repr(self._memobj.memory[number])
781

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

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

    
800
            tone = DTCS[index]
801
            return 'DTCS', tone, pol
802

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

    
827
    def get_memory(self, number):
828
        """Get the mem representation from the radio image"""
829
        _mem = self._memobj.memory[number]
830
        _names = self._memobj.names[number]
831

    
832
        # Create a high-level memory object to return to the UI
833
        mem = chirp_common.Memory()
834

    
835
        # Memory number
836
        mem.number = number
837

    
838
        if _mem.get_raw()[0] == "\xFF":
839
            mem.empty = True
840
            return mem
841

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

    
866
        # name TAG of the channel
867
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
868

    
869
        # power
870
        mem.power = POWER_LEVELS[int(_mem.power)]
871

    
872
        # wide/narrow
873
        mem.mode = MODES[int(_mem.wide)]
874

    
875
        # skip
876
        mem.skip = SKIP_VALUES[_mem.add]
877

    
878
        # tone data
879
        rxtone = txtone = None
880
        txtone = self._decode_tone(_mem.txtone)
881
        rxtone = self._decode_tone(_mem.rxtone)
882
        chirp_common.split_tone_decode(mem, txtone, rxtone)
883

    
884
        # Extra
885
        mem.extra = RadioSettingGroup("extra", "Extra")
886

    
887
        if not self.COLOR_LCD or \
888
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
889
            scramble = RadioSetting("scramble", "Scramble",
890
                                    RadioSettingValueBoolean(bool(
891
                                        _mem.scramble)))
892
            mem.extra.append(scramble)
893

    
894
        bcl = RadioSetting("bcl", "Busy channel lockout",
895
                           RadioSettingValueBoolean(bool(_mem.bcl)))
896
        mem.extra.append(bcl)
897

    
898
        pttid = RadioSetting("pttid", "PTT ID",
899
                             RadioSettingValueList(PTTID_LIST,
900
                                                   PTTID_LIST[_mem.pttid]))
901
        mem.extra.append(pttid)
902

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

    
911
        optsig = RadioSetting("optsig", "Optional signaling",
912
                              RadioSettingValueList(
913
                                  OPTSIG_LIST,
914
                                  OPTSIG_LIST[_mem.optsig]))
915
        mem.extra.append(optsig)
916

    
917
        spmute = RadioSetting("spmute", "Speaker mute",
918
                              RadioSettingValueList(
919
                                  SPMUTE_LIST,
920
                                  SPMUTE_LIST[_mem.spmute]))
921
        mem.extra.append(spmute)
922

    
923
        return mem
924

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

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

    
938
        # if empty memory
939
        if mem.empty:
940
            # the channel itself
941
            _mem.set_raw("\xFF" * 16)
942
            # the name tag
943
            _names.set_raw("\xFF" * 16)
944
            return
945

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

    
952
        if self._gmrs:
953
            if self.MODEL == "GMRS-50X1":
954
                if mem.number >= 1 and mem.number <= 30:
955
                    GMRS_FREQ = int(GMRS_FREQS[mem.number - 1] * 1000000)
956
                    mem.freq = GMRS_FREQ
957
                    if mem.number <= 22:
958
                        mem.duplex = ''
959
                        mem.offset = 0
960
                        if mem.number <= 7:
961
                            mem.power = POWER_LEVELS[2]
962
                        if mem.number >= 8 and mem.number <= 14:
963
                            mem.duplex = 'off'
964
                            mem.offset = 0
965
                            mem.mode = 'NFM'
966
                            mem.power = POWER_LEVELS[2]
967
                    if mem.number > 22:
968
                        mem.duplex = '+'
969
                        mem.offset = 5000000
970
                else:
971
                    mem.duplex = 'off'
972
                    mem.offset = 0
973
            if self.MODEL == "GMRS-20V2":
974
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
975
                    mem.duplex = ''
976
                    mem.offset = 0
977
                    mem.power = POWER_LEVELS[2]
978
                elif float(mem.freq) / 1000000 in GMRS_FREQS2:
979
                    mem.duplex = 'off'
980
                    mem.offset = 0
981
                    mem.mode = "NFM"
982
                    mem.power = POWER_LEVELS[2]
983
                elif float(mem.freq) / 1000000 in GMRS_FREQS3:
984
                    if mem.power == POWER_LEVELS[1]:
985
                        mem.power = POWER_LEVELS[0]
986
                    if mem.duplex == '+':
987
                        mem.offset = 5000000
988
                    else:
989
                        mem.duplex == ''
990
                        mem.offset = 0
991
                else:
992
                    mem.duplex = 'off'
993
                    mem.offset = 0
994

    
995
        # frequency
996
        _mem.rxfreq = mem.freq / 10
997

    
998
        # duplex
999
        if mem.duplex == "+":
1000
            _mem.txfreq = (mem.freq + mem.offset) / 10
1001
        elif mem.duplex == "-":
1002
            _mem.txfreq = (mem.freq - mem.offset) / 10
1003
        elif mem.duplex == "off":
1004
            for i in _mem.txfreq:
1005
                i.set_raw("\xFF")
1006
        elif mem.duplex == "split":
1007
            _mem.txfreq = mem.offset / 10
1008
        else:
1009
            _mem.txfreq = mem.freq / 10
1010

    
1011
        # tone data
1012
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
1013
            chirp_common.split_tone_encode(mem)
1014
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
1015
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
1016

    
1017
        # name TAG of the channel
1018
        if len(mem.name) < self.NAME_LENGTH:
1019
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
1020
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
1021
        _names.name = str(mem.name).replace(" ", "\xFF")
1022

    
1023
        # power, # default power level is high
1024
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
1025

    
1026
        # wide/narrow
1027
        _mem.wide = MODES.index(mem.mode)
1028

    
1029
        # scan add property
1030
        _mem.add = SKIP_VALUES.index(mem.skip)
1031

    
1032
        # resetting unknowns, this have to be set by hand
1033
        _mem.unknown0 = 0
1034
        _mem.unknown1 = 0
1035
        _mem.unknown2 = 0
1036
        _mem.unknown3 = 0
1037
        _mem.unknown4 = 0
1038
        _mem.unknown5 = 0
1039
        _mem.unknown6 = 0
1040

    
1041
        def _zero_settings():
1042
            _mem.spmute = 0
1043
            _mem.optsig = 0
1044
            _mem.scramble = 0
1045
            _mem.bcl = 0
1046
            _mem.pttid = 0
1047
            _mem.scode = 0
1048

    
1049
        if self.COLOR_LCD and _mem.scramble:
1050
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1051
            _mem.scramble = 0
1052

    
1053
        # extra settings
1054
        if len(mem.extra) > 0:
1055
            # there are setting, parse
1056
            LOG.debug("Extra-Setting supplied. Setting them.")
1057
            # Zero them all first so any not provided by model don't
1058
            # stay set
1059
            _zero_settings()
1060
            for setting in mem.extra:
1061
                setattr(_mem, setting.get_name(), setting.value)
1062
        else:
1063
            if mem.empty:
1064
                LOG.debug("New mem is empty.")
1065
            else:
1066
                LOG.debug("New mem is NOT empty")
1067
                # set extra-settings to default ONLY when apreviously empty or
1068
                # deleted memory was edited to prevent errors such as #4121
1069
                if mem_was_empty:
1070
                    LOG.debug("old mem was empty. Setting default for extras.")
1071
                    _zero_settings()
1072

    
1073
        return mem
1074

    
1075
    def get_settings(self):
1076
        """Translate the bit in the mem_struct into settings in the UI"""
1077
        _mem = self._memobj
1078
        basic = RadioSettingGroup("basic", "Basic Settings")
1079
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1080
        other = RadioSettingGroup("other", "Other Settings")
1081
        work = RadioSettingGroup("work", "Work Mode Settings")
1082
        top = RadioSettings(basic, advanced, other, work)
1083

    
1084
        # Basic
1085
        if self.COLOR_LCD:
1086
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1087
                               RadioSettingValueList(
1088
                                   self.LIST_TMR,
1089
                                   self.LIST_TMR[_mem.settings.tmr]))
1090
            basic.append(tmr)
1091
        else:
1092
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1093
                               RadioSettingValueBoolean(_mem.settings.tdr))
1094
            basic.append(tdr)
1095

    
1096
        sql = RadioSetting("settings.sql", "Squelch level",
1097
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1098
        basic.append(sql)
1099

    
1100
        if self.MODEL == "GMRS-50X1":
1101
            autolk = RadioSetting("settings.autolk", "Auto keylock",
1102
                                  RadioSettingValueBoolean(
1103
                                      _mem.settings.autolk))
1104
            basic.append(autolk)
1105

    
1106
        if self.MODEL == "DB25-G":
1107
            rs = RadioSettingValueInteger(0, 127, _mem.settings.mgain2)
1108
            mgain2 = RadioSetting("settings.mgain2", "Mic gain", rs)
1109
            basic.append(mgain2)
1110

    
1111
        tot = RadioSetting("settings.tot", "Time out timer",
1112
                           RadioSettingValueList(
1113
                               LIST_TOT,
1114
                               LIST_TOT[_mem.settings.tot]))
1115
        basic.append(tot)
1116

    
1117
        if self.MODEL == "KT-8R":
1118
                save = RadioSetting("settings.save", "Battery Save",
1119
                                    RadioSettingValueBoolean(
1120
                                        _mem.settings.save))
1121
                basic.append(save)
1122

    
1123
        model_list = ["KT-8R", "KT-WP12", "WP-9900"]
1124
        if self.MODEL not in model_list:
1125
            if self.VENDOR == "BTECH" or self.COLOR_LCD:
1126
                apo = RadioSetting("settings.apo", "Auto power off timer",
1127
                                   RadioSettingValueList(
1128
                                       LIST_APO,
1129
                                       LIST_APO[_mem.settings.apo]))
1130
                basic.append(apo)
1131
            else:
1132
                toa = RadioSetting("settings.apo", "Time out alert timer",
1133
                                   RadioSettingValueList(
1134
                                       LIST_OFF1TO10,
1135
                                       LIST_OFF1TO10[_mem.settings.apo]))
1136
                basic.append(toa)
1137

    
1138
        abr = RadioSetting("settings.abr", "Backlight timer",
1139
                           RadioSettingValueList(
1140
                               LIST_OFF1TO50,
1141
                               LIST_OFF1TO50[_mem.settings.abr]))
1142
        basic.append(abr)
1143

    
1144
        beep = RadioSetting("settings.beep", "Key beep",
1145
                            RadioSettingValueBoolean(_mem.settings.beep))
1146
        basic.append(beep)
1147

    
1148
        if self.MODEL == "GMRS-20V2":
1149
            rs = RadioSettingValueInteger(0, 58, _mem.settings.volume)
1150
            volume = RadioSetting("settings.volume", "Volume", rs)
1151
            basic.append(volume)
1152

    
1153
        if self.MODEL == "KT-WP12" or self.MODEL == "WP-9900":
1154
            rs = RadioSettingValueInteger(1, 51, _mem.settings.volume + 1)
1155
            volume = RadioSetting("settings.volume", "Volume", rs)
1156
            basic.append(volume)
1157

    
1158
        if self.MODEL == "KT-8R":
1159
            dsub = RadioSetting("settings.dsub", "CTCSS/DCS code display",
1160
                                RadioSettingValueBoolean(
1161
                                    _mem.settings.dsub))
1162
            basic.append(dsub)
1163

    
1164
        if self.MODEL == "KT-8R" or self.COLOR_LCD4:
1165
            dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1166
                                  RadioSettingValueBoolean(
1167
                                      _mem.settings.dtmfst))
1168
            basic.append(dtmfst)
1169
        else:
1170
            dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1171
                                  RadioSettingValueList(
1172
                                      LIST_DTMFST,
1173
                                      LIST_DTMFST[_mem.settings.dtmfst]))
1174
            basic.append(dtmfst)
1175

    
1176
        if not self.COLOR_LCD:
1177
            prisc = RadioSetting("settings.prisc", "Priority scan",
1178
                                 RadioSettingValueBoolean(
1179
                                     _mem.settings.prisc))
1180
            basic.append(prisc)
1181

    
1182
            prich = RadioSetting("settings.prich", "Priority channel",
1183
                                 RadioSettingValueInteger(0, self._upper,
1184
                                                          _mem.settings.prich))
1185
            basic.append(prich)
1186

    
1187
        screv = RadioSetting("settings.screv", "Scan resume method",
1188
                             RadioSettingValueList(
1189
                                 LIST_SCREV,
1190
                                 LIST_SCREV[_mem.settings.screv]))
1191
        basic.append(screv)
1192

    
1193
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1194
                             RadioSettingValueInteger(0, 30,
1195
                                                      _mem.settings.pttlt))
1196
        basic.append(pttlt)
1197

    
1198
        if self.VENDOR == "BTECH" and self.COLOR_LCD and not \
1199
                self.MODEL == "GMRS-20V2":
1200
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1201
                                 RadioSettingValueList(
1202
                                     LIST_EMCTPX,
1203
                                     LIST_EMCTPX[_mem.settings.emctp]))
1204
            basic.append(emctp)
1205
        else:
1206
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1207
                                 RadioSettingValueList(
1208
                                     LIST_EMCTP,
1209
                                     LIST_EMCTP[_mem.settings.emctp]))
1210
            basic.append(emctp)
1211

    
1212
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1213
                             RadioSettingValueInteger(0, self._upper,
1214
                                                      _mem.settings.emcch))
1215
        basic.append(emcch)
1216

    
1217
        if self.COLOR_LCD:
1218
            if _mem.settings.sigbp > 0x01:
1219
                val = 0x00
1220
            else:
1221
                val = _mem.settings.sigbp
1222
            sigbp = RadioSetting("settings.sigbp", "Signal beep",
1223
                                 RadioSettingValueBoolean(val))
1224
            basic.append(sigbp)
1225
        else:
1226
            ringt = RadioSetting("settings.ringt", "Ring time",
1227
                                 RadioSettingValueList(
1228
                                     LIST_OFF1TO9,
1229
                                     LIST_OFF1TO9[_mem.settings.ringt]))
1230
            basic.append(ringt)
1231

    
1232
        camdf = RadioSetting("settings.camdf", "Display mode A",
1233
                             RadioSettingValueList(
1234
                                 LIST_MDF,
1235
                                 LIST_MDF[_mem.settings.camdf]))
1236
        basic.append(camdf)
1237

    
1238
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1239
                             RadioSettingValueList(
1240
                                 LIST_MDF,
1241
                                 LIST_MDF[_mem.settings.cbmdf]))
1242
        basic.append(cbmdf)
1243

    
1244
        if self.COLOR_LCD:
1245
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1246
                                 RadioSettingValueList(
1247
                                     LIST_MDF,
1248
                                     LIST_MDF[_mem.settings.ccmdf]))
1249
            basic.append(ccmdf)
1250

    
1251
            if not self.COLOR_LCD4:
1252
                cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1253
                                     RadioSettingValueList(
1254
                                         LIST_MDF,
1255
                                         LIST_MDF[_mem.settings.cdmdf]))
1256
                basic.append(cdmdf)
1257

    
1258
                if self.MODEL == "UV-50X2_G2":
1259
                    vox = RadioSetting("settings.langua", "VOX",
1260
                                       RadioSettingValueList(
1261
                                           LIST_VOX,
1262
                                           LIST_VOX[_mem.settings.langua]))
1263
                    basic.append(vox)
1264
                else:
1265
                    langua = RadioSetting("settings.langua", "Language",
1266
                                          RadioSettingValueList(
1267
                                              LIST_LANGUA,
1268
                                              LIST_LANGUA[
1269
                                                  _mem.settings.langua]))
1270
                    basic.append(langua)
1271
        if self.MODEL == "KT-8R":
1272
            voice = RadioSetting("settings.voice", "Voice prompt",
1273
                                 RadioSettingValueList(
1274
                                     LIST_VOICE,
1275
                                     LIST_VOICE[_mem.settings.voice]))
1276
            basic.append(voice)
1277

    
1278
        if self.MODEL == "KT-8R" or self.COLOR_LCD4:
1279
            vox = RadioSetting("settings.vox", "VOX",
1280
                               RadioSettingValueList(
1281
                                   LIST_VOX,
1282
                                   LIST_VOX[_mem.settings.vox]))
1283
            basic.append(vox)
1284

    
1285
            voxt = RadioSetting("settings.voxt", "VOX delay time",
1286
                                RadioSettingValueList(
1287
                                    LIST_VOXT,
1288
                                    LIST_VOXT[_mem.settings.voxt]))
1289
            basic.append(voxt)
1290

    
1291
        if self.VENDOR == "BTECH":
1292
            if self.COLOR_LCD:
1293
                if self.MODEL == "GMRS-20V2":
1294
                    val = RadioSettingValueList(LIST_SYNCV2,
1295
                                                LIST_SYNCV2[
1296
                                                    _mem.settings.sync])
1297
                    sync = RadioSetting("settings.sync",
1298
                                        "Channel display sync", val)
1299
                    basic.append(sync)
1300
                else:
1301
                    val = RadioSettingValueList(LIST_SYNC,
1302
                                                LIST_SYNC[_mem.settings.sync])
1303
                    sync = RadioSetting("settings.sync",
1304
                                        "Channel display sync", val)
1305
                    basic.append(sync)
1306
            else:
1307
                sync = RadioSetting("settings.sync", "A/B channel sync",
1308
                                    RadioSettingValueBoolean(
1309
                                        _mem.settings.sync))
1310
                basic.append(sync)
1311

    
1312
        if self.COLOR_LCD4:
1313
            autolk = RadioSetting("settings.autolock", "Auto keylock",
1314
                                  RadioSettingValueBoolean(
1315
                                      _mem.settings.autolock))
1316
            basic.append(autolk)
1317

    
1318
        if not self.COLOR_LCD:
1319
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1320
                                  RadioSettingValueList(
1321
                                      LIST_PONMSG,
1322
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1323
            basic.append(ponmsg)
1324

    
1325
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3 or
1326
                                   self.COLOR_LCD4):
1327
            mainfc = RadioSetting("settings.mainfc",
1328
                                  "Main LCD foreground color",
1329
                                  RadioSettingValueList(
1330
                                      LIST_COLOR9,
1331
                                      LIST_COLOR9[_mem.settings.mainfc]))
1332
            basic.append(mainfc)
1333

    
1334
            if not self.COLOR_LCD4:
1335
                mainbc = RadioSetting("settings.mainbc",
1336
                                      "Main LCD background color",
1337
                                      RadioSettingValueList(
1338
                                          LIST_COLOR9,
1339
                                          LIST_COLOR9[_mem.settings.mainbc]))
1340
                basic.append(mainbc)
1341

    
1342
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1343
                                  RadioSettingValueList(
1344
                                      LIST_COLOR9,
1345
                                      LIST_COLOR9[_mem.settings.menufc]))
1346
            basic.append(menufc)
1347

    
1348
            if not self.COLOR_LCD4:
1349
                val = RadioSettingValueList(LIST_COLOR9,
1350
                                            LIST_COLOR9[_mem.settings.menubc])
1351
                menubc = RadioSetting("settings.menubc",
1352
                                      "Menu background color", val)
1353
                basic.append(menubc)
1354

    
1355
            stafc = RadioSetting("settings.stafc",
1356
                                 "Top status foreground color",
1357
                                 RadioSettingValueList(
1358
                                     LIST_COLOR9,
1359
                                     LIST_COLOR9[_mem.settings.stafc]))
1360
            basic.append(stafc)
1361

    
1362
            if not self.COLOR_LCD4:
1363
                stabc = RadioSetting("settings.stabc",
1364
                                     "Top status background color",
1365
                                     RadioSettingValueList(
1366
                                         LIST_COLOR9,
1367
                                         LIST_COLOR9[_mem.settings.stabc]))
1368
                basic.append(stabc)
1369

    
1370
            sigfc = RadioSetting("settings.sigfc",
1371
                                 "Bottom status foreground color",
1372
                                 RadioSettingValueList(
1373
                                     LIST_COLOR9,
1374
                                     LIST_COLOR9[_mem.settings.sigfc]))
1375
            basic.append(sigfc)
1376

    
1377
            if not self.COLOR_LCD4:
1378
                sigbc = RadioSetting("settings.sigbc",
1379
                                     "Bottom status background color",
1380
                                     RadioSettingValueList(
1381
                                         LIST_COLOR9,
1382
                                         LIST_COLOR9[_mem.settings.sigbc]))
1383
                basic.append(sigbc)
1384

    
1385
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1386
                                RadioSettingValueList(
1387
                                    LIST_COLOR9,
1388
                                    LIST_COLOR9[_mem.settings.rxfc]))
1389
            basic.append(rxfc)
1390

    
1391
            txfc = RadioSetting("settings.txfc",
1392
                                "Transmitting character color",
1393
                                RadioSettingValueList(
1394
                                    LIST_COLOR9,
1395
                                    LIST_COLOR9[_mem.settings.txfc]))
1396
            basic.append(txfc)
1397

    
1398
            if not self.COLOR_LCD4:
1399
                txdisp = RadioSetting("settings.txdisp",
1400
                                      "Transmitting status display",
1401
                                      RadioSettingValueList(
1402
                                          LIST_TXDISP,
1403
                                          LIST_TXDISP[_mem.settings.txdisp]))
1404
                basic.append(txdisp)
1405

    
1406
        elif self.COLOR_LCD2 or self.COLOR_LCD3:
1407
            stfc = RadioSetting("settings.stfc",
1408
                                "ST-FC",
1409
                                RadioSettingValueList(
1410
                                    LIST_COLOR8,
1411
                                    LIST_COLOR8[_mem.settings.stfc]))
1412
            basic.append(stfc)
1413

    
1414
            mffc = RadioSetting("settings.mffc",
1415
                                "MF-FC",
1416
                                RadioSettingValueList(
1417
                                    LIST_COLOR8,
1418
                                    LIST_COLOR8[_mem.settings.mffc]))
1419
            basic.append(mffc)
1420

    
1421
            sfafc = RadioSetting("settings.sfafc",
1422
                                 "SFA-FC",
1423
                                 RadioSettingValueList(
1424
                                     LIST_COLOR8,
1425
                                     LIST_COLOR8[_mem.settings.sfafc]))
1426
            basic.append(sfafc)
1427

    
1428
            sfbfc = RadioSetting("settings.sfbfc",
1429
                                 "SFB-FC",
1430
                                 RadioSettingValueList(
1431
                                     LIST_COLOR8,
1432
                                     LIST_COLOR8[_mem.settings.sfbfc]))
1433
            basic.append(sfbfc)
1434

    
1435
            sfcfc = RadioSetting("settings.sfcfc",
1436
                                 "SFC-FC",
1437
                                 RadioSettingValueList(
1438
                                     LIST_COLOR8,
1439
                                     LIST_COLOR8[_mem.settings.sfcfc]))
1440
            basic.append(sfcfc)
1441

    
1442
            sfdfc = RadioSetting("settings.sfdfc",
1443
                                 "SFD-FC",
1444
                                 RadioSettingValueList(
1445
                                     LIST_COLOR8,
1446
                                     LIST_COLOR8[_mem.settings.sfdfc]))
1447
            basic.append(sfdfc)
1448

    
1449
            subfc = RadioSetting("settings.subfc",
1450
                                 "SUB-FC",
1451
                                 RadioSettingValueList(
1452
                                     LIST_COLOR8,
1453
                                     LIST_COLOR8[_mem.settings.subfc]))
1454
            basic.append(subfc)
1455

    
1456
            fmfc = RadioSetting("settings.fmfc",
1457
                                "FM-FC",
1458
                                RadioSettingValueList(
1459
                                    LIST_COLOR8,
1460
                                    LIST_COLOR8[_mem.settings.fmfc]))
1461
            basic.append(fmfc)
1462

    
1463
            sigfc = RadioSetting("settings.sigfc",
1464
                                 "SIG-FC",
1465
                                 RadioSettingValueList(
1466
                                     LIST_COLOR8,
1467
                                     LIST_COLOR8[_mem.settings.sigfc]))
1468
            basic.append(sigfc)
1469

    
1470
            if not self.MODEL == "KT-8R":
1471
                modfc = RadioSetting("settings.modfc",
1472
                                     "MOD-FC",
1473
                                     RadioSettingValueList(
1474
                                         LIST_COLOR8,
1475
                                         LIST_COLOR8[_mem.settings.modfc]))
1476
                basic.append(modfc)
1477

    
1478
            menufc = RadioSetting("settings.menufc",
1479
                                  "MENUFC",
1480
                                  RadioSettingValueList(
1481
                                      LIST_COLOR8,
1482
                                      LIST_COLOR8[_mem.settings.menufc]))
1483
            basic.append(menufc)
1484

    
1485
            txfc = RadioSetting("settings.txfc",
1486
                                "TX-FC",
1487
                                RadioSettingValueList(
1488
                                    LIST_COLOR8,
1489
                                    LIST_COLOR8[_mem.settings.txfc]))
1490
            basic.append(txfc)
1491

    
1492
            if self.MODEL == "KT-8R":
1493
                rxfc = RadioSetting("settings.rxfc",
1494
                                    "RX-FC",
1495
                                    RadioSettingValueList(
1496
                                        LIST_COLOR8,
1497
                                        LIST_COLOR8[_mem.settings.rxfc]))
1498
                basic.append(rxfc)
1499

    
1500
            if not self.MODEL == "KT-8R":
1501
                txdisp = RadioSetting("settings.txdisp",
1502
                                      "Transmitting status display",
1503
                                      RadioSettingValueList(
1504
                                          LIST_TXDISP,
1505
                                          LIST_TXDISP[_mem.settings.txdisp]))
1506
                basic.append(txdisp)
1507
        elif self.COLOR_LCD4:
1508
            asfc = RadioSetting("settings.asfc",
1509
                                "Above Stat fore color",
1510
                                RadioSettingValueList(
1511
                                    LIST_COLOR8,
1512
                                    LIST_COLOR8[_mem.settings.asfc]))
1513
            basic.append(asfc)
1514

    
1515
            mainfc = RadioSetting("settings.mainfc",
1516
                                  "Main fore color",
1517
                                  RadioSettingValueList(
1518
                                     LIST_COLOR8,
1519
                                     LIST_COLOR8[_mem.settings.mainfc]))
1520
            basic.append(mainfc)
1521

    
1522
            a_fc = RadioSetting("settings.a_fc",
1523
                                "A - fore color",
1524
                                RadioSettingValueList(
1525
                                    LIST_COLOR8,
1526
                                    LIST_COLOR8[_mem.settings.a_fc]))
1527
            basic.append(a_fc)
1528

    
1529
            b_fc = RadioSetting("settings.b_fc",
1530
                                "B - fore color",
1531
                                RadioSettingValueList(
1532
                                    LIST_COLOR8,
1533
                                    LIST_COLOR8[_mem.settings.b_fc]))
1534
            basic.append(b_fc)
1535

    
1536
            c_fc = RadioSetting("settings.c_fc",
1537
                                "C - fore color",
1538
                                RadioSettingValueList(
1539
                                    LIST_COLOR8,
1540
                                    LIST_COLOR8[_mem.settings.c_fc]))
1541
            basic.append(c_fc)
1542

    
1543
            subfc = RadioSetting("settings.subfc",
1544
                                 "Sub fore color",
1545
                                 RadioSettingValueList(
1546
                                     LIST_COLOR8,
1547
                                     LIST_COLOR8[_mem.settings.subfc]))
1548
            basic.append(subfc)
1549

    
1550
            battfc = RadioSetting("settings.battfc",
1551
                                  "Battery fore color",
1552
                                  RadioSettingValueList(
1553
                                      LIST_COLOR8,
1554
                                      LIST_COLOR8[_mem.settings.battfc]))
1555
            basic.append(battfc)
1556

    
1557
            sigfc = RadioSetting("settings.sigfc",
1558
                                 "Signal fore color",
1559
                                 RadioSettingValueList(
1560
                                     LIST_COLOR8,
1561
                                     LIST_COLOR8[_mem.settings.sigfc]))
1562
            basic.append(sigfc)
1563

    
1564
            menufc = RadioSetting("settings.menufc",
1565
                                  "Menu fore color",
1566
                                  RadioSettingValueList(
1567
                                      LIST_COLOR8,
1568
                                      LIST_COLOR8[_mem.settings.menufc]))
1569
            basic.append(menufc)
1570

    
1571
            txfc = RadioSetting("settings.txfc",
1572
                                "TX fore color",
1573
                                RadioSettingValueList(
1574
                                    LIST_COLOR8,
1575
                                    LIST_COLOR8[_mem.settings.txfc]))
1576
            basic.append(txfc)
1577

    
1578
            rxfc = RadioSetting("settings.rxfc",
1579
                                "RX fore color",
1580
                                RadioSettingValueList(
1581
                                    LIST_COLOR8,
1582
                                    LIST_COLOR8[_mem.settings.rxfc]))
1583
            basic.append(rxfc)
1584
        else:
1585
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1586
                                 RadioSettingValueList(
1587
                                     LIST_COLOR4,
1588
                                     LIST_COLOR4[_mem.settings.wtled]))
1589
            basic.append(wtled)
1590

    
1591
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1592
                                 RadioSettingValueList(
1593
                                     LIST_COLOR4,
1594
                                     LIST_COLOR4[_mem.settings.rxled]))
1595
            basic.append(rxled)
1596

    
1597
            txled = RadioSetting("settings.txled", "TX backlight Color",
1598
                                 RadioSettingValueList(
1599
                                     LIST_COLOR4,
1600
                                     LIST_COLOR4[_mem.settings.txled]))
1601
            basic.append(txled)
1602

    
1603
        anil = RadioSetting("settings.anil", "ANI length",
1604
                            RadioSettingValueList(
1605
                                LIST_ANIL,
1606
                                LIST_ANIL[_mem.settings.anil]))
1607
        basic.append(anil)
1608

    
1609
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1610
                            RadioSettingValueList(
1611
                                LIST_REPS,
1612
                                LIST_REPS[_mem.settings.reps]))
1613
        basic.append(reps)
1614

    
1615
        if self.COLOR_LCD4:
1616
            dsub = RadioSetting("settings.dsub", "Subtone display",
1617
                                RadioSettingValueBoolean(
1618
                                    _mem.settings.dsub))
1619
            basic.append(dsub)
1620

    
1621
        if self.MODEL == "GMRS-20V2":
1622
            repsw = RadioSetting("settings.repsw", "Repeater SW",
1623
                                 RadioSettingValueList(
1624
                                     LIST_REPSW,
1625
                                     LIST_REPSW[_mem.settings.repsw]))
1626
            basic.append(repsw)
1627

    
1628
        model_list = ["GMRS-50X1", "KT-8R", "KT-WP12", "WP-9900"]
1629
        if self.MODEL not in model_list:
1630
            repm = RadioSetting("settings.repm", "Relay condition",
1631
                                RadioSettingValueList(
1632
                                    LIST_REPM,
1633
                                    LIST_REPM[_mem.settings.repm]))
1634
            basic.append(repm)
1635

    
1636
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1637
            if self.COLOR_LCD:
1638
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1639
                                     RadioSettingValueList(
1640
                                         LIST_OFF1TO50,
1641
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1642
                basic.append(tmrmr)
1643
            else:
1644
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1645
                                     RadioSettingValueList(
1646
                                         LIST_OFF1TO50,
1647
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1648
                basic.append(tdrab)
1649

    
1650
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1651
                               RadioSettingValueBoolean(_mem.settings.ste))
1652
            basic.append(ste)
1653

    
1654
            if self.COLOR_LCD4:
1655
                rpste = RadioSetting("settings.rpste", "Repeater STE",
1656
                                     RadioSettingValueList(
1657
                                         LIST_OFF1TO10,
1658
                                         LIST_OFF1TO10[_mem.settings.rpste]))
1659
                basic.append(rpste)
1660
            else:
1661
                rpste = RadioSetting("settings.rpste", "Repeater STE",
1662
                                     RadioSettingValueList(
1663
                                         LIST_OFF1TO9,
1664
                                         LIST_OFF1TO9[_mem.settings.rpste]))
1665
                basic.append(rpste)
1666

    
1667
            if self.COLOR_LCD4:
1668
                rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1669
                                     RadioSettingValueList(
1670
                                         LIST_OFF1TO60,
1671
                                         LIST_OFF1TO60[_mem.settings.rptdl]))
1672
                basic.append(rptdl)
1673
            else:
1674
                rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1675
                                     RadioSettingValueList(
1676
                                         LIST_RPTDL,
1677
                                         LIST_RPTDL[_mem.settings.rptdl]))
1678
                basic.append(rptdl)
1679

    
1680
        if self.MODEL == "DB25-G":
1681
            mgain = RadioSetting("settings.mgain", "Auto power-on",
1682
                                 RadioSettingValueBoolean(_mem.settings.mgain))
1683
            basic.append(mgain)
1684

    
1685
        if str(_mem.fingerprint.fp) in BTECH3:
1686
            mgain = RadioSetting("settings.mgain", "Mic gain",
1687
                                 RadioSettingValueInteger(0, 120,
1688
                                                          _mem.settings.mgain))
1689
            basic.append(mgain)
1690

    
1691
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1692
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1693
                                 RadioSettingValueInteger(0, 60,
1694
                                                          _mem.settings.dtmfg))
1695
            basic.append(dtmfg)
1696

    
1697
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1698
            mgain = RadioSetting("settings.mgain", "Mic gain",
1699
                                 RadioSettingValueInteger(0, 127,
1700
                                                          _mem.settings.mgain))
1701
            basic.append(mgain)
1702

    
1703
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1704
                                  RadioSettingValueList(
1705
                                      LIST_SKIPTX,
1706
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1707
            basic.append(skiptx)
1708

    
1709
            scmode = RadioSetting("settings.scmode", "Scan mode",
1710
                                  RadioSettingValueList(
1711
                                      LIST_SCMODE,
1712
                                      LIST_SCMODE[_mem.settings.scmode]))
1713
            basic.append(scmode)
1714

    
1715
        if self.MODEL == "KT-8R" or self.MODEL == "UV-25X2" \
1716
                or self.MODEL == "UV-25X4" or self.MODEL == "UV-50X2" \
1717
                or self.MODEL == "GMRS-50X1" or self.MODEL == "GMRS-20V2" \
1718
                or self.MODEL == "UV-50X2_G2":
1719
            tmrtx = RadioSetting("settings.tmrtx", "TX in multi-standby",
1720
                                 RadioSettingValueList(
1721
                                     LIST_TMRTX,
1722
                                     LIST_TMRTX[_mem.settings.tmrtx]))
1723
            basic.append(tmrtx)
1724

    
1725
        if self.MODEL == "UV-50X2_G2":
1726
            earpho = RadioSetting("settings.earpho", "Earphone",
1727
                                  RadioSettingValueList(
1728
                                      LIST_EARPH,
1729
                                      LIST_EARPH[_mem.settings.earpho]))
1730
            basic.append(earpho)
1731

    
1732
        # Advanced
1733
        def _filter(name):
1734
            filtered = ""
1735
            for char in str(name):
1736
                if char in VALID_CHARS:
1737
                    filtered += char
1738
                else:
1739
                    filtered += " "
1740
            return filtered
1741

    
1742
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3 or
1743
                                   self.COLOR_LCD4):
1744
            _msg = self._memobj.poweron_msg
1745
            line1 = RadioSetting("poweron_msg.line1",
1746
                                 "Power-on message line 1",
1747
                                 RadioSettingValueString(0, 8, _filter(
1748
                                                         _msg.line1)))
1749
            advanced.append(line1)
1750
            line2 = RadioSetting("poweron_msg.line2",
1751
                                 "Power-on message line 2",
1752
                                 RadioSettingValueString(0, 8, _filter(
1753
                                                         _msg.line2)))
1754
            advanced.append(line2)
1755
            line3 = RadioSetting("poweron_msg.line3",
1756
                                 "Power-on message line 3",
1757
                                 RadioSettingValueString(0, 8, _filter(
1758
                                                         _msg.line3)))
1759
            advanced.append(line3)
1760
            line4 = RadioSetting("poweron_msg.line4",
1761
                                 "Power-on message line 4",
1762
                                 RadioSettingValueString(0, 8, _filter(
1763
                                                         _msg.line4)))
1764
            advanced.append(line4)
1765
            line5 = RadioSetting("poweron_msg.line5",
1766
                                 "Power-on message line 5",
1767
                                 RadioSettingValueString(0, 8, _filter(
1768
                                                         _msg.line5)))
1769
            advanced.append(line5)
1770
            line6 = RadioSetting("poweron_msg.line6",
1771
                                 "Power-on message line 6",
1772
                                 RadioSettingValueString(0, 8, _filter(
1773
                                                         _msg.line6)))
1774
            advanced.append(line6)
1775
            line7 = RadioSetting("poweron_msg.line7",
1776
                                 "Power-on message line 7",
1777
                                 RadioSettingValueString(0, 8, _filter(
1778
                                                         _msg.line7)))
1779
            advanced.append(line7)
1780
            line8 = RadioSetting("poweron_msg.line8", "Static message",
1781
                                 RadioSettingValueString(0, 8, _filter(
1782
                                                         _msg.line8)))
1783
            advanced.append(line8)
1784
        elif self.COLOR_LCD2 or self.COLOR_LCD3 or self.COLOR_LCD4:
1785
            _msg = self._memobj.static_msg
1786
            line = RadioSetting("static_msg.line", "Static message",
1787
                                RadioSettingValueString(0, 16, _filter(
1788
                                    _msg.line)))
1789
            advanced.append(line)
1790
        else:
1791
            _msg = self._memobj.poweron_msg
1792
            line1 = RadioSetting("poweron_msg.line1",
1793
                                 "Power-on message line 1",
1794
                                 RadioSettingValueString(0, 6, _filter(
1795
                                                         _msg.line1)))
1796
            advanced.append(line1)
1797
            line2 = RadioSetting("poweron_msg.line2",
1798
                                 "Power-on message line 2",
1799
                                 RadioSettingValueString(0, 6, _filter(
1800
                                                         _msg.line2)))
1801
            advanced.append(line2)
1802

    
1803
        if self.MODEL in ("UV-2501", "UV-5001"):
1804
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1805
                                   RadioSettingValueBoolean(
1806
                                       _mem.settings2.vfomren))
1807
            advanced.append(vfomren)
1808

    
1809
            reseten = RadioSetting("settings2.reseten", "RESET",
1810
                                   RadioSettingValueBoolean(
1811
                                       _mem.settings2.reseten))
1812
            advanced.append(reseten)
1813

    
1814
            menuen = RadioSetting("settings2.menuen", "Menu",
1815
                                  RadioSettingValueBoolean(
1816
                                      _mem.settings2.menuen))
1817
            advanced.append(menuen)
1818

    
1819
        # Other
1820
        def convert_bytes_to_limit(bytes):
1821
            limit = ""
1822
            for byte in bytes:
1823
                if byte < 10:
1824
                    limit += chr(byte + 0x30)
1825
                else:
1826
                    break
1827
            return limit
1828

    
1829
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1830
            _ranges = self._memobj.ranges220
1831
            ranges = "ranges220"
1832
        else:
1833
            _ranges = self._memobj.ranges
1834
            ranges = "ranges"
1835

    
1836
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1837
        val = RadioSettingValueString(0, 3, _limit)
1838
        val.set_mutable(False)
1839
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1840
        other.append(vhf_low)
1841

    
1842
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1843
        val = RadioSettingValueString(0, 3, _limit)
1844
        val.set_mutable(False)
1845
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1846
        other.append(vhf_high)
1847

    
1848
        if self.BANDS == 3 or self.BANDS == 4:
1849
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1850
            val = RadioSettingValueString(0, 3, _limit)
1851
            val.set_mutable(False)
1852
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1853
            other.append(vhf2_low)
1854

    
1855
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1856
            val = RadioSettingValueString(0, 3, _limit)
1857
            val.set_mutable(False)
1858
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1859
            other.append(vhf2_high)
1860

    
1861
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1862
        val = RadioSettingValueString(0, 3, _limit)
1863
        val.set_mutable(False)
1864
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1865
        other.append(uhf_low)
1866

    
1867
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1868
        val = RadioSettingValueString(0, 3, _limit)
1869
        val.set_mutable(False)
1870
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1871
        other.append(uhf_high)
1872

    
1873
        if self.BANDS == 4:
1874
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1875
            val = RadioSettingValueString(0, 3, _limit)
1876
            val.set_mutable(False)
1877
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1878
            other.append(uhf2_low)
1879

    
1880
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1881
            val = RadioSettingValueString(0, 3, _limit)
1882
            val.set_mutable(False)
1883
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1884
            other.append(uhf2_high)
1885

    
1886
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1887
        val.set_mutable(False)
1888
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1889
        other.append(fp)
1890

    
1891
        # Work
1892
        if self.COLOR_LCD4:
1893
            dispab = RadioSetting("settings2.dispab", "Display",
1894
                                  RadioSettingValueList(
1895
                                      LIST_ABC,
1896
                                      LIST_ABC[_mem.settings2.dispab]))
1897
            work.append(dispab)
1898
        elif self.COLOR_LCD:
1899
            dispab = RadioSetting("settings2.dispab", "Display",
1900
                                  RadioSettingValueList(
1901
                                      LIST_ABCD,
1902
                                      LIST_ABCD[_mem.settings2.dispab]))
1903
            work.append(dispab)
1904
        else:
1905
            dispab = RadioSetting("settings2.dispab", "Display",
1906
                                  RadioSettingValueList(
1907
                                      LIST_AB,
1908
                                      LIST_AB[_mem.settings2.dispab]))
1909
            work.append(dispab)
1910

    
1911
        if self.COLOR_LCD:
1912
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1913
                                  RadioSettingValueList(
1914
                                      LIST_VFOMR,
1915
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1916
            work.append(vfomra)
1917

    
1918
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1919
                                  RadioSettingValueList(
1920
                                      LIST_VFOMR,
1921
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1922
            work.append(vfomrb)
1923

    
1924
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1925
                                  RadioSettingValueList(
1926
                                      LIST_VFOMR,
1927
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1928
            work.append(vfomrc)
1929

    
1930
            if not self.COLOR_LCD4:
1931
                vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1932
                                      RadioSettingValueList(
1933
                                          LIST_VFOMR,
1934
                                          LIST_VFOMR[_mem.settings2.vfomrd]))
1935
                work.append(vfomrd)
1936
        else:
1937
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1938
                                 RadioSettingValueList(
1939
                                     LIST_VFOMR,
1940
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1941
            work.append(vfomr)
1942

    
1943
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1944
                               RadioSettingValueBoolean(
1945
                                   _mem.settings2.keylock))
1946
        work.append(keylock)
1947

    
1948
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1949
                             RadioSettingValueInteger(0, self._upper,
1950
                                                      _mem.settings2.mrcha))
1951
        work.append(mrcha)
1952

    
1953
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1954
                             RadioSettingValueInteger(0, self._upper,
1955
                                                      _mem.settings2.mrchb))
1956
        work.append(mrchb)
1957

    
1958
        if self.COLOR_LCD:
1959
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1960
                                 RadioSettingValueInteger(
1961
                                     0, self._upper, _mem.settings2.mrchc))
1962
            work.append(mrchc)
1963

    
1964
            if not self.COLOR_LCD4:
1965
                mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1966
                                     RadioSettingValueInteger(
1967
                                         0, self._upper, _mem.settings2.mrchd))
1968
                work.append(mrchd)
1969

    
1970
        def convert_bytes_to_freq(bytes):
1971
            real_freq = 0
1972
            for byte in bytes:
1973
                real_freq = (real_freq * 10) + byte
1974
            return chirp_common.format_freq(real_freq * 10)
1975

    
1976
        def my_validate(value):
1977
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1978
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1979
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1980
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1981
            if self.BANDS == 3 or self.BANDS == 4:
1982
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1983
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1984
            if self.BANDS == 4:
1985
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1986
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1987

    
1988
            value = chirp_common.parse_freq(value)
1989
            msg = ("Can't be less then %i.0000")
1990
            if value > 99000000 and value < _vhf_lower * 1000000:
1991
                raise InvalidValueError(msg % (_vhf_lower))
1992
            msg = ("Can't be betweeb %i.9975-%i.0000")
1993
            if self.BANDS == 2:
1994
                if (_vhf_upper + 1) * 1000000 <= value and \
1995
                        value < _uhf_lower * 1000000:
1996
                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
1997
            if self.BANDS == 3:
1998
                if (_vhf_upper + 1) * 1000000 <= value and \
1999
                        value < _vhf2_lower * 1000000:
2000
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
2001
                if (_vhf2_upper + 1) * 1000000 <= value and \
2002
                        value < _uhf_lower * 1000000:
2003
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
2004
            if self.BANDS == 4:
2005
                if (_vhf_upper + 1) * 1000000 <= value and \
2006
                        value < _vhf2_lower * 1000000:
2007
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
2008
                if (_vhf2_upper + 1) * 1000000 <= value and \
2009
                        value < _uhf2_lower * 1000000:
2010
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
2011
                if (_uhf2_upper + 1) * 1000000 <= value and \
2012
                        value < _uhf_lower * 1000000:
2013
                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
2014
            msg = ("Can't be greater then %i.9975")
2015
            if value > 99000000 and value >= _uhf_upper * 1000000:
2016
                raise InvalidValueError(msg % (_uhf_upper))
2017
            return chirp_common.format_freq(value)
2018

    
2019
        def apply_freq(setting, obj):
2020
            value = chirp_common.parse_freq(str(setting.value)) / 10
2021
            for i in range(7, -1, -1):
2022
                obj.freq[i] = value % 10
2023
                value /= 10
2024

    
2025
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2026
                                        _mem.vfo.a.freq))
2027
        val1a.set_validate_callback(my_validate)
2028
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
2029
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
2030
        work.append(vfoafreq)
2031

    
2032
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2033
                                        _mem.vfo.b.freq))
2034
        val1b.set_validate_callback(my_validate)
2035
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
2036
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
2037
        work.append(vfobfreq)
2038

    
2039
        if self.COLOR_LCD:
2040
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2041
                                            _mem.vfo.c.freq))
2042
            val1c.set_validate_callback(my_validate)
2043
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
2044
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
2045
            work.append(vfocfreq)
2046

    
2047
            if not self.COLOR_LCD4:
2048
                val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2049
                                                _mem.vfo.d.freq))
2050
                val1d.set_validate_callback(my_validate)
2051
                vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
2052
                vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
2053
                work.append(vfodfreq)
2054

    
2055
        if not self.MODEL == "GMRS-50X1":
2056
            vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
2057
                                      RadioSettingValueList(
2058
                                          LIST_SHIFT,
2059
                                          LIST_SHIFT[_mem.vfo.a.shiftd]))
2060
            work.append(vfoashiftd)
2061

    
2062
            vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
2063
                                      RadioSettingValueList(
2064
                                          LIST_SHIFT,
2065
                                          LIST_SHIFT[_mem.vfo.b.shiftd]))
2066
            work.append(vfobshiftd)
2067

    
2068
            if self.COLOR_LCD:
2069
                vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
2070
                                          RadioSettingValueList(
2071
                                              LIST_SHIFT,
2072
                                              LIST_SHIFT[_mem.vfo.c.shiftd]))
2073
                work.append(vfocshiftd)
2074

    
2075
                if not self.COLOR_LCD4:
2076
                    val = RadioSettingValueList(LIST_SHIFT,
2077
                                                LIST_SHIFT[_mem.vfo.d.shiftd])
2078
                    vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
2079
                                              val)
2080
                    work.append(vfodshiftd)
2081

    
2082
        def convert_bytes_to_offset(bytes):
2083
            real_offset = 0
2084
            for byte in bytes:
2085
                real_offset = (real_offset * 10) + byte
2086
            return chirp_common.format_freq(real_offset * 1000)
2087

    
2088
        def apply_offset(setting, obj):
2089
            value = chirp_common.parse_freq(str(setting.value)) / 1000
2090
            for i in range(5, -1, -1):
2091
                obj.offset[i] = value % 10
2092
                value /= 10
2093

    
2094
        if not self.MODEL == "GMRS-50X1":
2095
            if self.COLOR_LCD:
2096
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2097
                                                _mem.vfo.a.offset))
2098
                vfoaoffset = RadioSetting("vfo.a.offset",
2099
                                          "VFO A offset (0.000-999.999)",
2100
                                          val1a)
2101
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2102
                work.append(vfoaoffset)
2103

    
2104
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2105
                                                _mem.vfo.b.offset))
2106
                vfoboffset = RadioSetting("vfo.b.offset",
2107
                                          "VFO B offset (0.000-999.999)",
2108
                                          val1b)
2109
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2110
                work.append(vfoboffset)
2111

    
2112
                val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2113
                                                _mem.vfo.c.offset))
2114
                vfocoffset = RadioSetting("vfo.c.offset",
2115
                                          "VFO C offset (0.000-999.999)",
2116
                                          val1c)
2117
                vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
2118
                work.append(vfocoffset)
2119

    
2120
                if not self.COLOR_LCD4:
2121
                    val1d = RadioSettingValueString(0, 10,
2122
                                                    convert_bytes_to_offset(
2123
                                                        _mem.vfo.d.offset))
2124
                    vfodoffset = RadioSetting("vfo.d.offset",
2125
                                              "VFO D offset (0.000-999.999)",
2126
                                              val1d)
2127
                    vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
2128
                    work.append(vfodoffset)
2129
            else:
2130
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2131
                                                _mem.vfo.a.offset))
2132
                vfoaoffset = RadioSetting("vfo.a.offset",
2133
                                          "VFO A offset (0.000-99.999)", val1a)
2134
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2135
                work.append(vfoaoffset)
2136

    
2137
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2138
                                                _mem.vfo.b.offset))
2139
                vfoboffset = RadioSetting("vfo.b.offset",
2140
                                          "VFO B offset (0.000-99.999)", val1b)
2141
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2142
                work.append(vfoboffset)
2143

    
2144
        if not self.MODEL == "GMRS-50X1":
2145
            vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
2146
                                   RadioSettingValueList(
2147
                                       LIST_TXP,
2148
                                       LIST_TXP[_mem.vfo.a.power]))
2149
            work.append(vfoatxp)
2150

    
2151
            vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
2152
                                   RadioSettingValueList(
2153
                                       LIST_TXP,
2154
                                       LIST_TXP[_mem.vfo.b.power]))
2155
            work.append(vfobtxp)
2156

    
2157
            if self.COLOR_LCD:
2158
                vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
2159
                                       RadioSettingValueList(
2160
                                           LIST_TXP,
2161
                                           LIST_TXP[_mem.vfo.c.power]))
2162
                work.append(vfoctxp)
2163

    
2164
                if not self.COLOR_LCD4:
2165
                    vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
2166
                                           RadioSettingValueList(
2167
                                               LIST_TXP,
2168
                                               LIST_TXP[_mem.vfo.d.power]))
2169
                    work.append(vfodtxp)
2170

    
2171
        if not self.MODEL == "GMRS-50X1":
2172
            vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
2173
                                    RadioSettingValueList(
2174
                                        LIST_WIDE,
2175
                                        LIST_WIDE[_mem.vfo.a.wide]))
2176
            work.append(vfoawide)
2177

    
2178
            vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
2179
                                    RadioSettingValueList(
2180
                                        LIST_WIDE,
2181
                                        LIST_WIDE[_mem.vfo.b.wide]))
2182
            work.append(vfobwide)
2183

    
2184
            if self.COLOR_LCD:
2185
                vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
2186
                                        RadioSettingValueList(
2187
                                            LIST_WIDE,
2188
                                            LIST_WIDE[_mem.vfo.c.wide]))
2189
                work.append(vfocwide)
2190

    
2191
                if not self.COLOR_LCD4:
2192
                    vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
2193
                                            RadioSettingValueList(
2194
                                                LIST_WIDE,
2195
                                                LIST_WIDE[_mem.vfo.d.wide]))
2196
                    work.append(vfodwide)
2197

    
2198
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
2199
                                RadioSettingValueList(
2200
                                    LIST_STEP,
2201
                                    LIST_STEP[_mem.vfo.a.step]))
2202
        work.append(vfoastep)
2203

    
2204
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
2205
                                RadioSettingValueList(
2206
                                    LIST_STEP,
2207
                                    LIST_STEP[_mem.vfo.b.step]))
2208
        work.append(vfobstep)
2209

    
2210
        if self.COLOR_LCD:
2211
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
2212
                                    RadioSettingValueList(
2213
                                        LIST_STEP,
2214
                                        LIST_STEP[_mem.vfo.c.step]))
2215
            work.append(vfocstep)
2216

    
2217
            if not self.COLOR_LCD4:
2218
                vfodstep = RadioSetting("vfo.d.step", "VFO D step",
2219
                                        RadioSettingValueList(
2220
                                            LIST_STEP,
2221
                                            LIST_STEP[_mem.vfo.d.step]))
2222
                work.append(vfodstep)
2223

    
2224
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
2225
                                  RadioSettingValueList(
2226
                                      OPTSIG_LIST,
2227
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
2228
        work.append(vfoaoptsig)
2229

    
2230
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
2231
                                  RadioSettingValueList(
2232
                                      OPTSIG_LIST,
2233
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
2234
        work.append(vfoboptsig)
2235

    
2236
        if self.COLOR_LCD:
2237
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
2238
                                      RadioSettingValueList(
2239
                                          OPTSIG_LIST,
2240
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
2241
            work.append(vfocoptsig)
2242

    
2243
            if not self.COLOR_LCD4:
2244
                val = RadioSettingValueList(OPTSIG_LIST,
2245
                                            OPTSIG_LIST[_mem.vfo.d.optsig])
2246
                vfodoptsig = RadioSetting("vfo.d.optsig",
2247
                                          "VFO D optional signal", val)
2248
                work.append(vfodoptsig)
2249

    
2250
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
2251
                                  RadioSettingValueList(
2252
                                      SPMUTE_LIST,
2253
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
2254
        work.append(vfoaspmute)
2255

    
2256
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
2257
                                  RadioSettingValueList(
2258
                                      SPMUTE_LIST,
2259
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
2260
        work.append(vfobspmute)
2261

    
2262
        if self.COLOR_LCD:
2263
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
2264
                                      RadioSettingValueList(
2265
                                          SPMUTE_LIST,
2266
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
2267
            work.append(vfocspmute)
2268

    
2269
            if not self.COLOR_LCD4:
2270
                vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
2271
                                          RadioSettingValueList(
2272
                                              SPMUTE_LIST,
2273
                                              SPMUTE_LIST[_mem.vfo.d.spmute]))
2274
                work.append(vfodspmute)
2275

    
2276
        if not self.COLOR_LCD or \
2277
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
2278
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
2279
                                   RadioSettingValueBoolean(
2280
                                       _mem.vfo.a.scramble))
2281
            work.append(vfoascr)
2282

    
2283
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
2284
                                   RadioSettingValueBoolean(
2285
                                       _mem.vfo.b.scramble))
2286
            work.append(vfobscr)
2287

    
2288
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
2289
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
2290
                                   RadioSettingValueBoolean(
2291
                                       _mem.vfo.c.scramble))
2292
            work.append(vfocscr)
2293

    
2294
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
2295
                                   RadioSettingValueBoolean(
2296
                                       _mem.vfo.d.scramble))
2297
            work.append(vfodscr)
2298

    
2299
        if not self.MODEL == "GMRS-50X1":
2300
            vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
2301
                                     RadioSettingValueList(
2302
                                         PTTIDCODE_LIST,
2303
                                         PTTIDCODE_LIST[_mem.vfo.a.scode]))
2304
            work.append(vfoascode)
2305

    
2306
            vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
2307
                                     RadioSettingValueList(
2308
                                         PTTIDCODE_LIST,
2309
                                         PTTIDCODE_LIST[_mem.vfo.b.scode]))
2310
            work.append(vfobscode)
2311

    
2312
            if self.COLOR_LCD:
2313
                vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
2314
                                         RadioSettingValueList(
2315
                                             PTTIDCODE_LIST,
2316
                                             PTTIDCODE_LIST[_mem.vfo.c.scode]))
2317
                work.append(vfocscode)
2318

    
2319
                if not self.COLOR_LCD4:
2320
                    val = RadioSettingValueList(PTTIDCODE_LIST,
2321
                                                PTTIDCODE_LIST[
2322
                                                    _mem.vfo.d.scode])
2323
                    vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
2324
                                             val)
2325
                    work.append(vfodscode)
2326

    
2327
        if not self.MODEL == "GMRS-50X1":
2328
            pttid = RadioSetting("settings.pttid", "PTT ID",
2329
                                 RadioSettingValueList(
2330
                                     PTTID_LIST,
2331
                                     PTTID_LIST[_mem.settings.pttid]))
2332
            work.append(pttid)
2333

    
2334
        if not self.COLOR_LCD:
2335
            # FM presets
2336
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
2337
            top.append(fm_presets)
2338

    
2339
            def fm_validate(value):
2340
                if value == 0:
2341
                    return chirp_common.format_freq(value)
2342
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
2343
                    msg = ("FM-Preset-Frequency: " +
2344
                           "Must be between 87.5 and 108 MHz")
2345
                    raise InvalidValueError(msg)
2346
                return value
2347

    
2348
            def apply_fm_preset_name(setting, obj):
2349
                valstring = str(setting.value)
2350
                for i in range(0, 6):
2351
                    if valstring[i] in VALID_CHARS:
2352
                        obj[i] = valstring[i]
2353
                    else:
2354
                        obj[i] = '0xff'
2355

    
2356
            def apply_fm_freq(setting, obj):
2357
                value = chirp_common.parse_freq(str(setting.value)) / 10
2358
                for i in range(7, -1, -1):
2359
                    obj.freq[i] = value % 10
2360
                    value /= 10
2361

    
2362
            _presets = self._memobj.fm_radio_preset
2363
            i = 1
2364
            for preset in _presets:
2365
                line = RadioSetting("fm_presets_" + str(i),
2366
                                    "Station name " + str(i),
2367
                                    RadioSettingValueString(0, 6, _filter(
2368
                                        preset.broadcast_station_name)))
2369
                line.set_apply_callback(apply_fm_preset_name,
2370
                                        preset.broadcast_station_name)
2371

    
2372
                val = RadioSettingValueFloat(0, 108,
2373
                                             convert_bytes_to_freq(
2374
                                                 preset.freq))
2375
                fmfreq = RadioSetting("fm_presets_" + str(i) + "_freq",
2376
                                      "Frequency " + str(i), val)
2377
                val.set_validate_callback(fm_validate)
2378
                fmfreq.set_apply_callback(apply_fm_freq, preset)
2379
                fm_presets.append(line)
2380
                fm_presets.append(fmfreq)
2381

    
2382
                i = i + 1
2383

    
2384
        # DTMF-Setting
2385
        dtmf_enc_settings = RadioSettingGroup("dtmf_enc_settings",
2386
                                              "DTMF Encoding Settings")
2387
        dtmf_dec_settings = RadioSettingGroup("dtmf_dec_settings",
2388
                                              "DTMF Decoding Settings")
2389
        top.append(dtmf_enc_settings)
2390
        top.append(dtmf_dec_settings)
2391
        txdisable = RadioSetting("dtmf_settings.txdisable",
2392
                                 "TX-Disable",
2393
                                 RadioSettingValueBoolean(
2394
                                     _mem.dtmf_settings.txdisable))
2395
        dtmf_enc_settings.append(txdisable)
2396

    
2397
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
2398
                                 "RX-Disable",
2399
                                 RadioSettingValueBoolean(
2400
                                     _mem.dtmf_settings.rxdisable))
2401
        dtmf_enc_settings.append(rxdisable)
2402

    
2403
        if _mem.dtmf_settings.dtmfspeed_on > 0x0F:
2404
            val = 0x03
2405
        else:
2406
            val = _mem.dtmf_settings.dtmfspeed_on
2407
        dtmfspeed_on = RadioSetting(
2408
            "dtmf_settings.dtmfspeed_on",
2409
            "DTMF Speed (On Time)",
2410
            RadioSettingValueList(LIST_DTMF_SPEED,
2411
                                  LIST_DTMF_SPEED[
2412
                                      val]))
2413
        dtmf_enc_settings.append(dtmfspeed_on)
2414

    
2415
        if _mem.dtmf_settings.dtmfspeed_off > 0x0F:
2416
            val = 0x03
2417
        else:
2418
            val = _mem.dtmf_settings.dtmfspeed_off
2419
        dtmfspeed_off = RadioSetting(
2420
            "dtmf_settings.dtmfspeed_off",
2421
            "DTMF Speed (Off Time)",
2422
            RadioSettingValueList(LIST_DTMF_SPEED,
2423
                                  LIST_DTMF_SPEED[
2424
                                      val]))
2425
        dtmf_enc_settings.append(dtmfspeed_off)
2426

    
2427
        def memory2string(dmtf_mem):
2428
            dtmf_string = ""
2429
            for digit in dmtf_mem:
2430
                if digit != 255:
2431
                    index = LIST_DTMF_VALUES.index(digit)
2432
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2433
            return dtmf_string
2434

    
2435
        def apply_dmtf_frame(setting, obj):
2436
            LOG.debug("Setting DTMF-Code: " + str(setting.value))
2437
            val_string = str(setting.value)
2438
            for i in range(0, 16):
2439
                obj[i] = 255
2440
            i = 0
2441
            for current_char in val_string:
2442
                current_char = current_char.upper()
2443
                index = LIST_DTMF_DIGITS.index(current_char)
2444
                obj[i] = LIST_DTMF_VALUES[index]
2445
                i = i + 1
2446

    
2447
        codes = self._memobj.dtmf_codes
2448
        i = 1
2449
        for dtmfcode in codes:
2450
            val = RadioSettingValueString(0, 16, memory2string(
2451
                                              dtmfcode.code),
2452
                                          False, CHARSET_DTMF_DIGITS)
2453
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
2454
                                "DMTF Code " + str(i), val)
2455
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
2456
            dtmf_enc_settings.append(line)
2457
            i = i + 1
2458

    
2459
        line = RadioSetting("dtmf_settings.mastervice",
2460
                            "Master and Vice ID",
2461
                            RadioSettingValueBoolean(
2462
                                _mem.dtmf_settings.mastervice))
2463
        dtmf_dec_settings.append(line)
2464

    
2465
        val = RadioSettingValueString(0, 16, memory2string(
2466
                                          _mem.dtmf_settings.masterid),
2467
                                      False, CHARSET_DTMF_DIGITS)
2468
        line = RadioSetting("dtmf_settings.masterid",
2469
                            "Master Control ID ", val)
2470
        line.set_apply_callback(apply_dmtf_frame,
2471
                                _mem.dtmf_settings.masterid)
2472
        dtmf_dec_settings.append(line)
2473

    
2474
        line = RadioSetting("dtmf_settings.minspection",
2475
                            "Master Inspection",
2476
                            RadioSettingValueBoolean(
2477
                                _mem.dtmf_settings.minspection))
2478
        dtmf_dec_settings.append(line)
2479

    
2480
        line = RadioSetting("dtmf_settings.mmonitor",
2481
                            "Master Monitor",
2482
                            RadioSettingValueBoolean(
2483
                                _mem.dtmf_settings.mmonitor))
2484
        dtmf_dec_settings.append(line)
2485

    
2486
        line = RadioSetting("dtmf_settings.mstun",
2487
                            "Master Stun",
2488
                            RadioSettingValueBoolean(
2489
                                _mem.dtmf_settings.mstun))
2490
        dtmf_dec_settings.append(line)
2491

    
2492
        line = RadioSetting("dtmf_settings.mkill",
2493
                            "Master Kill",
2494
                            RadioSettingValueBoolean(
2495
                                _mem.dtmf_settings.mkill))
2496
        dtmf_dec_settings.append(line)
2497

    
2498
        line = RadioSetting("dtmf_settings.mrevive",
2499
                            "Master Revive",
2500
                            RadioSettingValueBoolean(
2501
                                _mem.dtmf_settings.mrevive))
2502
        dtmf_dec_settings.append(line)
2503

    
2504
        val = RadioSettingValueString(0, 16, memory2string(
2505
                                          _mem.dtmf_settings.viceid),
2506
                                      False, CHARSET_DTMF_DIGITS)
2507
        line = RadioSetting("dtmf_settings.viceid",
2508
                            "Vice Control ID ", val)
2509
        line.set_apply_callback(apply_dmtf_frame,
2510
                                _mem.dtmf_settings.viceid)
2511
        dtmf_dec_settings.append(line)
2512

    
2513
        line = RadioSetting("dtmf_settings.vinspection",
2514
                            "Vice Inspection",
2515
                            RadioSettingValueBoolean(
2516
                                _mem.dtmf_settings.vinspection))
2517
        dtmf_dec_settings.append(line)
2518

    
2519
        line = RadioSetting("dtmf_settings.vmonitor",
2520
                            "Vice Monitor",
2521
                            RadioSettingValueBoolean(
2522
                                _mem.dtmf_settings.vmonitor))
2523
        dtmf_dec_settings.append(line)
2524

    
2525
        line = RadioSetting("dtmf_settings.vstun",
2526
                            "Vice Stun",
2527
                            RadioSettingValueBoolean(
2528
                                _mem.dtmf_settings.vstun))
2529
        dtmf_dec_settings.append(line)
2530

    
2531
        line = RadioSetting("dtmf_settings.vkill",
2532
                            "Vice Kill",
2533
                            RadioSettingValueBoolean(
2534
                                _mem.dtmf_settings.vkill))
2535
        dtmf_dec_settings.append(line)
2536

    
2537
        line = RadioSetting("dtmf_settings.vrevive",
2538
                            "Vice Revive",
2539
                            RadioSettingValueBoolean(
2540
                                _mem.dtmf_settings.vrevive))
2541
        dtmf_dec_settings.append(line)
2542

    
2543
        val = RadioSettingValueString(0, 16, memory2string(
2544
                                          _mem.dtmf_settings.inspection),
2545
                                      False, CHARSET_DTMF_DIGITS)
2546
        line = RadioSetting("dtmf_settings.inspection",
2547
                            "Inspection", val)
2548
        line.set_apply_callback(apply_dmtf_frame,
2549
                                _mem.dtmf_settings.inspection)
2550
        dtmf_dec_settings.append(line)
2551

    
2552
        val = RadioSettingValueString(0, 16, memory2string(
2553
                                          _mem.dtmf_settings.alarmcode),
2554
                                      False, CHARSET_DTMF_DIGITS)
2555
        line = RadioSetting("dtmf_settings.alarmcode",
2556
                            "Alarm", val)
2557
        line.set_apply_callback(apply_dmtf_frame,
2558
                                _mem.dtmf_settings.alarmcode)
2559
        dtmf_dec_settings.append(line)
2560

    
2561
        val = RadioSettingValueString(0, 16, memory2string(
2562
                                          _mem.dtmf_settings.kill),
2563
                                      False, CHARSET_DTMF_DIGITS)
2564
        line = RadioSetting("dtmf_settings.kill",
2565
                            "Kill", val)
2566
        line.set_apply_callback(apply_dmtf_frame,
2567
                                _mem.dtmf_settings.kill)
2568
        dtmf_dec_settings.append(line)
2569

    
2570
        val = RadioSettingValueString(0, 16, memory2string(
2571
                                          _mem.dtmf_settings.monitor),
2572
                                      False, CHARSET_DTMF_DIGITS)
2573
        line = RadioSetting("dtmf_settings.monitor",
2574
                            "Monitor", val)
2575
        line.set_apply_callback(apply_dmtf_frame,
2576
                                _mem.dtmf_settings.monitor)
2577
        dtmf_dec_settings.append(line)
2578

    
2579
        val = RadioSettingValueString(0, 16, memory2string(
2580
                                          _mem.dtmf_settings.stun),
2581
                                      False, CHARSET_DTMF_DIGITS)
2582
        line = RadioSetting("dtmf_settings.stun",
2583
                            "Stun", val)
2584
        line.set_apply_callback(apply_dmtf_frame,
2585
                                _mem.dtmf_settings.stun)
2586
        dtmf_dec_settings.append(line)
2587

    
2588
        val = RadioSettingValueString(0, 16, memory2string(
2589
                                          _mem.dtmf_settings.revive),
2590
                                      False, CHARSET_DTMF_DIGITS)
2591
        line = RadioSetting("dtmf_settings.revive",
2592
                            "Revive", val)
2593
        line.set_apply_callback(apply_dmtf_frame,
2594
                                _mem.dtmf_settings.revive)
2595
        dtmf_dec_settings.append(line)
2596

    
2597
        def apply_dmtf_listvalue(setting, obj):
2598
            LOG.debug("Setting value: " + str(setting.value) + " from list")
2599
            val = str(setting.value)
2600
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2601
            val = LIST_DTMF_SPECIAL_VALUES[index]
2602
            obj.set_value(val)
2603

    
2604
        if _mem.dtmf_settings.groupcode not in LIST_DTMF_SPECIAL_VALUES:
2605
            val = 0x0B
2606
        else:
2607
            val = _mem.dtmf_settings.groupcode
2608
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2609
        line = RadioSetting(
2610
            "dtmf_settings.groupcode",
2611
            "Group Code",
2612
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2613
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2614
        line.set_apply_callback(apply_dmtf_listvalue,
2615
                                _mem.dtmf_settings.groupcode)
2616
        dtmf_dec_settings.append(line)
2617

    
2618
        if _mem.dtmf_settings.spacecode not in LIST_DTMF_SPECIAL_VALUES:
2619
            val = 0x0C
2620
        else:
2621
            val = _mem.dtmf_settings.spacecode
2622
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2623
        line = RadioSetting(
2624
            "dtmf_settings.spacecode",
2625
            "Space Code",
2626
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2627
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2628
        line.set_apply_callback(apply_dmtf_listvalue,
2629
                                _mem.dtmf_settings.spacecode)
2630
        dtmf_dec_settings.append(line)
2631

    
2632
        if self.COLOR_LCD:
2633
            if _mem.dtmf_settings.resettime > 0x63:
2634
                val = 0x4F
2635
            else:
2636
                val = _mem.dtmf_settings.resettime
2637
            line = RadioSetting(
2638
                "dtmf_settings.resettime",
2639
                "Reset time",
2640
                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
2641
                                      LIST_5TONE_RESET_COLOR[
2642
                                          val]))
2643
            dtmf_dec_settings.append(line)
2644
        else:
2645
            line = RadioSetting(
2646
                "dtmf_settings.resettime",
2647
                "Reset time",
2648
                RadioSettingValueList(LIST_5TONE_RESET,
2649
                                      LIST_5TONE_RESET[
2650
                                          _mem.dtmf_settings.resettime]))
2651
            dtmf_dec_settings.append(line)
2652

    
2653
        if _mem.dtmf_settings.delayproctime > 0x27:
2654
            val = 0x04
2655
        else:
2656
            val = _mem.dtmf_settings.delayproctime
2657
        line = RadioSetting(
2658
            "dtmf_settings.delayproctime",
2659
            "Delay processing time",
2660
            RadioSettingValueList(LIST_DTMF_DELAY,
2661
                                  LIST_DTMF_DELAY[
2662
                                      val]))
2663
        dtmf_dec_settings.append(line)
2664

    
2665
        # 5 Tone Settings
2666
        stds_5tone = RadioSettingGroup("stds_5tone", "Standards")
2667
        codes_5tone = RadioSettingGroup("codes_5tone", "Codes")
2668

    
2669
        group_5tone = RadioSettingGroup("group_5tone", "5 Tone Settings")
2670
        group_5tone.append(stds_5tone)
2671
        group_5tone.append(codes_5tone)
2672

    
2673
        top.append(group_5tone)
2674

    
2675
        def apply_list_value(setting, obj):
2676
            options = setting.value.get_options()
2677
            obj.set_value(options.index(str(setting.value)))
2678

    
2679
        _5tone_standards = self._memobj._5tone_std_settings
2680
        i = 0
2681
        for standard in _5tone_standards:
2682
            std_5tone = RadioSettingGroup("std_5tone_" + str(i),
2683
                                          LIST_5TONE_STANDARDS[i])
2684
            stds_5tone.append(std_5tone)
2685

    
2686
            period = standard.period
2687
            if period == 255:
2688
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2689
                          " is not yet configured. Setting to 70ms.")
2690
                period = 5
2691

    
2692
            if period <= len(LIST_5TONE_STANDARD_PERIODS):
2693
                line = RadioSetting(
2694
                    "_5tone_std_settings_" + str(i) + "_period",
2695
                    "Period (ms)", RadioSettingValueList
2696
                    (LIST_5TONE_STANDARD_PERIODS,
2697
                     LIST_5TONE_STANDARD_PERIODS[period]))
2698
                line.set_apply_callback(apply_list_value, standard.period)
2699
                std_5tone.append(line)
2700
            else:
2701
                LOG.debug("Invalid value for 5tone period! Disabling.")
2702

    
2703
            group_tone = standard.group_tone
2704
            if group_tone == 255:
2705
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2706
                          " is not yet configured. Setting to A.")
2707
                group_tone = 10
2708

    
2709
            if group_tone <= len(LIST_5TONE_DIGITS):
2710
                line = RadioSetting(
2711
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2712
                    "Group Tone",
2713
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2714
                                          LIST_5TONE_DIGITS[
2715
                                              group_tone]))
2716
                line.set_apply_callback(apply_list_value,
2717
                                        standard.group_tone)
2718
                std_5tone.append(line)
2719
            else:
2720
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2721

    
2722
            repeat_tone = standard.repeat_tone
2723
            if repeat_tone == 255:
2724
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2725
                          " is not yet configured. Setting to E.")
2726
                repeat_tone = 14
2727

    
2728
            if repeat_tone <= len(LIST_5TONE_DIGITS):
2729
                line = RadioSetting(
2730
                    "_5tone_std_settings_" + str(i) + "_repttone",
2731
                    "Repeat Tone",
2732
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2733
                                          LIST_5TONE_DIGITS[
2734
                                              repeat_tone]))
2735
                line.set_apply_callback(apply_list_value,
2736
                                        standard.repeat_tone)
2737
                std_5tone.append(line)
2738
            else:
2739
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2740
            i = i + 1
2741

    
2742
        def my_apply_5tonestdlist_value(setting, obj):
2743
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2744
                obj.set_value(0xFF)
2745
            else:
2746
                obj.set_value(LIST_5TONE_STANDARDS.
2747
                              index(str(setting.value)))
2748

    
2749
        def apply_5tone_frame(setting, obj):
2750
            LOG.debug("Setting 5 Tone: " + str(setting.value))
2751
            valstring = str(setting.value)
2752
            if len(valstring) == 0:
2753
                for i in range(0, 5):
2754
                    obj[i] = 255
2755
            else:
2756
                validFrame = True
2757
                for i in range(0, 5):
2758
                    currentChar = valstring[i].upper()
2759
                    if currentChar in LIST_5TONE_DIGITS:
2760
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2761
                    else:
2762
                        validFrame = False
2763
                        LOG.debug("invalid char: " + str(currentChar))
2764
                if not validFrame:
2765
                    LOG.debug("setting whole frame to FF")
2766
                    for i in range(0, 5):
2767
                        obj[i] = 255
2768

    
2769
        def validate_5tone_frame(value):
2770
            if (len(str(value)) != 5) and (len(str(value)) != 0):
2771
                msg = ("5 Tone must have 5 digits or 0 digits")
2772
                raise InvalidValueError(msg)
2773
            for digit in str(value):
2774
                if digit.upper() not in LIST_5TONE_DIGITS:
2775
                    msg = (str(digit) + " is not a valid digit for 5tones")
2776
                    raise InvalidValueError(msg)
2777
            return value
2778

    
2779
        def frame2string(frame):
2780
            frameString = ""
2781
            for digit in frame:
2782
                if digit != 255:
2783
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2784
            return frameString
2785

    
2786
        _5tone_codes = self._memobj._5tone_codes
2787
        i = 1
2788
        for code in _5tone_codes:
2789
            code_5tone = RadioSettingGroup("code_5tone_" + str(i),
2790
                                           "5 Tone code " + str(i))
2791
            codes_5tone.append(code_5tone)
2792
            if (code.standard == 255):
2793
                currentVal = 15
2794
            else:
2795
                currentVal = code.standard
2796
            line = RadioSetting("_5tone_code_" + str(i) + "_std",
2797
                                " Standard",
2798
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2799
                                                      LIST_5TONE_STANDARDS[
2800
                                                          currentVal]))
2801
            line.set_apply_callback(my_apply_5tonestdlist_value,
2802
                                    code.standard)
2803
            code_5tone.append(line)
2804

    
2805
            val = RadioSettingValueString(0, 6,
2806
                                          frame2string(code.frame1), False)
2807
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
2808
                                " Frame 1", val)
2809
            val.set_validate_callback(validate_5tone_frame)
2810
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2811
            code_5tone.append(line)
2812

    
2813
            val = RadioSettingValueString(0, 6,
2814
                                          frame2string(code.frame2), False)
2815
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2816
                                " Frame 2", val)
2817
            val.set_validate_callback(validate_5tone_frame)
2818
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2819
            code_5tone.append(line)
2820

    
2821
            val = RadioSettingValueString(0, 6,
2822
                                          frame2string(code.frame3), False)
2823
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2824
                                " Frame 3", val)
2825
            val.set_validate_callback(validate_5tone_frame)
2826
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2827
            code_5tone.append(line)
2828
            i = i + 1
2829

    
2830
        _5_tone_decode1 = RadioSetting(
2831
            "_5tone_settings._5tone_decode_call_frame1",
2832
            "5 Tone decode call Frame 1",
2833
            RadioSettingValueBoolean(
2834
                _mem._5tone_settings._5tone_decode_call_frame1))
2835
        group_5tone.append(_5_tone_decode1)
2836

    
2837
        _5_tone_decode2 = RadioSetting(
2838
            "_5tone_settings._5tone_decode_call_frame2",
2839
            "5 Tone decode call Frame 2",
2840
            RadioSettingValueBoolean(
2841
                _mem._5tone_settings._5tone_decode_call_frame2))
2842
        group_5tone.append(_5_tone_decode2)
2843

    
2844
        _5_tone_decode3 = RadioSetting(
2845
            "_5tone_settings._5tone_decode_call_frame3",
2846
            "5 Tone decode call Frame 3",
2847
            RadioSettingValueBoolean(
2848
                _mem._5tone_settings._5tone_decode_call_frame3))
2849
        group_5tone.append(_5_tone_decode3)
2850

    
2851
        _5_tone_decode_disp1 = RadioSetting(
2852
            "_5tone_settings._5tone_decode_disp_frame1",
2853
            "5 Tone decode disp Frame 1",
2854
            RadioSettingValueBoolean(
2855
                _mem._5tone_settings._5tone_decode_disp_frame1))
2856
        group_5tone.append(_5_tone_decode_disp1)
2857

    
2858
        _5_tone_decode_disp2 = RadioSetting(
2859
            "_5tone_settings._5tone_decode_disp_frame2",
2860
            "5 Tone decode disp Frame 2",
2861
            RadioSettingValueBoolean(
2862
                _mem._5tone_settings._5tone_decode_disp_frame2))
2863
        group_5tone.append(_5_tone_decode_disp2)
2864

    
2865
        _5_tone_decode_disp3 = RadioSetting(
2866
            "_5tone_settings._5tone_decode_disp_frame3",
2867
            "5 Tone decode disp Frame 3",
2868
            RadioSettingValueBoolean(
2869
                _mem._5tone_settings._5tone_decode_disp_frame3))
2870
        group_5tone.append(_5_tone_decode_disp3)
2871

    
2872
        decode_standard = _mem._5tone_settings.decode_standard
2873
        if decode_standard == 255:
2874
            decode_standard = 0
2875
        if decode_standard <= len(LIST_5TONE_STANDARDS_without_none):
2876
            line = RadioSetting("_5tone_settings.decode_standard",
2877
                                "5 Tone-decode Standard",
2878
                                RadioSettingValueList(
2879
                                    LIST_5TONE_STANDARDS_without_none,
2880
                                    LIST_5TONE_STANDARDS_without_none[
2881
                                        decode_standard]))
2882
            group_5tone.append(line)
2883
        else:
2884
            LOG.debug("Invalid decode std...")
2885

    
2886
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2887
        if _5tone_delay1 == 255:
2888
            _5tone_delay1 = 20
2889

    
2890
        if _5tone_delay1 <= len(LIST_5TONE_DELAY):
2891
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2892
                                         LIST_5TONE_DELAY[
2893
                                             _5tone_delay1])
2894
            line = RadioSetting("_5tone_settings._5tone_delay1",
2895
                                "5 Tone Delay Frame 1", list)
2896
            group_5tone.append(line)
2897
        else:
2898
            LOG.debug(
2899
                "Invalid value for 5tone delay (frame1) ! Disabling.")
2900

    
2901
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2902
        if _5tone_delay2 == 255:
2903
            _5tone_delay2 = 20
2904
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2905

    
2906
        if _5tone_delay2 <= len(LIST_5TONE_DELAY):
2907
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2908
                                         LIST_5TONE_DELAY[
2909
                                             _5tone_delay2])
2910
            line = RadioSetting("_5tone_settings._5tone_delay2",
2911
                                "5 Tone Delay Frame 2", list)
2912
            group_5tone.append(line)
2913
        else:
2914
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2915

    
2916
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2917
        if _5tone_delay3 == 255:
2918
            _5tone_delay3 = 20
2919
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2920

    
2921
        if _5tone_delay3 <= len(LIST_5TONE_DELAY):
2922
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2923
                                         LIST_5TONE_DELAY[
2924
                                             _5tone_delay3])
2925
            line = RadioSetting("_5tone_settings._5tone_delay3",
2926
                                "5 Tone Delay Frame 3", list)
2927
            group_5tone.append(line)
2928
        else:
2929
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2930

    
2931
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2932
        if ext_length == 255:
2933
            ext_length = 0
2934
            LOG.debug("1st Tone ext length unconfigured! Resetting to 0")
2935

    
2936
        if ext_length <= len(LIST_5TONE_DELAY):
2937
            list = RadioSettingValueList(
2938
                LIST_5TONE_DELAY,
2939
                LIST_5TONE_DELAY[
2940
                    ext_length])
2941
            line = RadioSetting(
2942
                "_5tone_settings._5tone_first_digit_ext_length",
2943
                "First digit extend length", list)
2944
            group_5tone.append(line)
2945
        else:
2946
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2947

    
2948
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2949
        if decode_reset_time == 255:
2950
            decode_reset_time = 59
2951
            LOG.debug("Decode reset time unconfigured. resetting.")
2952
        if decode_reset_time <= len(LIST_5TONE_RESET):
2953
            list = RadioSettingValueList(
2954
                LIST_5TONE_RESET,
2955
                LIST_5TONE_RESET[
2956
                    decode_reset_time])
2957
            line = RadioSetting("_5tone_settings.decode_reset_time",
2958
                                "Decode reset time", list)
2959
            group_5tone.append(line)
2960
        else:
2961
            LOG.debug("Invalid value decode reset time! Disabling.")
2962

    
2963
        # 2 Tone
2964
        encode_2tone = RadioSettingGroup("encode_2tone", "2 Tone Encode")
2965
        decode_2tone = RadioSettingGroup("decode_2tone", "2 Code Decode")
2966

    
2967
        top.append(encode_2tone)
2968
        top.append(decode_2tone)
2969

    
2970
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2971
        if duration_1st_tone == 255:
2972
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2973
                      "configured. Setting to 600ms")
2974
            duration_1st_tone = 60
2975

    
2976
        if duration_1st_tone <= len(LIST_5TONE_DELAY):
2977
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2978
                                        LIST_5TONE_DELAY[
2979
                                            duration_1st_tone])
2980
            line = RadioSetting("_2tone.duration_1st_tone",
2981
                                "Duration 1st Tone", val)
2982
            encode_2tone.append(line)
2983

    
2984
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2985
        if duration_2nd_tone == 255:
2986
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2987
                      "configured. Setting to 600ms")
2988
            duration_2nd_tone = 60
2989

    
2990
        if duration_2nd_tone <= len(LIST_5TONE_DELAY):
2991
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2992
                                        LIST_5TONE_DELAY[
2993
                                            duration_2nd_tone])
2994
            line = RadioSetting("_2tone.duration_2nd_tone",
2995
                                "Duration 2nd Tone", val)
2996
            encode_2tone.append(line)
2997

    
2998
        duration_gap = self._memobj._2tone.duration_gap
2999
        if duration_gap == 255:
3000
            LOG.debug("Duration of gap is not yet " +
3001
                      "configured. Setting to 300ms")
3002
            duration_gap = 30
3003

    
3004
        if duration_gap <= len(LIST_5TONE_DELAY):
3005
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
3006
                                RadioSettingValueList(LIST_5TONE_DELAY,
3007
                                                      LIST_5TONE_DELAY[
3008
                                                          duration_gap]))
3009
            encode_2tone.append(line)
3010

    
3011
        def _2tone_validate(value):
3012
            if value == 0:
3013
                return 65535
3014
            if value == 65535:
3015
                return value
3016
            if not (300 <= value and value <= 3000):
3017
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
3018
                raise InvalidValueError(msg)
3019
            return value
3020

    
3021
        def apply_2tone_freq(setting, obj):
3022
            val = int(setting.value)
3023
            if (val == 0) or (val == 65535):
3024
                obj.set_value(65535)
3025
            else:
3026
                obj.set_value(val)
3027

    
3028
        i = 1
3029
        for code in self._memobj._2tone._2tone_encode:
3030
            code_2tone = RadioSettingGroup("code_2tone_" + str(i),
3031
                                           "Encode Code " + str(i))
3032
            encode_2tone.append(code_2tone)
3033

    
3034
            tmp = code.freq1
3035
            if tmp == 65535:
3036
                tmp = 0
3037
            val1 = RadioSettingValueInteger(0, 65535, tmp)
3038
            freq1 = RadioSetting("2tone_code_" + str(i) + "_freq1",
3039
                                 "Frequency 1", val1)
3040
            val1.set_validate_callback(_2tone_validate)
3041
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
3042
            code_2tone.append(freq1)
3043

    
3044
            tmp = code.freq2
3045
            if tmp == 65535:
3046
                tmp = 0
3047
            val2 = RadioSettingValueInteger(0, 65535, tmp)
3048
            freq2 = RadioSetting("2tone_code_" + str(i) + "_freq2",
3049
                                 "Frequency 2", val2)
3050
            val2.set_validate_callback(_2tone_validate)
3051
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
3052
            code_2tone.append(freq2)
3053

    
3054
            i = i + 1
3055

    
3056
        decode_reset_time = _mem._2tone.reset_time
3057
        if decode_reset_time == 255:
3058
            decode_reset_time = 59
3059
            LOG.debug("Decode reset time unconfigured. resetting.")
3060
        if decode_reset_time <= len(LIST_5TONE_RESET):
3061
            list = RadioSettingValueList(
3062
                LIST_5TONE_RESET,
3063
                LIST_5TONE_RESET[
3064
                    decode_reset_time])
3065
            line = RadioSetting("_2tone.reset_time",
3066
                                "Decode reset time", list)
3067
            decode_2tone.append(line)
3068
        else:
3069
            LOG.debug("Invalid value decode reset time! Disabling.")
3070

    
3071
        def apply_2tone_freq_pair(setting, obj):
3072
            val = int(setting.value)
3073
            derived_val = 65535
3074
            frqname = str(setting._name[-5:])
3075
            derivedname = "derived_from_" + frqname
3076

    
3077
            if (val == 0):
3078
                val = 65535
3079
                derived_val = 65535
3080
            else:
3081
                derived_val = int(round(2304000.0/val))
3082

    
3083
            obj[frqname].set_value(val)
3084
            obj[derivedname].set_value(derived_val)
3085

    
3086
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " +
3087
                      derivedname + ": " + str(derived_val))
3088

    
3089
        i = 1
3090
        for decode_code in self._memobj._2tone._2tone_decode:
3091
            _2tone_dec_code = RadioSettingGroup("code_2tone_" + str(i),
3092
                                                "Decode Code " + str(i))
3093
            decode_2tone.append(_2tone_dec_code)
3094

    
3095
            j = 1
3096
            for dec in decode_code.decs:
3097
                val = dec.dec
3098
                if val == 255:
3099
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
3100
                              " is not yet configured. Setting to 0.")
3101
                    val = 0
3102

    
3103
                if val <= len(LIST_2TONE_DEC):
3104
                    line = RadioSetting(
3105
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
3106
                        "Dec " + str(j), RadioSettingValueList
3107
                        (LIST_2TONE_DEC,
3108
                         LIST_2TONE_DEC[val]))
3109
                    line.set_apply_callback(apply_list_value, dec.dec)
3110
                    _2tone_dec_code.append(line)
3111
                else:
3112
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
3113

    
3114
                val = dec.response
3115
                if val == 255:
3116
                    LOG.debug("Response for Code " +
3117
                              str(i) + " Dec " + str(j) +
3118
                              " is not yet configured. Setting to 0.")
3119
                    val = 0
3120

    
3121
                if val <= len(LIST_2TONE_RESPONSE):
3122
                    line = RadioSetting(
3123
                        "_2tone_dec_settings_" +
3124
                        str(i) + "_resp_" + str(j),
3125
                        "Response " + str(j), RadioSettingValueList
3126
                        (LIST_2TONE_RESPONSE,
3127
                         LIST_2TONE_RESPONSE[val]))
3128
                    line.set_apply_callback(apply_list_value, dec.response)
3129
                    _2tone_dec_code.append(line)
3130
                else:
3131
                    LOG.debug(
3132
                        "Invalid value for 2tone response! Disabling.")
3133

    
3134
                val = dec.alert
3135
                if val == 255:
3136
                    LOG.debug("Alert for Code " +
3137
                              str(i) + " Dec " + str(j) +
3138
                              " is not yet configured. Setting to 0.")
3139
                    val = 0
3140

    
3141
                if val <= len(PTTIDCODE_LIST):
3142
                    line = RadioSetting(
3143
                        "_2tone_dec_settings_" +
3144
                        str(i) + "_alert_" + str(j),
3145
                        "Alert " + str(j), RadioSettingValueList
3146
                        (PTTIDCODE_LIST,
3147
                         PTTIDCODE_LIST[val]))
3148
                    line.set_apply_callback(apply_list_value, dec.alert)
3149
                    _2tone_dec_code.append(line)
3150
                else:
3151
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
3152
                j = j + 1
3153

    
3154
            freq = self._memobj._2tone.freqs[i-1]
3155
            for char in ['A', 'B', 'C', 'D']:
3156
                setting_name = "freq" + str(char)
3157

    
3158
                tmp = freq[setting_name]
3159
                if tmp == 65535:
3160
                    tmp = 0
3161
                if tmp != 0:
3162
                    expected = int(round(2304000.0/tmp))
3163
                    from_mem = freq["derived_from_" + setting_name]
3164
                    if expected != from_mem:
3165
                        LOG.error("Expected " + str(expected) +
3166
                                  " but read " + str(from_mem) +
3167
                                  ". Disabling 2Tone Decode Freqs!")
3168
                        break
3169
                val = RadioSettingValueInteger(0, 65535, tmp)
3170
                frq = RadioSetting("2tone_dec_" + str(i) +
3171
                                   "_freq" + str(char),
3172
                                   ("Decode Frequency " + str(char)), val)
3173
                val.set_validate_callback(_2tone_validate)
3174
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
3175
                _2tone_dec_code.append(frq)
3176

    
3177
            i = i + 1
3178

    
3179
        return top
3180

    
3181
    def set_settings(self, settings):
3182
        _settings = self._memobj.settings
3183
        for element in settings:
3184
            if not isinstance(element, RadioSetting):
3185
                if element.get_name() == "fm_preset":
3186
                    self._set_fm_preset(element)
3187
                else:
3188
                    self.set_settings(element)
3189
                    continue
3190
            else:
3191
                try:
3192
                    name = element.get_name()
3193
                    if "." in name:
3194
                        bits = name.split(".")
3195
                        obj = self._memobj
3196
                        for bit in bits[:-1]:
3197
                            if "/" in bit:
3198
                                bit, index = bit.split("/", 1)
3199
                                index = int(index)
3200
                                obj = getattr(obj, bit)[index]
3201
                            else:
3202
                                obj = getattr(obj, bit)
3203
                        setting = bits[-1]
3204
                    else:
3205
                        obj = _settings
3206
                        setting = element.get_name()
3207

    
3208
                    if element.has_apply_callback():
3209
                        LOG.debug("Using apply callback")
3210
                        element.run_apply_callback()
3211
                    elif setting == "volume" and self.MODEL == "KT-WP12":
3212
                        setattr(obj, setting, int(element.value) - 1)
3213
                    elif setting == "volume" and self.MODEL == "WP-9900":
3214
                        setattr(obj, setting, int(element.value) - 1)
3215
                    elif element.value.get_mutable():
3216
                        LOG.debug("Setting %s = %s" % (setting, element.value))
3217
                        setattr(obj, setting, element.value)
3218
                except Exception, e:
3219
                    LOG.debug(element.get_name())
3220
                    raise
3221

    
3222
    @classmethod
3223
    def match_model(cls, filedata, filename):
3224
        match_size = False
3225
        match_model = False
3226

    
3227
        # testing the file data size
3228
        if len(filedata) == MEM_SIZE:
3229
            match_size = True
3230

    
3231
        # testing the firmware model fingerprint
3232
        match_model = model_match(cls, filedata)
3233

    
3234
        if match_size and match_model:
3235
            return True
3236
        else:
3237
            return False
3238

    
3239

    
3240
MEM_FORMAT = """
3241
#seekto 0x0000;
3242
struct {
3243
  lbcd rxfreq[4];
3244
  lbcd txfreq[4];
3245
  ul16 rxtone;
3246
  ul16 txtone;
3247
  u8 unknown0:4,
3248
     scode:4;
3249
  u8 unknown1:2,
3250
     spmute:2,
3251
     unknown2:2,
3252
     optsig:2;
3253
  u8 unknown3:3,
3254
     scramble:1,
3255
     unknown4:3,
3256
     power:1;
3257
  u8 unknown5:1,
3258
     wide:1,
3259
     unknown6:2,
3260
     bcl:1,
3261
     add:1,
3262
     pttid:2;
3263
} memory[200];
3264

    
3265
#seekto 0x0E00;
3266
struct {
3267
  u8 tdr;
3268
  u8 unknown1;
3269
  u8 sql;
3270
  u8 unknown2[2];
3271
  u8 tot;
3272
  u8 apo;           // BTech radios use this as the Auto Power Off time
3273
                    // other radios use this as pre-Time Out Alert
3274
  u8 unknown3;
3275
  u8 abr;
3276
  u8 beep;
3277
  u8 unknown4[4];
3278
  u8 dtmfst;
3279
  u8 unknown5[2];
3280
  u8 prisc;
3281
  u8 prich;
3282
  u8 screv;
3283
  u8 unknown6[2];
3284
  u8 pttid;
3285
  u8 pttlt;
3286
  u8 unknown7;
3287
  u8 emctp;
3288
  u8 emcch;
3289
  u8 ringt;
3290
  u8 unknown8;
3291
  u8 camdf;
3292
  u8 cbmdf;
3293
  u8 sync;          // BTech radios use this as the display sync setting
3294
                    // other radios use this as the auto keypad lock setting
3295
  u8 ponmsg;
3296
  u8 wtled;
3297
  u8 rxled;
3298
  u8 txled;
3299
  u8 unknown9[5];
3300
  u8 anil;
3301
  u8 reps;
3302
  u8 repm;
3303
  u8 tdrab;
3304
  u8 ste;
3305
  u8 rpste;
3306
  u8 rptdl;
3307
  u8 mgain;
3308
  u8 dtmfg;
3309
} settings;
3310

    
3311
#seekto 0x0E80;
3312
struct {
3313
  u8 unknown1;
3314
  u8 vfomr;
3315
  u8 keylock;
3316
  u8 unknown2;
3317
  u8 unknown3:4,
3318
     vfomren:1,
3319
     unknown4:1,
3320
     reseten:1,
3321
     menuen:1;
3322
  u8 unknown5[11];
3323
  u8 dispab;
3324
  u8 mrcha;
3325
  u8 mrchb;
3326
  u8 menu;
3327
} settings2;
3328

    
3329
#seekto 0x0EC0;
3330
struct {
3331
  char line1[6];
3332
  char line2[6];
3333
} poweron_msg;
3334

    
3335
struct settings_vfo {
3336
  u8 freq[8];
3337
  u8 offset[6];
3338
  u8 unknown2[2];
3339
  ul16 rxtone;
3340
  ul16 txtone;
3341
  u8 scode;
3342
  u8 spmute;
3343
  u8 optsig;
3344
  u8 scramble;
3345
  u8 wide;
3346
  u8 power;
3347
  u8 shiftd;
3348
  u8 step;
3349
  u8 unknown3[4];
3350
};
3351

    
3352
#seekto 0x0F00;
3353
struct {
3354
  struct settings_vfo a;
3355
  struct settings_vfo b;
3356
} vfo;
3357

    
3358
#seekto 0x1000;
3359
struct {
3360
  char name[6];
3361
  u8 unknown1[10];
3362
} names[200];
3363

    
3364
#seekto 0x2400;
3365
struct {
3366
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3367
  u8 group_tone;
3368
  u8 repeat_tone;
3369
  u8 unused[13];
3370
} _5tone_std_settings[15];
3371

    
3372
#seekto 0x2500;
3373
struct {
3374
  u8 frame1[5];
3375
  u8 frame2[5];
3376
  u8 frame3[5];
3377
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3378
} _5tone_codes[15];
3379

    
3380
#seekto 0x25F0;
3381
struct {
3382
  u8 _5tone_delay1; // * 10ms
3383
  u8 _5tone_delay2; // * 10ms
3384
  u8 _5tone_delay3; // * 10ms
3385
  u8 _5tone_first_digit_ext_length;
3386
  u8 unknown1;
3387
  u8 unknown2;
3388
  u8 unknown3;
3389
  u8 unknown4;
3390
  u8 decode_standard;
3391
  u8 unknown5:5,
3392
     _5tone_decode_call_frame3:1,
3393
     _5tone_decode_call_frame2:1,
3394
     _5tone_decode_call_frame1:1;
3395
  u8 unknown6:5,
3396
     _5tone_decode_disp_frame3:1,
3397
     _5tone_decode_disp_frame2:1,
3398
     _5tone_decode_disp_frame1:1;
3399
  u8 decode_reset_time; // * 100 + 100ms
3400
} _5tone_settings;
3401

    
3402
#seekto 0x2900;
3403
struct {
3404
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3405
} dtmf_codes[15];
3406

    
3407
#seekto 0x29F0;
3408
struct {
3409
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3410
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3411
  u8 unknown0[14];
3412
  u8 inspection[16];
3413
  u8 monitor[16];
3414
  u8 alarmcode[16];
3415
  u8 stun[16];
3416
  u8 kill[16];
3417
  u8 revive[16];
3418
  u8 unknown1[16];
3419
  u8 unknown2[16];
3420
  u8 unknown3[16];
3421
  u8 unknown4[16];
3422
  u8 unknown5[16];
3423
  u8 unknown6[16];
3424
  u8 unknown7[16];
3425
  u8 masterid[16];
3426
  u8 viceid[16];
3427
  u8 unused01:7,
3428
     mastervice:1;
3429
  u8 unused02:3,
3430
     mrevive:1,
3431
     mkill:1,
3432
     mstun:1,
3433
     mmonitor:1,
3434
     minspection:1;
3435
  u8 unused03:3,
3436
     vrevive:1,
3437
     vkill:1,
3438
     vstun:1,
3439
     vmonitor:1,
3440
     vinspection:1;
3441
  u8 unused04:6,
3442
     txdisable:1,
3443
     rxdisable:1;
3444
  u8 groupcode;
3445
  u8 spacecode;
3446
  u8 delayproctime; // * 100 + 100ms
3447
  u8 resettime;     // * 100 + 100ms
3448
} dtmf_settings;
3449

    
3450
#seekto 0x2D00;
3451
struct {
3452
  struct {
3453
    ul16 freq1;
3454
    u8 unused01[6];
3455
    ul16 freq2;
3456
    u8 unused02[6];
3457
  } _2tone_encode[15];
3458
  u8 duration_1st_tone; // *10ms
3459
  u8 duration_2nd_tone; // *10ms
3460
  u8 duration_gap;      // *10ms
3461
  u8 unused03[13];
3462
  struct {
3463
    struct {
3464
      u8 dec;      // one out of LIST_2TONE_DEC
3465
      u8 response; // one out of LIST_2TONE_RESPONSE
3466
      u8 alert;    // 1-16
3467
    } decs[4];
3468
    u8 unused04[4];
3469
  } _2tone_decode[15];
3470
  u8 unused05[16];
3471

    
3472
  struct {
3473
    ul16 freqA;
3474
    ul16 freqB;
3475
    ul16 freqC;
3476
    ul16 freqD;
3477
    // unknown what those values mean, but they are
3478
    // derived from configured frequencies
3479
    ul16 derived_from_freqA; // 2304000/freqA
3480
    ul16 derived_from_freqB; // 2304000/freqB
3481
    ul16 derived_from_freqC; // 2304000/freqC
3482
    ul16 derived_from_freqD; // 2304000/freqD
3483
  }freqs[15];
3484
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3485
} _2tone;
3486

    
3487
#seekto 0x3000;
3488
struct {
3489
  u8 freq[8];
3490
  char broadcast_station_name[6];
3491
  u8 unknown[2];
3492
} fm_radio_preset[16];
3493

    
3494
#seekto 0x3C90;
3495
struct {
3496
  u8 vhf_low[3];
3497
  u8 vhf_high[3];
3498
  u8 uhf_low[3];
3499
  u8 uhf_high[3];
3500
} ranges;
3501

    
3502
// the UV-2501+220 & KT8900R has different zones for storing ranges
3503

    
3504
#seekto 0x3CD0;
3505
struct {
3506
  u8 vhf_low[3];
3507
  u8 vhf_high[3];
3508
  u8 unknown1[4];
3509
  u8 unknown2[6];
3510
  u8 vhf2_low[3];
3511
  u8 vhf2_high[3];
3512
  u8 unknown3[4];
3513
  u8 unknown4[6];
3514
  u8 uhf_low[3];
3515
  u8 uhf_high[3];
3516
} ranges220;
3517

    
3518
#seekto 0x3F70;
3519
struct {
3520
  char fp[6];
3521
} fingerprint;
3522

    
3523
"""
3524

    
3525

    
3526
class BTech(BTechMobileCommon):
3527
    """BTECH's UV-5001 and alike radios"""
3528
    BANDS = 2
3529
    COLOR_LCD = False
3530
    NAME_LENGTH = 6
3531

    
3532
    def set_options(self):
3533
        """This is to read the options from the image and set it in the
3534
        environment, for now just the limits of the freqs in the VHF/UHF
3535
        ranges"""
3536

    
3537
        # setting the correct ranges for each radio type
3538
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3539
            # the model 2501+220 has a segment in 220
3540
            # and a different position in the memmap
3541
            # also the QYT KT8900R
3542
            ranges = self._memobj.ranges220
3543
        else:
3544
            ranges = self._memobj.ranges
3545

    
3546
        # the normal dual bands
3547
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3548
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3549

    
3550
        # DEBUG
3551
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3552
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3553

    
3554
        # 220Mhz radios case
3555
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3556
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3557
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3558
            self._220_range = vhf2
3559

    
3560
        # set the class with the real data
3561
        self._vhf_range = vhf
3562
        self._uhf_range = uhf
3563

    
3564
    def process_mmap(self):
3565
        """Process the mem map into the mem object"""
3566

    
3567
        # Get it
3568
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3569

    
3570
        # load specific parameters from the radio image
3571
        self.set_options()
3572

    
3573

    
3574
# Declaring Aliases (Clones of the real radios)
3575
class JT2705M(chirp_common.Alias):
3576
    VENDOR = "Jetstream"
3577
    MODEL = "JT2705M"
3578

    
3579

    
3580
class JT6188Mini(chirp_common.Alias):
3581
    VENDOR = "Juentai"
3582
    MODEL = "JT-6188 Mini"
3583

    
3584

    
3585
class JT6188Plus(chirp_common.Alias):
3586
    VENDOR = "Juentai"
3587
    MODEL = "JT-6188 Plus"
3588

    
3589

    
3590
class SSGT890(chirp_common.Alias):
3591
    VENDOR = "Sainsonic"
3592
    MODEL = "GT-890"
3593

    
3594

    
3595
class ZastoneMP300(chirp_common.Alias):
3596
    VENDOR = "Zastone"
3597
    MODEL = "MP-300"
3598

    
3599

    
3600
# real radios
3601
@directory.register
3602
class UV2501(BTech):
3603
    """Baofeng Tech UV2501"""
3604
    MODEL = "UV-2501"
3605
    _fileid = [UV2501G3_fp,
3606
               UV2501G2_fp,
3607
               UV2501pp2_fp,
3608
               UV2501pp_fp]
3609

    
3610

    
3611
@directory.register
3612
class UV2501_220(BTech):
3613
    """Baofeng Tech UV2501+220"""
3614
    MODEL = "UV-2501+220"
3615
    BANDS = 3
3616
    _magic = MSTRING_220
3617
    _fileid = [UV2501_220G3_fp,
3618
               UV2501_220G2_fp,
3619
               UV2501_220_fp,
3620
               UV2501_220pp_fp]
3621

    
3622

    
3623
@directory.register
3624
class UV5001(BTech):
3625
    """Baofeng Tech UV5001"""
3626
    MODEL = "UV-5001"
3627
    _fileid = [UV5001G3_fp,
3628
               UV5001G22_fp,
3629
               UV5001G2_fp,
3630
               UV5001alpha_fp,
3631
               UV5001pp_fp]
3632
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3633
                     chirp_common.PowerLevel("Low", watts=10)]
3634

    
3635

    
3636
@directory.register
3637
class MINI8900(BTech):
3638
    """WACCOM MINI-8900"""
3639
    VENDOR = "WACCOM"
3640
    MODEL = "MINI-8900"
3641
    _magic = MSTRING_MINI8900
3642
    _fileid = [MINI8900_fp, ]
3643
    # Clones
3644
    ALIASES = [JT6188Plus, ]
3645

    
3646

    
3647
@directory.register
3648
class KTUV980(BTech):
3649
    """QYT KT-UV980"""
3650
    VENDOR = "QYT"
3651
    MODEL = "KT-UV980"
3652
    _vhf_range = (136000000, 175000000)
3653
    _uhf_range = (400000000, 481000000)
3654
    _magic = MSTRING_MINI8900
3655
    _fileid = [KTUV980_fp, ]
3656
    # Clones
3657
    ALIASES = [JT2705M, ]
3658

    
3659
# Please note that there is a version of this radios that is a clone of the
3660
# Waccom Mini8900, maybe an early version?
3661

    
3662

    
3663
class OTGRadioV1(chirp_common.Alias):
3664
    VENDOR = 'OTGSTUFF'
3665
    MODEL = 'OTG Radio v1'
3666

    
3667

    
3668
@directory.register
3669
class KT9800(BTech):
3670
    """QYT KT8900"""
3671
    VENDOR = "QYT"
3672
    MODEL = "KT8900"
3673
    _vhf_range = (136000000, 175000000)
3674
    _uhf_range = (400000000, 481000000)
3675
    _magic = MSTRING_KT8900
3676
    _fileid = [KT8900_fp,
3677
               KT8900_fp1,
3678
               KT8900_fp2,
3679
               KT8900_fp3,
3680
               KT8900_fp4,
3681
               KT8900_fp5,
3682
               KT8900_fp6,
3683
               KT8900_fp7]
3684
    # Clones
3685
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3686

    
3687

    
3688
@directory.register
3689
class KT9800R(BTech):
3690
    """QYT KT8900R"""
3691
    VENDOR = "QYT"
3692
    MODEL = "KT8900R"
3693
    BANDS = 3
3694
    _vhf_range = (136000000, 175000000)
3695
    _220_range = (240000000, 271000000)
3696
    _uhf_range = (400000000, 481000000)
3697
    _magic = MSTRING_KT8900R
3698
    _fileid = [KT8900R_fp,
3699
               KT8900R_fp1,
3700
               KT8900R_fp2,
3701
               KT8900R_fp3,
3702
               KT8900R_fp4,
3703
               KT8900R_fp5]
3704

    
3705

    
3706
@directory.register
3707
class LT588UV(BTech):
3708
    """LUITON LT-588UV"""
3709
    VENDOR = "LUITON"
3710
    MODEL = "LT-588UV"
3711
    _vhf_range = (136000000, 175000000)
3712
    _uhf_range = (400000000, 481000000)
3713
    _magic = MSTRING_KT8900
3714
    _fileid = [LT588UV_fp,
3715
               LT588UV_fp1]
3716
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
3717
                     chirp_common.PowerLevel("Low", watts=10)]
3718

    
3719

    
3720
COLOR_MEM_FORMAT = """
3721
#seekto 0x0000;
3722
struct {
3723
  lbcd rxfreq[4];
3724
  lbcd txfreq[4];
3725
  ul16 rxtone;
3726
  ul16 txtone;
3727
  u8 unknown0:4,
3728
     scode:4;
3729
  u8 unknown1:2,
3730
     spmute:2,
3731
     unknown2:2,
3732
     optsig:2;
3733
  u8 unknown3:3,
3734
     scramble:1,
3735
     unknown4:2,
3736
     power:2;
3737
  u8 unknown5:1,
3738
     wide:1,
3739
     unknown6:2,
3740
     bcl:1,
3741
     add:1,
3742
     pttid:2;
3743
} memory[200];
3744

    
3745
#seekto 0x0E00;
3746
struct {
3747
  u8 tmr;
3748
  u8 unknown1;
3749
  u8 sql;
3750
  u8 unknown2;
3751
  u8 mgain2;
3752
  u8 tot;
3753
  u8 apo;
3754
  u8 unknown3;
3755
  u8 abr;
3756
  u8 beep;
3757
  u8 unknown4[4];
3758
  u8 dtmfst;
3759
  u8 unknown5[2];
3760
  u8 screv;
3761
  u8 unknown6[2];
3762
  u8 pttid;
3763
  u8 pttlt;
3764
  u8 unknown7;
3765
  u8 emctp;
3766
  u8 emcch;
3767
  u8 sigbp;
3768
  u8 unknown8;
3769
  u8 camdf;
3770
  u8 cbmdf;
3771
  u8 ccmdf;
3772
  u8 cdmdf;
3773
  u8 langua;        // BTech Gen2 radios have removed the Language setting
3774
                    // use this as the VOX setting
3775
  u8 sync;          // BTech radios use this as the display sync
3776
                    // setting, other radios use this as the auto
3777
                    // keypad lock setting
3778
  u8 mainfc;
3779
  u8 mainbc;
3780
  u8 menufc;
3781
  u8 menubc;
3782
  u8 stafc;
3783
  u8 stabc;
3784
  u8 sigfc;
3785
  u8 sigbc;
3786
  u8 rxfc;
3787
  u8 txfc;
3788
  u8 txdisp;
3789
  u8 unknown9[5];
3790
  u8 anil;
3791
  u8 reps;
3792
  u8 repm;
3793
  u8 tmrmr;
3794
  u8 ste;
3795
  u8 rpste;
3796
  u8 rptdl;
3797
  u8 dtmfg;
3798
  u8 mgain;         // used by db25-g for ponyey
3799
  u8 skiptx;
3800
  u8 scmode;
3801
  u8 tmrtx;
3802
  u8 earpho;
3803
} settings;
3804

    
3805
#seekto 0x0E80;
3806
struct {
3807
  u8 unknown1;
3808
  u8 vfomr;
3809
  u8 keylock;
3810
  u8 unknown2;
3811
  u8 unknown3:4,
3812
     vfomren:1,
3813
     unknown4:1,
3814
     reseten:1,
3815
     menuen:1;
3816
  u8 unknown5[11];
3817
  u8 dispab;
3818
  u8 unknown6[2];
3819
  u8 menu;
3820
  u8 unknown7[7];
3821
  u8 vfomra;
3822
  u8 vfomrb;
3823
  u8 vfomrc;
3824
  u8 vfomrd;
3825
  u8 mrcha;
3826
  u8 mrchb;
3827
  u8 mrchc;
3828
  u8 mrchd;
3829
} settings2;
3830

    
3831
struct settings_vfo {
3832
  u8 freq[8];
3833
  u8 offset[6];
3834
  u8 unknown2[2];
3835
  ul16 rxtone;
3836
  ul16 txtone;
3837
  u8 scode;
3838
  u8 spmute;
3839
  u8 optsig;
3840
  u8 scramble;
3841
  u8 wide;
3842
  u8 power;
3843
  u8 shiftd;
3844
  u8 step;
3845
  u8 unknown3[4];
3846
};
3847

    
3848
#seekto 0x0F00;
3849
struct {
3850
  struct settings_vfo a;
3851
  struct settings_vfo b;
3852
  struct settings_vfo c;
3853
  struct settings_vfo d;
3854
} vfo;
3855

    
3856
#seekto 0x0F80;
3857
struct {
3858
  char line1[8];
3859
  char line2[8];
3860
  char line3[8];
3861
  char line4[8];
3862
  char line5[8];
3863
  char line6[8];
3864
  char line7[8];
3865
  char line8[8];
3866
} poweron_msg;
3867

    
3868
#seekto 0x1000;
3869
struct {
3870
  char name[8];
3871
  u8 unknown1[8];
3872
} names[200];
3873

    
3874
#seekto 0x2400;
3875
struct {
3876
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3877
  u8 group_tone;
3878
  u8 repeat_tone;
3879
  u8 unused[13];
3880
} _5tone_std_settings[15];
3881

    
3882
#seekto 0x2500;
3883
struct {
3884
  u8 frame1[5];
3885
  u8 frame2[5];
3886
  u8 frame3[5];
3887
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3888
} _5tone_codes[15];
3889

    
3890
#seekto 0x25F0;
3891
struct {
3892
  u8 _5tone_delay1; // * 10ms
3893
  u8 _5tone_delay2; // * 10ms
3894
  u8 _5tone_delay3; // * 10ms
3895
  u8 _5tone_first_digit_ext_length;
3896
  u8 unknown1;
3897
  u8 unknown2;
3898
  u8 unknown3;
3899
  u8 unknown4;
3900
  u8 decode_standard;
3901
  u8 unknown5:5,
3902
     _5tone_decode_call_frame3:1,
3903
     _5tone_decode_call_frame2:1,
3904
     _5tone_decode_call_frame1:1;
3905
  u8 unknown6:5,
3906
     _5tone_decode_disp_frame3:1,
3907
     _5tone_decode_disp_frame2:1,
3908
     _5tone_decode_disp_frame1:1;
3909
  u8 decode_reset_time; // * 100 + 100ms
3910
} _5tone_settings;
3911

    
3912
#seekto 0x2900;
3913
struct {
3914
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3915
} dtmf_codes[15];
3916

    
3917
#seekto 0x29F0;
3918
struct {
3919
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3920
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3921
  u8 unknown0[14];
3922
  u8 inspection[16];
3923
  u8 monitor[16];
3924
  u8 alarmcode[16];
3925
  u8 stun[16];
3926
  u8 kill[16];
3927
  u8 revive[16];
3928
  u8 unknown1[16];
3929
  u8 unknown2[16];
3930
  u8 unknown3[16];
3931
  u8 unknown4[16];
3932
  u8 unknown5[16];
3933
  u8 unknown6[16];
3934
  u8 unknown7[16];
3935
  u8 masterid[16];
3936
  u8 viceid[16];
3937
  u8 unused01:7,
3938
     mastervice:1;
3939
  u8 unused02:3,
3940
     mrevive:1,
3941
     mkill:1,
3942
     mstun:1,
3943
     mmonitor:1,
3944
     minspection:1;
3945
  u8 unused03:3,
3946
     vrevive:1,
3947
     vkill:1,
3948
     vstun:1,
3949
     vmonitor:1,
3950
     vinspection:1;
3951
  u8 unused04:6,
3952
     txdisable:1,
3953
     rxdisable:1;
3954
  u8 groupcode;
3955
  u8 spacecode;
3956
  u8 delayproctime; // * 100 + 100ms
3957
  u8 resettime;     // * 100 + 100ms
3958
} dtmf_settings;
3959

    
3960
#seekto 0x2D00;
3961
struct {
3962
  struct {
3963
    ul16 freq1;
3964
    u8 unused01[6];
3965
    ul16 freq2;
3966
    u8 unused02[6];
3967
  } _2tone_encode[15];
3968
  u8 duration_1st_tone; // *10ms
3969
  u8 duration_2nd_tone; // *10ms
3970
  u8 duration_gap;      // *10ms
3971
  u8 unused03[13];
3972
  struct {
3973
    struct {
3974
      u8 dec;      // one out of LIST_2TONE_DEC
3975
      u8 response; // one out of LIST_2TONE_RESPONSE
3976
      u8 alert;    // 1-16
3977
    } decs[4];
3978
    u8 unused04[4];
3979
  } _2tone_decode[15];
3980
  u8 unused05[16];
3981

    
3982
  struct {
3983
    ul16 freqA;
3984
    ul16 freqB;
3985
    ul16 freqC;
3986
    ul16 freqD;
3987
    // unknown what those values mean, but they are
3988
    // derived from configured frequencies
3989
    ul16 derived_from_freqA; // 2304000/freqA
3990
    ul16 derived_from_freqB; // 2304000/freqB
3991
    ul16 derived_from_freqC; // 2304000/freqC
3992
    ul16 derived_from_freqD; // 2304000/freqD
3993
  }freqs[15];
3994
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3995
} _2tone;
3996

    
3997
#seekto 0x3D80;
3998
struct {
3999
  u8 vhf_low[3];
4000
  u8 vhf_high[3];
4001
  u8 unknown1[4];
4002
  u8 unknown2[6];
4003
  u8 vhf2_low[3];
4004
  u8 vhf2_high[3];
4005
  u8 unknown3[4];
4006
  u8 unknown4[6];
4007
  u8 uhf_low[3];
4008
  u8 uhf_high[3];
4009
  u8 unknown5[4];
4010
  u8 unknown6[6];
4011
  u8 uhf2_low[3];
4012
  u8 uhf2_high[3];
4013
} ranges;
4014

    
4015
#seekto 0x3F70;
4016
struct {
4017
  char fp[6];
4018
} fingerprint;
4019

    
4020
"""
4021

    
4022

    
4023
class BTechColor(BTechMobileCommon):
4024
    """BTECH's Color LCD Mobile and alike radios"""
4025
    COLOR_LCD = True
4026
    NAME_LENGTH = 8
4027
    LIST_TMR = LIST_TMR16
4028

    
4029
    def process_mmap(self):
4030
        """Process the mem map into the mem object"""
4031

    
4032
        # Get it
4033
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
4034

    
4035
        # load specific parameters from the radio image
4036
        self.set_options()
4037

    
4038
    def set_options(self):
4039
        """This is to read the options from the image and set it in the
4040
        environment, for now just the limits of the freqs in the VHF/UHF
4041
        ranges"""
4042

    
4043
        # setting the correct ranges for each radio type
4044
        ranges = self._memobj.ranges
4045

    
4046
        # the normal dual bands
4047
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4048
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4049

    
4050
        # DEBUG
4051
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4052
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4053

    
4054
        # the additional bands
4055
        if self.MODEL in ["UV-25X4", "KT7900D"]:
4056
            # 200Mhz band
4057
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4058
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4059
            self._220_range = vhf2
4060

    
4061
            # 350Mhz band
4062
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4063
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4064
            self._350_range = uhf2
4065

    
4066
        # set the class with the real data
4067
        self._vhf_range = vhf
4068
        self._uhf_range = uhf
4069

    
4070

    
4071
# Declaring Aliases (Clones of the real radios)
4072
class SKT8900D(chirp_common.Alias):
4073
    VENDOR = "Surecom"
4074
    MODEL = "S-KT8900D"
4075

    
4076

    
4077
class QB25(chirp_common.Alias):
4078
    VENDOR = "Radioddity"
4079
    MODEL = "QB25"
4080

    
4081

    
4082
class CRT279UV(chirp_common.Alias):
4083
    VENDOR = "CRT"
4084
    MODEL = "CRT-279UV"
4085

    
4086

    
4087
# real radios
4088
@directory.register
4089
class UV25X2(BTechColor):
4090
    """Baofeng Tech UV25X2"""
4091
    MODEL = "UV-25X2"
4092
    BANDS = 2
4093
    _vhf_range = (130000000, 180000000)
4094
    _uhf_range = (400000000, 521000000)
4095
    _magic = MSTRING_UV25X2
4096
    _fileid = [UV25X2_fp, ]
4097

    
4098

    
4099
@directory.register
4100
class UV25X4(BTechColor):
4101
    """Baofeng Tech UV25X4"""
4102
    MODEL = "UV-25X4"
4103
    BANDS = 4
4104
    _vhf_range = (130000000, 180000000)
4105
    _220_range = (200000000, 271000000)
4106
    _uhf_range = (400000000, 521000000)
4107
    _350_range = (350000000, 391000000)
4108
    _magic = MSTRING_UV25X4
4109
    _fileid = [UV25X4_fp, ]
4110

    
4111

    
4112
@directory.register
4113
class UV50X2(BTechColor):
4114
    """Baofeng Tech UV50X2"""
4115
    MODEL = "UV-50X2"
4116
    BANDS = 2
4117
    _vhf_range = (130000000, 180000000)
4118
    _uhf_range = (400000000, 521000000)
4119
    _magic = MSTRING_UV25X2
4120
    _fileid = [UV50X2_fp, ]
4121
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4122
                     chirp_common.PowerLevel("Low", watts=10)]
4123

    
4124

    
4125
@directory.register
4126
class UV50X2_G2(BTechColor):
4127
    """Baofeng Tech UV50X2_G2"""
4128
    MODEL = "UV-50X2_G2"
4129
    BANDS = 2
4130
    _vhf_range = (130000000, 180000000)
4131
    _uhf_range = (400000000, 521000000)
4132
    _magic = MSTRING_UV25X2
4133
    _fileid = [UV50X2_fp, ]
4134
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4135
                     chirp_common.PowerLevel("Low", watts=10)]
4136

    
4137
    @classmethod
4138
    def match_model(cls, filedata, filename):
4139
        # This model is only ever matched via metadata
4140
        return False
4141

    
4142

    
4143
@directory.register
4144
class KT7900D(BTechColor):
4145
    """QYT KT7900D"""
4146
    VENDOR = "QYT"
4147
    MODEL = "KT7900D"
4148
    BANDS = 4
4149
    LIST_TMR = LIST_TMR15
4150
    _vhf_range = (136000000, 175000000)
4151
    _220_range = (200000000, 271000000)
4152
    _uhf_range = (400000000, 481000000)
4153
    _350_range = (350000000, 371000000)
4154
    _magic = MSTRING_KT8900D
4155
    _fileid = [KT7900D_fp, KT7900D_fp1, KT7900D_fp2, KT7900D_fp3, KT7900D_fp4,
4156
               KT7900D_fp5, KT7900D_fp6, KT7900D_fp7, KT7900D_fp8, QB25_fp,
4157
               KT7900D_fp9, ]
4158
    # Clones
4159
    ALIASES = [SKT8900D, QB25, CRT279UV, ]
4160

    
4161

    
4162
@directory.register
4163
class KT8900D(BTechColor):
4164
    """QYT KT8900D"""
4165
    VENDOR = "QYT"
4166
    MODEL = "KT8900D"
4167
    BANDS = 2
4168
    LIST_TMR = LIST_TMR15
4169
    _vhf_range = (136000000, 175000000)
4170
    _uhf_range = (400000000, 481000000)
4171
    _magic = MSTRING_KT8900D
4172
    _fileid = [KT8900D_fp3, KT8900D_fp2, KT8900D_fp1, KT8900D_fp]
4173

    
4174
    # Clones
4175
    ALIASES = [OTGRadioV1]
4176

    
4177

    
4178
@directory.register
4179
class KT5800(BTechColor):
4180
    """QYT KT5800"""
4181
    VENDOR = "QYT"
4182
    MODEL = "KT5800"
4183
    BANDS = 2
4184
    LIST_TMR = LIST_TMR15
4185
    _vhf_range = (136000000, 175000000)
4186
    _uhf_range = (400000000, 481000000)
4187
    _magic = MSTRING_KT8900D
4188
    _fileid = [KT5800_fp, ]
4189

    
4190

    
4191
@directory.register
4192
class KT980PLUS(BTechColor):
4193
    """QYT KT980PLUS"""
4194
    VENDOR = "QYT"
4195
    MODEL = "KT980PLUS"
4196
    BANDS = 2
4197
    LIST_TMR = LIST_TMR15
4198
    _vhf_range = (136000000, 175000000)
4199
    _uhf_range = (400000000, 481000000)
4200
    _magic = MSTRING_KT8900D
4201
    _fileid = [KT980PLUS_fp1, KT980PLUS_fp]
4202
    _power_levels = [chirp_common.PowerLevel("High", watts=75),
4203
                     chirp_common.PowerLevel("Low", watts=55)]
4204

    
4205
    @classmethod
4206
    def match_model(cls, filedata, filename):
4207
        # This model is only ever matched via metadata
4208
        return False
4209

    
4210

    
4211
@directory.register
4212
class DB25G(BTechColor):
4213
    """Radioddity DB25-G"""
4214
    VENDOR = "Radioddity"
4215
    MODEL = "DB25-G"
4216
    BANDS = 2
4217
    LIST_TMR = LIST_TMR15
4218
    _vhf_range = (136000000, 175000000)
4219
    _uhf_range = (400000000, 481000000)
4220
    _magic = MSTRING_KT8900D
4221
    _fileid = [DB25G_fp1, DB25G_fp]
4222
    _gmrs = True
4223
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
4224
                     chirp_common.PowerLevel("Mid", watts=15),
4225
                     chirp_common.PowerLevel("Low", watts=5)]
4226

    
4227
    @classmethod
4228
    def match_model(cls, filedata, filename):
4229
        # This model is only ever matched via metadata
4230
        return False
4231

    
4232

    
4233
GMRS_MEM_FORMAT = """
4234
#seekto 0x0000;
4235
struct {
4236
  lbcd rxfreq[4];
4237
  lbcd txfreq[4];
4238
  ul16 rxtone;
4239
  ul16 txtone;
4240
  u8 unknown0:4,
4241
     scode:4;
4242
  u8 unknown1:2,
4243
     spmute:2,
4244
     unknown2:2,
4245
     optsig:2;
4246
  u8 unknown3:3,
4247
     scramble:1,
4248
     unknown4:2,
4249
     power:2;
4250
  u8 unknown5:1,
4251
     wide:1,
4252
     unknown6:2,
4253
     bcl:1,
4254
     add:1,
4255
     pttid:2;
4256
} memory[256];
4257

    
4258
#seekto 0x1000;
4259
struct {
4260
  char name[7];
4261
  u8 unknown1[9];
4262
} names[256];
4263

    
4264
#seekto 0x2400;
4265
struct {
4266
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4267
  u8 group_tone;
4268
  u8 repeat_tone;
4269
  u8 unused[13];
4270
} _5tone_std_settings[15];
4271

    
4272
#seekto 0x2500;
4273
struct {
4274
  u8 frame1[5];
4275
  u8 frame2[5];
4276
  u8 frame3[5];
4277
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4278
} _5tone_codes[15];
4279

    
4280
#seekto 0x25F0;
4281
struct {
4282
  u8 _5tone_delay1; // * 10ms
4283
  u8 _5tone_delay2; // * 10ms
4284
  u8 _5tone_delay3; // * 10ms
4285
  u8 _5tone_first_digit_ext_length;
4286
  u8 unknown1;
4287
  u8 unknown2;
4288
  u8 unknown3;
4289
  u8 unknown4;
4290
  u8 decode_standard;
4291
  u8 unknown5:5,
4292
     _5tone_decode_call_frame3:1,
4293
     _5tone_decode_call_frame2:1,
4294
     _5tone_decode_call_frame1:1;
4295
  u8 unknown6:5,
4296
     _5tone_decode_disp_frame3:1,
4297
     _5tone_decode_disp_frame2:1,
4298
     _5tone_decode_disp_frame1:1;
4299
  u8 decode_reset_time; // * 100 + 100ms
4300
} _5tone_settings;
4301

    
4302
#seekto 0x2900;
4303
struct {
4304
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4305
} dtmf_codes[15];
4306

    
4307
#seekto 0x29F0;
4308
struct {
4309
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4310
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4311
  u8 unknown0[14];
4312
  u8 inspection[16];
4313
  u8 monitor[16];
4314
  u8 alarmcode[16];
4315
  u8 stun[16];
4316
  u8 kill[16];
4317
  u8 revive[16];
4318
  u8 unknown1[16];
4319
  u8 unknown2[16];
4320
  u8 unknown3[16];
4321
  u8 unknown4[16];
4322
  u8 unknown5[16];
4323
  u8 unknown6[16];
4324
  u8 unknown7[16];
4325
  u8 masterid[16];
4326
  u8 viceid[16];
4327
  u8 unused01:7,
4328
     mastervice:1;
4329
  u8 unused02:3,
4330
     mrevive:1,
4331
     mkill:1,
4332
     mstun:1,
4333
     mmonitor:1,
4334
     minspection:1;
4335
  u8 unused03:3,
4336
     vrevive:1,
4337
     vkill:1,
4338
     vstun:1,
4339
     vmonitor:1,
4340
     vinspection:1;
4341
  u8 unused04:6,
4342
     txdisable:1,
4343
     rxdisable:1;
4344
  u8 groupcode;
4345
  u8 spacecode;
4346
  u8 delayproctime; // * 100 + 100ms
4347
  u8 resettime;     // * 100 + 100ms
4348
} dtmf_settings;
4349

    
4350
#seekto 0x2D00;
4351
struct {
4352
  struct {
4353
    ul16 freq1;
4354
    u8 unused01[6];
4355
    ul16 freq2;
4356
    u8 unused02[6];
4357
  } _2tone_encode[15];
4358
  u8 duration_1st_tone; // *10ms
4359
  u8 duration_2nd_tone; // *10ms
4360
  u8 duration_gap;      // *10ms
4361
  u8 unused03[13];
4362
  struct {
4363
    struct {
4364
      u8 dec;      // one out of LIST_2TONE_DEC
4365
      u8 response; // one out of LIST_2TONE_RESPONSE
4366
      u8 alert;    // 1-16
4367
    } decs[4];
4368
    u8 unused04[4];
4369
  } _2tone_decode[15];
4370
  u8 unused05[16];
4371

    
4372
  struct {
4373
    ul16 freqA;
4374
    ul16 freqB;
4375
    ul16 freqC;
4376
    ul16 freqD;
4377
    // unknown what those values mean, but they are
4378
    // derived from configured frequencies
4379
    ul16 derived_from_freqA; // 2304000/freqA
4380
    ul16 derived_from_freqB; // 2304000/freqB
4381
    ul16 derived_from_freqC; // 2304000/freqC
4382
    ul16 derived_from_freqD; // 2304000/freqD
4383
  }freqs[15];
4384
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4385
} _2tone;
4386

    
4387
#seekto 0x3000;
4388
struct {
4389
  u8 freq[8];
4390
  char broadcast_station_name[6];
4391
  u8 unknown[2];
4392
} fm_radio_preset[16];
4393

    
4394
#seekto 0x3200;
4395
struct {
4396
  u8 tmr;
4397
  u8 unknown1;
4398
  u8 sql;
4399
  u8 unknown2;
4400
  u8 autolk;
4401
  u8 tot;
4402
  u8 apo;
4403
  u8 unknown3;
4404
  u8 abr;
4405
  u8 beep;
4406
  u8 unknown4[4];
4407
  u8 dtmfst;
4408
  u8 unknown5[2];
4409
  u8 screv;
4410
  u8 unknown6[2];
4411
  u8 pttid;
4412
  u8 pttlt;
4413
  u8 unknown7;
4414
  u8 emctp;
4415
  u8 emcch;
4416
  u8 sigbp;
4417
  u8 unknown8;
4418
  u8 camdf;
4419
  u8 cbmdf;
4420
  u8 ccmdf;
4421
  u8 cdmdf;
4422
  u8 langua;
4423
  u8 sync;
4424

    
4425

    
4426
  u8 stfc;
4427
  u8 mffc;
4428
  u8 sfafc;
4429
  u8 sfbfc;
4430
  u8 sfcfc;
4431
  u8 sfdfc;
4432
  u8 subfc;
4433
  u8 fmfc;
4434
  u8 sigfc;
4435
  u8 modfc;
4436
  u8 menufc;
4437
  u8 txfc;
4438
  u8 txdisp;
4439
  u8 unknown9[5];
4440
  u8 anil;
4441
  u8 reps;
4442
  u8 repm;
4443
  u8 tmrmr;
4444
  u8 ste;
4445
  u8 rpste;
4446
  u8 rptdl;
4447
  u8 dtmfg;
4448
  u8 mgain;
4449
  u8 skiptx;
4450
  u8 scmode;
4451
  u8 tmrtx;
4452
} settings;
4453

    
4454
#seekto 0x3280;
4455
struct {
4456
  u8 unknown1;
4457
  u8 vfomr;
4458
  u8 keylock;
4459
  u8 unknown2;
4460
  u8 unknown3:4,
4461
     vfomren:1,
4462
     unknown4:1,
4463
     reseten:1,
4464
     menuen:1;
4465
  u8 unknown5[11];
4466
  u8 dispab;
4467
  u8 unknown6[2];
4468
  u8 smenu;
4469
  u8 unknown7[7];
4470
  u8 vfomra;
4471
  u8 vfomrb;
4472
  u8 vfomrc;
4473
  u8 vfomrd;
4474
  u8 mrcha;
4475
  u8 mrchb;
4476
  u8 mrchc;
4477
  u8 mrchd;
4478
} settings2;
4479

    
4480
struct settings_vfo {
4481
  u8 freq[8];
4482
  u8 offset[6];
4483
  u8 unknown2[2];
4484
  ul16 rxtone;
4485
  ul16 txtone;
4486
  u8 scode;
4487
  u8 spmute;
4488
  u8 optsig;
4489
  u8 scramble;
4490
  u8 wide;
4491
  u8 power;
4492
  u8 shiftd;
4493
  u8 step;
4494
  u8 unknown3[4];
4495
};
4496

    
4497
#seekto 0x3300;
4498
struct {
4499
  struct settings_vfo a;
4500
  struct settings_vfo b;
4501
  struct settings_vfo c;
4502
  struct settings_vfo d;
4503
} vfo;
4504

    
4505
#seekto 0x3D80;
4506
struct {
4507
  u8 vhf_low[3];
4508
  u8 vhf_high[3];
4509
  u8 unknown1[4];
4510
  u8 unknown2[6];
4511
  u8 vhf2_low[3];
4512
  u8 vhf2_high[3];
4513
  u8 unknown3[4];
4514
  u8 unknown4[6];
4515
  u8 uhf_low[3];
4516
  u8 uhf_high[3];
4517
  u8 unknown5[4];
4518
  u8 unknown6[6];
4519
  u8 uhf2_low[3];
4520
  u8 uhf2_high[3];
4521
} ranges;
4522

    
4523
#seekto 0x33B0;
4524
struct {
4525
  char line[16];
4526
} static_msg;
4527

    
4528
#seekto 0x3F70;
4529
struct {
4530
  char fp[6];
4531
} fingerprint;
4532

    
4533
"""
4534

    
4535

    
4536
class BTechGMRS(BTechMobileCommon):
4537
    """BTECH's GMRS Mobile"""
4538
    COLOR_LCD = True
4539
    COLOR_LCD2 = True
4540
    NAME_LENGTH = 7
4541
    UPLOAD_MEM_SIZE = 0X3400
4542

    
4543
    def process_mmap(self):
4544
        """Process the mem map into the mem object"""
4545

    
4546
        # Get it
4547
        self._memobj = bitwise.parse(GMRS_MEM_FORMAT, self._mmap)
4548

    
4549
        # load specific parameters from the radio image
4550
        self.set_options()
4551

    
4552
    def set_options(self):
4553
        """This is to read the options from the image and set it in the
4554
        environment, for now just the limits of the freqs in the VHF/UHF
4555
        ranges"""
4556

    
4557
        # setting the correct ranges for each radio type
4558
        ranges = self._memobj.ranges
4559

    
4560
        # the normal dual bands
4561
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4562
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4563

    
4564
        # DEBUG
4565
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4566
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4567

    
4568
        # set the class with the real data
4569
        self._vhf_range = vhf
4570
        self._uhf_range = uhf
4571

    
4572

    
4573
# real radios
4574
@directory.register
4575
class GMRS50X1(BTechGMRS):
4576
    """Baofeng Tech GMRS50X1"""
4577
    MODEL = "GMRS-50X1"
4578
    BANDS = 2
4579
    LIST_TMR = LIST_TMR16
4580
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4581
                     chirp_common.PowerLevel("Mid", watts=10),
4582
                     chirp_common.PowerLevel("Low", watts=5)]
4583
    _vhf_range = (136000000, 175000000)
4584
    _uhf_range = (400000000, 521000000)
4585
    _upper = 255
4586
    _magic = MSTRING_GMRS50X1
4587
    _fileid = [GMRS50X1_fp1, GMRS50X1_fp, ]
4588
    _gmrs = True
4589

    
4590

    
4591
COLORHT_MEM_FORMAT = """
4592
#seekto 0x0000;
4593
struct {
4594
  lbcd rxfreq[4];
4595
  lbcd txfreq[4];
4596
  ul16 rxtone;
4597
  ul16 txtone;
4598
  u8 unknown0:4,
4599
     scode:4;
4600
  u8 unknown1:2,
4601
     spmute:2,
4602
     unknown2:2,
4603
     optsig:2;
4604
  u8 unknown3:3,
4605
     scramble:1,
4606
     unknown4:3,
4607
     power:1;
4608
  u8 unknown5:1,
4609
     wide:1,
4610
     unknown6:2,
4611
     bcl:1,
4612
     add:1,
4613
     pttid:2;
4614
} memory[200];
4615

    
4616
#seekto 0x0E00;
4617
struct {
4618
  u8 tmr;
4619
  u8 unknownE01;
4620
  u8 sql;
4621
  u8 unknownE03[2];
4622
  u8 tot;
4623
  u8 save;
4624
  u8 unknownE07;
4625
  u8 abr;
4626
  u8 beep;
4627
  u8 unknownE0A[4];
4628
  u8 dsub;
4629
  u8 dtmfst;
4630
  u8 screv;
4631
  u8 unknownE11[3];
4632
  u8 pttid;
4633
  u8 unknownE15;
4634
  u8 pttlt;
4635
  u8 unknownE17;
4636
  u8 emctp;
4637
  u8 emcch;
4638
  u8 sigbp;
4639
  u8 unknownE1B;
4640
  u8 camdf;
4641
  u8 cbmdf;
4642
  u8 ccmdf;
4643
  u8 cdmdf;
4644
  u8 langua;
4645
  u8 voice;
4646
  u8 vox;
4647
  u8 voxt;
4648
  u8 sync;          // BTech radios use this as the display sync setting
4649
                    // other radios use this as the auto keypad lock setting
4650
  u8 stfc;
4651
  u8 mffc;
4652
  u8 sfafc;
4653
  u8 sfbfc;
4654
  u8 sfcfc;
4655
  u8 sfdfc;
4656
  u8 subfc;
4657
  u8 fmfc;
4658
  u8 sigfc;
4659
  u8 menufc;
4660
  u8 txfc;
4661
  u8 rxfc;
4662
  u8 unknownE31[5];
4663
  u8 anil;
4664
  u8 reps;
4665
  u8 tmrmr;
4666
  u8 ste;
4667
  u8 rpste;
4668
  u8 rptdl;
4669
  u8 dtmfg;
4670
  u8 tmrtx;
4671
} settings;
4672

    
4673
#seekto 0x0E80;
4674
struct {
4675
  u8 unknown1;
4676
  u8 vfomr;
4677
  u8 keylock;
4678
  u8 unknown2;
4679
  u8 unknown3:4,
4680
     vfomren:1,
4681
     unknown4:1,
4682
     reseten:1,
4683
     menuen:1;
4684
  u8 unknown5[11];
4685
  u8 dispab;
4686
  u8 unknown6[2];
4687
  u8 menu;
4688
  u8 unknown7[7];
4689
  u8 vfomra;
4690
  u8 vfomrb;
4691
  u8 vfomrc;
4692
  u8 vfomrd;
4693
  u8 mrcha;
4694
  u8 mrchb;
4695
  u8 mrchc;
4696
  u8 mrchd;
4697
} settings2;
4698

    
4699
struct settings_vfo {
4700
  u8 freq[8];
4701
  u8 offset[6];
4702
  u8 unknown2[2];
4703
  ul16 rxtone;
4704
  ul16 txtone;
4705
  u8 scode;
4706
  u8 spmute;
4707
  u8 optsig;
4708
  u8 scramble;
4709
  u8 wide;
4710
  u8 power;
4711
  u8 shiftd;
4712
  u8 step;
4713
  u8 unknown3[4];
4714
};
4715

    
4716
#seekto 0x0F00;
4717
struct {
4718
  struct settings_vfo a;
4719
  struct settings_vfo b;
4720
  struct settings_vfo c;
4721
  struct settings_vfo d;
4722
} vfo;
4723

    
4724
#seekto 0x0FE0;
4725
struct {
4726
  char line[16];
4727
} static_msg;
4728

    
4729
#seekto 0x1000;
4730
struct {
4731
  char name[8];
4732
  u8 unknown1[8];
4733
} names[200];
4734

    
4735
#seekto 0x2400;
4736
struct {
4737
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4738
  u8 group_tone;
4739
  u8 repeat_tone;
4740
  u8 unused[13];
4741
} _5tone_std_settings[15];
4742

    
4743
#seekto 0x2500;
4744
struct {
4745
  u8 frame1[5];
4746
  u8 frame2[5];
4747
  u8 frame3[5];
4748
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4749
} _5tone_codes[15];
4750

    
4751
#seekto 0x25F0;
4752
struct {
4753
  u8 _5tone_delay1; // * 10ms
4754
  u8 _5tone_delay2; // * 10ms
4755
  u8 _5tone_delay3; // * 10ms
4756
  u8 _5tone_first_digit_ext_length;
4757
  u8 unknown1;
4758
  u8 unknown2;
4759
  u8 unknown3;
4760
  u8 unknown4;
4761
  u8 decode_standard;
4762
  u8 unknown5:5,
4763
     _5tone_decode_call_frame3:1,
4764
     _5tone_decode_call_frame2:1,
4765
     _5tone_decode_call_frame1:1;
4766
  u8 unknown6:5,
4767
     _5tone_decode_disp_frame3:1,
4768
     _5tone_decode_disp_frame2:1,
4769
     _5tone_decode_disp_frame1:1;
4770
  u8 decode_reset_time; // * 100 + 100ms
4771
} _5tone_settings;
4772

    
4773
#seekto 0x2900;
4774
struct {
4775
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4776
} dtmf_codes[15];
4777

    
4778
#seekto 0x29F0;
4779
struct {
4780
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4781
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4782
  u8 unknown0[14];
4783
  u8 inspection[16];
4784
  u8 monitor[16];
4785
  u8 alarmcode[16];
4786
  u8 stun[16];
4787
  u8 kill[16];
4788
  u8 revive[16];
4789
  u8 unknown1[16];
4790
  u8 unknown2[16];
4791
  u8 unknown3[16];
4792
  u8 unknown4[16];
4793
  u8 unknown5[16];
4794
  u8 unknown6[16];
4795
  u8 unknown7[16];
4796
  u8 masterid[16];
4797
  u8 viceid[16];
4798
  u8 unused01:7,
4799
     mastervice:1;
4800
  u8 unused02:3,
4801
     mrevive:1,
4802
     mkill:1,
4803
     mstun:1,
4804
     mmonitor:1,
4805
     minspection:1;
4806
  u8 unused03:3,
4807
     vrevive:1,
4808
     vkill:1,
4809
     vstun:1,
4810
     vmonitor:1,
4811
     vinspection:1;
4812
  u8 unused04:6,
4813
     txdisable:1,
4814
     rxdisable:1;
4815
  u8 groupcode;
4816
  u8 spacecode;
4817
  u8 delayproctime; // * 100 + 100ms
4818
  u8 resettime;     // * 100 + 100ms
4819
} dtmf_settings;
4820

    
4821
#seekto 0x2D00;
4822
struct {
4823
  struct {
4824
    ul16 freq1;
4825
    u8 unused01[6];
4826
    ul16 freq2;
4827
    u8 unused02[6];
4828
  } _2tone_encode[15];
4829
  u8 duration_1st_tone; // *10ms
4830
  u8 duration_2nd_tone; // *10ms
4831
  u8 duration_gap;      // *10ms
4832
  u8 unused03[13];
4833
  struct {
4834
    struct {
4835
      u8 dec;      // one out of LIST_2TONE_DEC
4836
      u8 response; // one out of LIST_2TONE_RESPONSE
4837
      u8 alert;    // 1-16
4838
    } decs[4];
4839
    u8 unused04[4];
4840
  } _2tone_decode[15];
4841
  u8 unused05[16];
4842

    
4843
  struct {
4844
    ul16 freqA;
4845
    ul16 freqB;
4846
    ul16 freqC;
4847
    ul16 freqD;
4848
    // unknown what those values mean, but they are
4849
    // derived from configured frequencies
4850
    ul16 derived_from_freqA; // 2304000/freqA
4851
    ul16 derived_from_freqB; // 2304000/freqB
4852
    ul16 derived_from_freqC; // 2304000/freqC
4853
    ul16 derived_from_freqD; // 2304000/freqD
4854
  }freqs[15];
4855
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4856
} _2tone;
4857

    
4858
#seekto 0x3D80;
4859
struct {
4860
  u8 vhf_low[3];
4861
  u8 vhf_high[3];
4862
  u8 unknown1[4];
4863
  u8 unknown2[6];
4864
  u8 vhf2_low[3];
4865
  u8 vhf2_high[3];
4866
  u8 unknown3[4];
4867
  u8 unknown4[6];
4868
  u8 uhf_low[3];
4869
  u8 uhf_high[3];
4870
  u8 unknown5[4];
4871
  u8 unknown6[6];
4872
  u8 uhf2_low[3];
4873
  u8 uhf2_high[3];
4874
} ranges;
4875

    
4876
#seekto 0x3F70;
4877
struct {
4878
  char fp[6];
4879
} fingerprint;
4880

    
4881
"""
4882

    
4883

    
4884
class QYTColorHT(BTechMobileCommon):
4885
    """QTY's Color LCD Handheld and alike radios"""
4886
    COLOR_LCD = True
4887
    COLOR_LCD3 = True
4888
    NAME_LENGTH = 8
4889
    LIST_TMR = LIST_TMR15
4890

    
4891
    def process_mmap(self):
4892
        """Process the mem map into the mem object"""
4893

    
4894
        # Get it
4895
        self._memobj = bitwise.parse(COLORHT_MEM_FORMAT, self._mmap)
4896

    
4897
        # load specific parameters from the radio image
4898
        self.set_options()
4899

    
4900
    def set_options(self):
4901
        """This is to read the options from the image and set it in the
4902
        environment, for now just the limits of the freqs in the VHF/UHF
4903
        ranges"""
4904

    
4905
        # setting the correct ranges for each radio type
4906
        ranges = self._memobj.ranges
4907

    
4908
        # the normal dual bands
4909
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4910
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4911

    
4912
        # DEBUG
4913
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4914
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4915

    
4916
        # the additional bands
4917
        if self.MODEL in ["KT-8R"]:
4918
            # 200Mhz band
4919
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4920
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4921
            self._220_range = vhf2
4922

    
4923
            # 350Mhz band
4924
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4925
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4926
            self._350_range = uhf2
4927

    
4928
        # set the class with the real data
4929
        self._vhf_range = vhf
4930
        self._uhf_range = uhf
4931

    
4932

    
4933
# real radios
4934
@directory.register
4935
class KT8R(QYTColorHT):
4936
    """QYT KT8R"""
4937
    VENDOR = "QYT"
4938
    MODEL = "KT-8R"
4939
    BANDS = 4
4940
    LIST_TMR = LIST_TMR16
4941
    _vhf_range = (136000000, 175000000)
4942
    _220_range = (200000000, 261000000)
4943
    _uhf_range = (400000000, 481000000)
4944
    _350_range = (350000000, 391000000)
4945
    _magic = MSTRING_KT8R
4946
    _fileid = [KT8R_fp2, KT8R_fp1, KT8R_fp, ]
4947
    _power_levels = [chirp_common.PowerLevel("High", watts=5),
4948
                     chirp_common.PowerLevel("Low", watts=1)]
4949

    
4950

    
4951
COLOR9900_MEM_FORMAT = """
4952
#seekto 0x0000;
4953
struct {
4954
  lbcd rxfreq[4];
4955
  lbcd txfreq[4];
4956
  ul16 rxtone;
4957
  ul16 txtone;
4958
  u8 unknown0:4,
4959
     scode:4;
4960
  u8 unknown1:2,
4961
     spmute:2,
4962
     unknown2:2,
4963
     optsig:2;
4964
  u8 unknown3:3,
4965
     scramble:1,
4966
     unknown4:2,
4967
     power:2;
4968
  u8 unknown5:1,
4969
     wide:1,
4970
     unknown6:2,
4971
     bcl:1,
4972
     add:1,
4973
     pttid:2;
4974
} memory[200];
4975

    
4976
#seekto 0x0E00;
4977
struct {
4978
  u8 tmr;
4979
  u8 unknown1;
4980
  u8 sql;
4981
  u8 unknown2[2];
4982
  u8 tot;
4983
  u8 volume;
4984
  u8 unknown3;
4985
  u8 abr;
4986
  u8 beep;
4987
  u8 unknown4[4];
4988
  u8 dsub;
4989
  u8 dtmfst;
4990
  u8 unknown_e10;
4991
  u8 unknown_e11;
4992
  u8 screv;
4993
  u8 unknown_e13;
4994
  u8 unknown_e14;
4995
  u8 pttid;
4996
  u8 pttlt;
4997
  u8 unknown7;
4998
  u8 emctp;
4999
  u8 emcch;
5000
  u8 sigbp;
5001
  u8 unknown8;
5002
  u8 camdf;
5003
  u8 cbmdf;
5004
  u8 ccmdf;
5005
  u8 language;
5006
  u8 tmrtx;
5007
  u8 vox;
5008
  u8 voxt;
5009
  u8 autolock;
5010
  u8 asfc;
5011
  u8 mainfc;
5012
  u8 a_fc;
5013
  u8 b_fc;
5014
  u8 c_fc;
5015
  u8 subfc;
5016
  u8 battfc;
5017
  u8 sigfc;
5018
  u8 menufc;
5019
  u8 txfc;
5020
  u8 rxfc;
5021
  u8 unknown_e2f;
5022
  u8 unknown_e30;
5023
  u8 unknown9[3];
5024
  u8 anil;
5025
  u8 reps;
5026
  u8 tmrmr;
5027
  u8 ste;
5028
  u8 rpste;
5029
  u8 rptdl;
5030
  u8 dtmfg;
5031
} settings;
5032

    
5033
#seekto 0x0E80;
5034
struct {
5035
  u8 unknown1;
5036
  u8 vfomr;
5037
  u8 keylock;
5038
  u8 unknown2;
5039
  u8 unknown3:4,
5040
     vfomren:1,
5041
     unknown4:1,
5042
     reseten:1,
5043
     menuen:1;
5044
  u8 unknown5[11];
5045
  u8 dispab;
5046
  u8 unknown6[2];
5047
  u8 menu;
5048
  u8 unknown7[7];
5049
  u8 vfomra;
5050
  u8 vfomrb;
5051
  u8 vfomrc;
5052
  u8 vfomrd;
5053
  u8 mrcha;
5054
  u8 mrchb;
5055
  u8 mrchc;
5056
  u8 mrchd;
5057
} settings2;
5058

    
5059
struct settings_vfo {
5060
  u8 freq[8];
5061
  u8 offset[6];
5062
  u8 unknown2[2];
5063
  ul16 rxtone;
5064
  ul16 txtone;
5065
  u8 scode;
5066
  u8 spmute;
5067
  u8 optsig;
5068
  u8 scramble;
5069
  u8 wide;
5070
  u8 power;
5071
  u8 shiftd;
5072
  u8 step;
5073
  u8 unknown3[4];
5074
};
5075

    
5076
#seekto 0x0F00;
5077
struct {
5078
  struct settings_vfo a;
5079
  struct settings_vfo b;
5080
  struct settings_vfo c;
5081
  struct settings_vfo d;
5082
} vfo;
5083

    
5084
#seekto 0x0F80;
5085
struct {
5086
  char line1[8];
5087
  char line2[8];
5088
  char line3[8];
5089
  char line4[8];
5090
  char line5[8];
5091
  char line6[8];
5092
  char line7[8];
5093
  char line8[8];
5094
} poweron_msg;
5095

    
5096
#seekto 0x0FE0;
5097
struct {
5098
  char line[16];
5099
} static_msg;
5100

    
5101
#seekto 0x1000;
5102
struct {
5103
  char name[7];
5104
  u8 unknown1[9];
5105
} names[200];
5106

    
5107
#seekto 0x2400;
5108
struct {
5109
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5110
  u8 group_tone;
5111
  u8 repeat_tone;
5112
  u8 unused[13];
5113
} _5tone_std_settings[15];
5114

    
5115
#seekto 0x2500;
5116
struct {
5117
  u8 frame1[5];
5118
  u8 frame2[5];
5119
  u8 frame3[5];
5120
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5121
} _5tone_codes[15];
5122

    
5123
#seekto 0x25F0;
5124
struct {
5125
  u8 _5tone_delay1; // * 10ms
5126
  u8 _5tone_delay2; // * 10ms
5127
  u8 _5tone_delay3; // * 10ms
5128
  u8 _5tone_first_digit_ext_length;
5129
  u8 unknown1;
5130
  u8 unknown2;
5131
  u8 unknown3;
5132
  u8 unknown4;
5133
  u8 decode_standard;
5134
  u8 unknown5:5,
5135
     _5tone_decode_call_frame3:1,
5136
     _5tone_decode_call_frame2:1,
5137
     _5tone_decode_call_frame1:1;
5138
  u8 unknown6:5,
5139
     _5tone_decode_disp_frame3:1,
5140
     _5tone_decode_disp_frame2:1,
5141
     _5tone_decode_disp_frame1:1;
5142
  u8 decode_reset_time; // * 100 + 100ms
5143
} _5tone_settings;
5144

    
5145
#seekto 0x2900;
5146
struct {
5147
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5148
} dtmf_codes[15];
5149

    
5150
#seekto 0x29F0;
5151
struct {
5152
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10      // 9f0
5153
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10      // 9f1
5154
  u8 unknown0[14];                                             // 9f2-9ff
5155
  u8 inspection[16];                                           // a00-a0f
5156
  u8 monitor[16];                                              // a10-a1f
5157
  u8 alarmcode[16];                                            // a20-a2f
5158
  u8 stun[16];                                                 // a30-a3f
5159
  u8 kill[16];                                                 // a40-a4f
5160
  u8 revive[16];                                               // a50-a5f
5161
  u8 unknown1[16];                                             // a60-a6f
5162
  u8 unknown2[16];                                             // a70-a7f
5163
  u8 unknown3[16];                                             // a80-a8f
5164
  u8 unknown4[16];                                             // a90-a9f
5165
  u8 unknown5[16];                                             // aa0-aaf
5166
  u8 unknown6[16];                                             // ab0-abf
5167
  u8 unknown7[16];                                             // ac0-acf
5168
  u8 masterid[16];                                             // ad0-adf
5169
  u8 viceid[16];                                               // ae0-aef
5170
  u8 unused01:7,                                               // af0
5171
     mastervice:1;
5172
  u8 unused02:3,                                               // af1
5173
     mrevive:1,
5174
     mkill:1,
5175
     mstun:1,
5176
     mmonitor:1,
5177
     minspection:1;
5178
  u8 unused03:3,                                               // af2
5179
     vrevive:1,
5180
     vkill:1,
5181
     vstun:1,
5182
     vmonitor:1,
5183
     vinspection:1;
5184
  u8 unused04:6,                                               // af3
5185
     txdisable:1,
5186
     rxdisable:1;
5187
  u8 groupcode;                                                // af4
5188
  u8 spacecode;                                                // af5
5189
  u8 delayproctime; // * 100 + 100ms                           // af6
5190
  u8 resettime;     // * 100 + 100ms                           // af7
5191
} dtmf_settings;
5192

    
5193
#seekto 0x2D00;
5194
struct {
5195
  struct {
5196
    ul16 freq1;
5197
    u8 unused01[6];
5198
    ul16 freq2;
5199
    u8 unused02[6];
5200
  } _2tone_encode[15];
5201
  u8 duration_1st_tone; // *10ms
5202
  u8 duration_2nd_tone; // *10ms
5203
  u8 duration_gap;      // *10ms
5204
  u8 unused03[13];
5205
  struct {
5206
    struct {
5207
      u8 dec;      // one out of LIST_2TONE_DEC
5208
      u8 response; // one out of LIST_2TONE_RESPONSE
5209
      u8 alert;    // 1-16
5210
    } decs[4];
5211
    u8 unused04[4];
5212
  } _2tone_decode[15];
5213
  u8 unused05[16];
5214

    
5215
  struct {
5216
    ul16 freqA;
5217
    ul16 freqB;
5218
    ul16 freqC;
5219
    ul16 freqD;
5220
    // unknown what those values mean, but they are
5221
    // derived from configured frequencies
5222
    ul16 derived_from_freqA; // 2304000/freqA
5223
    ul16 derived_from_freqB; // 2304000/freqB
5224
    ul16 derived_from_freqC; // 2304000/freqC
5225
    ul16 derived_from_freqD; // 2304000/freqD
5226
  }freqs[15];
5227
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
5228
} _2tone;
5229

    
5230
#seekto 0x3D80;
5231
struct {
5232
  u8 vhf_low[3];
5233
  u8 vhf_high[3];
5234
  u8 unknown1[4];
5235
  u8 unknown2[6];
5236
  u8 vhf2_low[3];
5237
  u8 vhf2_high[3];
5238
  u8 unknown3[4];
5239
  u8 unknown4[6];
5240
  u8 uhf_low[3];
5241
  u8 uhf_high[3];
5242
  u8 unknown5[4];
5243
  u8 unknown6[6];
5244
  u8 uhf2_low[3];
5245
  u8 uhf2_high[3];
5246
} ranges;
5247

    
5248
#seekto 0x3F70;
5249
struct {
5250
  char fp[6];
5251
} fingerprint;
5252

    
5253
"""
5254

    
5255

    
5256
COLOR20V2_MEM_FORMAT = """
5257
#seekto 0x0000;
5258
struct {
5259
  lbcd rxfreq[4];
5260
  lbcd txfreq[4];
5261
  ul16 rxtone;
5262
  ul16 txtone;
5263
  u8 unknown0:4,
5264
     scode:4;
5265
  u8 unknown1:2,
5266
     spmute:2,
5267
     unknown2:2,
5268
     optsig:2;
5269
  u8 unknown3:3,
5270
     scramble:1,
5271
     unknown4:2,
5272
     power:2;
5273
  u8 unknown5:1,
5274
     wide:1,
5275
     unknown6:2,
5276
     bcl:1,
5277
     add:1,
5278
     pttid:2;
5279
} memory[200];
5280

    
5281
#seekto 0x0E00;
5282
struct {
5283
  u8 tmr;
5284
  u8 unknown1;
5285
  u8 sql;
5286
  u8 unknown2;
5287
  u8 autolock;
5288
  u8 tot;
5289
  u8 apo;
5290
  u8 unknown3;
5291
  u8 abr;
5292
  u8 beep;
5293
  u8 unknown4[4];
5294
  u8 dtmfst;
5295
  u8 unknown5[2];
5296
  u8 screv;
5297
  u8 unknown6[2];
5298
  u8 pttid;
5299
  u8 pttlt;
5300
  u8 unknown7;
5301
  u8 emctp;
5302
  u8 emcch;
5303
  u8 sigbp;
5304
  u8 unknown8;
5305
  u8 camdf;
5306
  u8 cbmdf;
5307
  u8 ccmdf;
5308
  u8 vox;
5309
  u8 voxt;
5310
  u8 sync;
5311
  u8 asfc;
5312
  u8 mainfc;
5313
  u8 a_fc;
5314
  u8 b_fc;
5315
  u8 c_fc;
5316
  u8 subfc;
5317
  u8 battfc;
5318
  u8 sigfc;
5319
  u8 menufc;
5320
  u8 txfc;
5321
  u8 rxfc;
5322
  u8 repsw;
5323
  u8 dsub;
5324
  u8 unknown9[5];
5325
  u8 anil;
5326
  u8 reps;
5327
  u8 repm;
5328
  u8 tmrmr;
5329
  u8 ste;
5330
  u8 rpste;
5331
  u8 rptdl;
5332
  u8 dtmfg;
5333
  u8 mgain;
5334
  u8 skiptx;
5335
  u8 scmode;
5336
  u8 tmrtx;
5337
  u8 volume;
5338
  u8 unknown_10;
5339
  u8 save;
5340
} settings;
5341

    
5342
#seekto 0x0E80;
5343
struct {
5344
  u8 unknown1;
5345
  u8 vfomr;
5346
  u8 keylock;
5347
  u8 unknown2;
5348
  u8 unknown3:4,
5349
     vfomren:1,
5350
     unknown4:1,
5351
     reseten:1,
5352
     menuen:1;
5353
  u8 unknown5[11];
5354
  u8 dispab;
5355
  u8 unknown6[2];
5356
  u8 menu;
5357
  u8 unknown7[7];
5358
  u8 vfomra;
5359
  u8 vfomrb;
5360
  u8 vfomrc;
5361
  u8 vfomrd;
5362
  u8 mrcha;
5363
  u8 mrchb;
5364
  u8 mrchc;
5365
  u8 mrchd;
5366
} settings2;
5367

    
5368
struct settings_vfo {
5369
  u8 freq[8];
5370
  u8 offset[6];
5371
  u8 unknown2[2];
5372
  ul16 rxtone;
5373
  ul16 txtone;
5374
  u8 scode;
5375
  u8 spmute;
5376
  u8 optsig;
5377
  u8 scramble;
5378
  u8 wide;
5379
  u8 power;
5380
  u8 shiftd;
5381
  u8 step;
5382
  u8 unknown3[4];
5383
};
5384

    
5385
#seekto 0x0F00;
5386
struct {
5387
  struct settings_vfo a;
5388
  struct settings_vfo b;
5389
  struct settings_vfo c;
5390
  struct settings_vfo d;
5391
} vfo;
5392

    
5393
#seekto 0x0F80;
5394
struct {
5395
  char line1[8];
5396
  char line2[8];
5397
  char line3[8];
5398
  char line4[8];
5399
  char line5[8];
5400
  char line6[8];
5401
  char line7[8];
5402
  char line8[8];
5403
} poweron_msg;
5404

    
5405
#seekto 0x0FE0;
5406
struct {
5407
  char line[16];
5408
} static_msg;
5409

    
5410
#seekto 0x1000;
5411
struct {
5412
  char name[7];
5413
  u8 unknown1[9];
5414
} names[200];
5415

    
5416
#seekto 0x2400;
5417
struct {
5418
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5419
  u8 group_tone;
5420
  u8 repeat_tone;
5421
  u8 unused[13];
5422
} _5tone_std_settings[15];
5423

    
5424
#seekto 0x2500;
5425
struct {
5426
  u8 frame1[5];
5427
  u8 frame2[5];
5428
  u8 frame3[5];
5429
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5430
} _5tone_codes[15];
5431

    
5432
#seekto 0x25F0;
5433
struct {
5434
  u8 _5tone_delay1; // * 10ms
5435
  u8 _5tone_delay2; // * 10ms
5436
  u8 _5tone_delay3; // * 10ms
5437
  u8 _5tone_first_digit_ext_length;
5438
  u8 unknown1;
5439
  u8 unknown2;
5440
  u8 unknown3;
5441
  u8 unknown4;
5442
  u8 decode_standard;
5443
  u8 unknown5:5,
5444
     _5tone_decode_call_frame3:1,
5445
     _5tone_decode_call_frame2:1,
5446
     _5tone_decode_call_frame1:1;
5447
  u8 unknown6:5,
5448
     _5tone_decode_disp_frame3:1,
5449
     _5tone_decode_disp_frame2:1,
5450
     _5tone_decode_disp_frame1:1;
5451
  u8 decode_reset_time; // * 100 + 100ms
5452
} _5tone_settings;
5453

    
5454
#seekto 0x2900;
5455
struct {
5456
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5457
} dtmf_codes[15];
5458

    
5459
#seekto 0x29F0;
5460
struct {
5461
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10      // 9f0
5462
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10      // 9f1
5463
  u8 unknown0[14];                                             // 9f2-9ff
5464
  u8 inspection[16];                                           // a00-a0f
5465
  u8 monitor[16];                                              // a10-a1f
5466
  u8 alarmcode[16];                                            // a20-a2f
5467
  u8 stun[16];                                                 // a30-a3f
5468
  u8 kill[16];                                                 // a40-a4f
5469
  u8 revive[16];                                               // a50-a5f
5470
  u8 unknown1[16];                                             // a60-a6f
5471
  u8 unknown2[16];                                             // a70-a7f
5472
  u8 unknown3[16];                                             // a80-a8f
5473
  u8 unknown4[16];                                             // a90-a9f
5474
  u8 unknown5[16];                                             // aa0-aaf
5475
  u8 unknown6[16];                                             // ab0-abf
5476
  u8 unknown7[16];                                             // ac0-acf
5477
  u8 masterid[16];                                             // ad0-adf
5478
  u8 viceid[16];                                               // ae0-aef
5479
  u8 unused01:7,                                               // af0
5480
     mastervice:1;
5481
  u8 unused02:3,                                               // af1
5482
     mrevive:1,
5483
     mkill:1,
5484
     mstun:1,
5485
     mmonitor:1,
5486
     minspection:1;
5487
  u8 unused03:3,                                               // af2
5488
     vrevive:1,
5489
     vkill:1,
5490
     vstun:1,
5491
     vmonitor:1,
5492
     vinspection:1;
5493
  u8 unused04:6,                                               // af3
5494
     txdisable:1,
5495
     rxdisable:1;
5496
  u8 groupcode;                                                // af4
5497
  u8 spacecode;                                                // af5
5498
  u8 delayproctime; // * 100 + 100ms                           // af6
5499
  u8 resettime;     // * 100 + 100ms                           // af7
5500
} dtmf_settings;
5501

    
5502
#seekto 0x2D00;
5503
struct {
5504
  struct {
5505
    ul16 freq1;
5506
    u8 unused01[6];
5507
    ul16 freq2;
5508
    u8 unused02[6];
5509
  } _2tone_encode[15];
5510
  u8 duration_1st_tone; // *10ms
5511
  u8 duration_2nd_tone; // *10ms
5512
  u8 duration_gap;      // *10ms
5513
  u8 unused03[13];
5514
  struct {
5515
    struct {
5516
      u8 dec;      // one out of LIST_2TONE_DEC
5517
      u8 response; // one out of LIST_2TONE_RESPONSE
5518
      u8 alert;    // 1-16
5519
    } decs[4];
5520
    u8 unused04[4];
5521
  } _2tone_decode[15];
5522
  u8 unused05[16];
5523

    
5524
  struct {
5525
    ul16 freqA;
5526
    ul16 freqB;
5527
    ul16 freqC;
5528
    ul16 freqD;
5529
    // unknown what those values mean, but they are
5530
    // derived from configured frequencies
5531
    ul16 derived_from_freqA; // 2304000/freqA
5532
    ul16 derived_from_freqB; // 2304000/freqB
5533
    ul16 derived_from_freqC; // 2304000/freqC
5534
    ul16 derived_from_freqD; // 2304000/freqD
5535
  }freqs[15];
5536
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
5537
} _2tone;
5538

    
5539
#seekto 0x3D80;
5540
struct {
5541
  u8 vhf_low[3];
5542
  u8 vhf_high[3];
5543
  u8 unknown1[4];
5544
  u8 unknown2[6];
5545
  u8 vhf2_low[3];
5546
  u8 vhf2_high[3];
5547
  u8 unknown3[4];
5548
  u8 unknown4[6];
5549
  u8 uhf_low[3];
5550
  u8 uhf_high[3];
5551
  u8 unknown5[4];
5552
  u8 unknown6[6];
5553
  u8 uhf2_low[3];
5554
  u8 uhf2_high[3];
5555
} ranges;
5556

    
5557
#seekto 0x3F70;
5558
struct {
5559
  char fp[6];
5560
} fingerprint;
5561

    
5562
"""
5563

    
5564

    
5565
class BTechColorWP(BTechMobileCommon):
5566
    """BTECH's Color WP Mobile and alike radios"""
5567
    COLOR_LCD = True
5568
    COLOR_LCD4 = True
5569
    NAME_LENGTH = 7
5570
    LIST_TMR = LIST_TMR7
5571

    
5572
    def set_options(self):
5573
        """This is to read the options from the image and set it in the
5574
        environment, for now just the limits of the freqs in the VHF/UHF
5575
        ranges"""
5576

    
5577
        # setting the correct ranges for each radio type
5578
        ranges = self._memobj.ranges
5579

    
5580
        # the normal dual bands
5581
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
5582
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
5583

    
5584
        # DEBUG
5585
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
5586
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
5587

    
5588
        # set the class with the real data
5589
        self._vhf_range = vhf
5590
        self._uhf_range = uhf
5591

    
5592

    
5593
# real radios
5594
@directory.register
5595
class KTWP12(BTechColorWP):
5596
    """QYT KT-WP12"""
5597
    VENDOR = "QYT"
5598
    MODEL = "KT-WP12"
5599
    BANDS = 2
5600
    UPLOAD_MEM_SIZE = 0X3100
5601
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
5602
                     chirp_common.PowerLevel("Low", watts=5)]
5603
    _upper = 199
5604
    _magic = MSTRING_KTWP12
5605
    _fileid = [KTWP12_fp, ]
5606
    _gmrs = False
5607

    
5608
    def process_mmap(self):
5609
        """Process the mem map into the mem object"""
5610

    
5611
        # Get it
5612
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
5613

    
5614
        # load specific parameters from the radio image
5615
        self.set_options()
5616

    
5617

    
5618
@directory.register
5619
class WP9900(BTechColorWP):
5620
    """Anysecu WP-9900"""
5621
    VENDOR = "Anysecu"
5622
    MODEL = "WP-9900"
5623
    BANDS = 2
5624
    UPLOAD_MEM_SIZE = 0X3100
5625
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
5626
                     chirp_common.PowerLevel("Low", watts=5)]
5627
    _upper = 199
5628
    _magic = MSTRING_KTWP12
5629
    _fileid = [WP9900_fp, ]
5630
    _gmrs = False
5631

    
5632
    def process_mmap(self):
5633
        """Process the mem map into the mem object"""
5634

    
5635
        # Get it
5636
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
5637

    
5638
        # load specific parameters from the radio image
5639
        self.set_options()
5640

    
5641

    
5642
@directory.register
5643
class GMRS20V2(BTechColorWP):
5644
    """Baofeng Tech GMRS-20V2"""
5645
    MODEL = "GMRS-20V2"
5646
    BANDS = 2
5647
    UPLOAD_MEM_SIZE = 0X3100
5648
    _power_levels = [chirp_common.PowerLevel("High", watts=20),
5649
                     chirp_common.PowerLevel("", watts=6),
5650
                     chirp_common.PowerLevel("Low", watts=5)]
5651
    _upper = 199
5652
    _magic = MSTRING_GMRS20V2
5653
    _fileid = [GMRS20V2_fp2, GMRS20V2_fp1, GMRS20V2_fp, ]
5654
    _gmrs = True
5655

    
5656
    def process_mmap(self):
5657
        """Process the mem map into the mem object"""
5658

    
5659
        # Get it
5660
        self._memobj = bitwise.parse(COLOR20V2_MEM_FORMAT, self._mmap)
5661

    
5662
        # load specific parameters from the radio image
5663
        self.set_options()
    (1-1/1)