Project

General

Profile

New Model #9827 » btech_wp-9900_draft_#1.py

Jim Unroe, 05/16/2022 01:25 AM

 
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 memmory in these radios
34
#
35
# The real memory of these radios extends to 0x4000
36
# On read the factory software only uses up to 0x3200
37
# On write it just uploads the contents up to 0x3100
38
#
39
# The mem beyond 0x3200 holds the ID data
40

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

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

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

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

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

    
143
# this var controls the verbosity in the debug and by default it's low (False)
144
# make it True and you will to get a very verbose debug.log
145
debug = True  # ## <change me back to False> False
146

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

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

    
159

    
160
# #### ID strings #####################################################
161

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

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

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

    
191
# B-TECH UV-25X2
192
UV25X2_fp = "UC2012"
193

    
194
# B-TECH UV-25X4
195
UV25X4_fp = "UC4014"
196

    
197
# B-TECH UV-50X2
198
UV50X2_fp = "UC2M12"
199

    
200
# B-TECH GMRS-50X1
201
GMRS50X1_fp = "NC1802"
202
GMRS50X1_fp1 = "NC1932"
203

    
204
# special var to know when we found a BTECH Gen 3
205
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
206

    
207

    
208
# WACCOM Mini-8900
209
MINI8900_fp = "M28854"
210

    
211

    
212
# QYT KT-UV980
213
KTUV980_fp = "H28854"
214

    
215
# QYT KT8900
216
KT8900_fp = "M29154"
217
# New generations KT8900
218
KT8900_fp1 = "M2C234"
219
KT8900_fp2 = "M2G1F4"
220
KT8900_fp3 = "M2G2F4"
221
KT8900_fp4 = "M2G304"
222
KT8900_fp5 = "M2G314"
223
KT8900_fp6 = "M2G424"
224
KT8900_fp7 = "M27184"
225

    
226
# KT8900R
227
KT8900R_fp = "M3G1F4"
228
# Second Generation
229
KT8900R_fp1 = "M3G214"
230
# another model
231
KT8900R_fp2 = "M3C234"
232
# another model G4?
233
KT8900R_fp3 = "M39164"
234
# another model
235
KT8900R_fp4 = "M3G314"
236
# AC3MB: another id
237
KT8900R_fp5 = "M3B064"
238

    
239
# KT7900D (quad band)
240
KT7900D_fp = "VC4004"
241
KT7900D_fp1 = "VC4284"
242
KT7900D_fp2 = "VC4264"
243
KT7900D_fp3 = "VC4114"
244
KT7900D_fp4 = "VC4104"
245
KT7900D_fp5 = "VC4254"
246
KT7900D_fp6 = "VC5264"
247
KT7900D_fp7 = "VC9204"
248

    
249
# QB25 (quad band) - a clone of KT7900D
250
QB25_fp = "QB-25"
251

    
252
# KT8900D (dual band)
253
KT8900D_fp = "VC2002"
254
KT8900D_fp1 = "VC8632"
255
KT8900D_fp2 = "VC3402"
256
KT8900D_fp3 = "VC7062"
257

    
258
# LUITON LT-588UV
259
LT588UV_fp = "V2G1F4"
260
# Added by rstrickoff gen 2 id
261
LT588UV_fp1 = "V2G214"
262

    
263
# QYT KT-8R (quad band ht)
264
KT8R_fp = "MCB264"
265
KT8R_fp1 = "MCB284"
266
KT8R_fp2 = "MC5264"
267

    
268
# QYT KT5800 (dual band)
269
KT5800_fp = "VCB222"
270

    
271
# QYT KT980Plus (dual band)
272
KT980PLUS_fp = "VC2002"
273
KT980PLUS_fp1 = "VC6042"
274

    
275
# Radioddity DB25-G (gmrs)
276
DB25G_fp = "VC6182"
277
DB25G_fp1 = "VC7062"
278

    
279
# Anysecu WP-9900
280
WP9900_fp = "WP3094"
281

    
282

    
283
# ### MAGICS
284
# for the Waccom Mini-8900
285
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
286
# for the B-TECH UV-2501+220 (including pre production ones)
287
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
288
# for the QYT KT8900 & R
289
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
290
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
291
# magic string for all other models
292
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
293
# for the QYT KT7900D & KT8900D
294
MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
295
# for the BTECH UV-25X2 and UV-50X2
296
MSTRING_UV25X2 = "\x55\x20\x16\x12\x28\xFF\xDC\x02"
297
# for the BTECH UV-25X4
298
MSTRING_UV25X4 = "\x55\x20\x16\x11\x18\xFF\xDC\x02"
299
# for the BTECH GMRS-50X1
300
MSTRING_GMRS50X1 = "\x55\x20\x18\x10\x18\xFF\xDC\x02"
301
# for the QYT KT-8R
302
MSTRING_KT8R = "\x55\x20\x17\x07\x03\xFF\xDC\x02"
303
# for the Anysecu WP-9900
304
MSTRING_WP9900 = "\x55\x20\x18\x11\x02\xFF\xDC\x02"
305

    
306

    
307
def _clean_buffer(radio):
308
    """Cleaning the read serial buffer, hard timeout to survive an infinite
309
    data stream"""
310

    
311
    # touching the serial timeout to optimize the flushing
312
    # restored at the end to the default value
313
    radio.pipe.timeout = 0.1
314
    dump = "1"
315
    datacount = 0
316

    
317
    try:
318
        while len(dump) > 0:
319
            dump = radio.pipe.read(100)
320
            datacount += len(dump)
321
            # hard limit to survive a infinite serial data stream
322
            # 5 times bigger than a normal rx block (69 bytes)
323
            if datacount > 345:
324
                seriale = "Please check your serial port selection."
325
                raise errors.RadioError(seriale)
326

    
327
        # restore the default serial timeout
328
        radio.pipe.timeout = STIMEOUT
329

    
330
    except Exception:
331
        raise errors.RadioError("Unknown error cleaning the serial buffer")
332

    
333

    
334
def _rawrecv(radio, amount):
335
    """Raw read from the radio device, less intensive way"""
336

    
337
    data = ""
338

    
339
    try:
340
        data = radio.pipe.read(amount)
341

    
342
        # DEBUG
343
        if debug is True:
344
            LOG.debug("<== (%d) bytes:\n\n%s" %
345
                      (len(data), util.hexprint(data)))
346

    
347
        # fail if no data is received
348
        if len(data) == 0:
349
            raise errors.RadioError("No data received from radio")
350

    
351
        # notice on the logs if short
352
        if len(data) < amount:
353
            LOG.warn("Short reading %d bytes from the %d requested." %
354
                     (len(data), amount))
355

    
356
    except:
357
        raise errors.RadioError("Error reading data from radio")
358

    
359
    return data
360

    
361

    
362
def _send(radio, data):
363
    """Send data to the radio device"""
364

    
365
    try:
366
        radio.pipe.write(data)
367

    
368
        # DEBUG
369
        if debug is True:
370
            LOG.debug("==> (%d) bytes:\n\n%s" %
371
                      (len(data), util.hexprint(data)))
372
    except:
373
        raise errors.RadioError("Error sending data to radio")
374

    
375

    
376
def _make_frame(cmd, addr, length, data=""):
377
    """Pack the info in the headder format"""
378
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
379
    # add the data if set
380
    if len(data) != 0:
381
        frame += data
382

    
383
    return frame
384

    
385

    
386
def _recv(radio, addr):
387
    """Get data from the radio all at once to lower syscalls load"""
388

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

    
392
    # get the whole block
393
    block = _rawrecv(radio, BLOCK_SIZE + 5)
394

    
395
    # basic check
396
    if len(block) < (BLOCK_SIZE + 5):
397
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
398

    
399
    # checking for the ack
400
    if block[0] != ACK_CMD:
401
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
402

    
403
    # header validation
404
    c, a, l = struct.unpack(">BHB", block[1:5])
405
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
406
        LOG.debug("Invalid header for block 0x%04x" % addr)
407
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
408
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
409

    
410
    # return the data
411
    return block[5:]
412

    
413

    
414
def _do_ident(radio, status, upload=False):
415
    """Put the radio in PROGRAM mode & identify it"""
416
    #  set the serial discipline
417
    radio.pipe.baudrate = 9600
418
    radio.pipe.parity = "N"
419

    
420
    # lengthen the timeout here as these radios are reseting due to timeout
421
    radio.pipe.timeout = 0.75
422

    
423
    # send the magic word
424
    _send(radio, radio._magic)
425

    
426
    # Now you get a 50 byte reply if all goes well
427
    ident = _rawrecv(radio, 50)
428

    
429
    # checking for the ack
430
    if ident[0] != ACK_CMD:
431
        raise errors.RadioError("Bad ack from radio")
432

    
433
    # basic check for the ident block
434
    if len(ident) != 50:
435
        raise errors.RadioError("Radio send a short ident block.")
436

    
437
    # check if ident is OK
438
    itis = False
439
    for fp in radio._fileid:
440
        if fp in ident:
441
            # got it!
442
            itis = True
443
            # checking if we are dealing with a Gen 3 BTECH
444
            if radio.VENDOR == "BTECH" and fp in BTECH3:
445
                radio.btech3 = True
446

    
447
            break
448

    
449
    if itis is False:
450
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
451
        raise errors.RadioError("Radio identification failed.")
452

    
453
    # pause here for the radio to catch up
454
    sleep(0.1)
455

    
456
    # the OEM software reads this additional block, so we will, too
457

    
458
    # Get the full 21 bytes at a time to reduce load
459
    # 1 byte ACK + 4 bytes header + 16 bytes of data (BLOCK_SIZE)
460
    frame = _make_frame("S", 0x3DF0, 16)
461
    _send(radio, frame)
462
    id2 = _rawrecv(radio, 21)
463

    
464
    # restore the default serial timeout
465
    radio.pipe.timeout = STIMEOUT
466

    
467
    # checking for the ack
468
    if id2[0] not in "\x06\x05":
469
        raise errors.RadioError("Bad ack from radio")
470

    
471
    # basic check for the additional block
472
    if len(id2) < 21:
473
        raise errors.RadioError("The extra ID is short, aborting.")
474

    
475
    # this radios need a extra request/answer here on the upload
476
    # the amount of data received depends of the radio type
477
    #
478
    # also the first block of TX must no have the ACK at the beginning
479
    # see _upload for this.
480
    if upload is True:
481
        # send an ACK
482
        _send(radio, ACK_CMD)
483

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

    
496
        # checking
497
        if len(ack) == 0 or ack[-1:] != ACK_CMD:
498
            raise errors.RadioError("Radio didn't ACK the upload")
499

    
500
    # DEBUG
501
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
502

    
503
    return True
504

    
505

    
506
def _download(radio):
507
    """Get the memory map"""
508

    
509
    # UI progress
510
    status = chirp_common.Status()
511

    
512
    # put radio in program mode and identify it
513
    _do_ident(radio, status)
514

    
515
    # reset the progress bar in the UI
516
    status.max = MEM_SIZE / BLOCK_SIZE
517
    status.msg = "Cloning from radio..."
518
    status.cur = 0
519
    radio.status_fn(status)
520

    
521
    data = ""
522
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
523
        # sending the read request
524
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
525

    
526
        # read
527
        d = _recv(radio, addr)
528

    
529
        # aggregate the data
530
        data += d
531

    
532
        # UI Update
533
        status.cur = addr / BLOCK_SIZE
534
        status.msg = "Cloning from radio..."
535
        radio.status_fn(status)
536

    
537
    return data
538

    
539

    
540
def _upload(radio):
541
    """Upload procedure"""
542

    
543
    # The UPLOAD mem is restricted to lower than 0x3100,
544
    # so we will overide that here localy
545
    MEM_SIZE = radio.UPLOAD_MEM_SIZE
546

    
547
    # UI progress
548
    status = chirp_common.Status()
549

    
550
    # put radio in program mode and identify it
551
    _do_ident(radio, status, True)
552

    
553
    # get the data to upload to radio
554
    data = radio.get_mmap()
555

    
556
    # Reset the UI progress
557
    status.max = MEM_SIZE / TX_BLOCK_SIZE
558
    status.cur = 0
559
    status.msg = "Cloning to radio..."
560
    radio.status_fn(status)
561

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

    
567
        # build the frame to send
568
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
569

    
570
        # first block must not send the ACK at the beginning for the
571
        # ones that has the extra id, since this have to do a extra step
572
        if addr == 0:
573
            frame = frame[1:]
574

    
575
        # send the frame
576
        _send(radio, frame)
577

    
578
        # receiving the response
579
        ack = _rawrecv(radio, 1)
580

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

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

    
588
        # UI Update
589
        status.cur = addr / TX_BLOCK_SIZE
590
        status.msg = "Cloning to radio..."
591
        radio.status_fn(status)
592

    
593

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

    
598
    if rid in cls._fileid:
599
        return True
600

    
601
    return False
602

    
603

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

    
612
    return (ilow, ihigh)
613

    
614

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

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

    
626
    # if you get here is because the freq pairs are split
627
    return True
628

    
629

    
630
class BTechMobileCommon(chirp_common.CloneModeRadio,
631
                        chirp_common.ExperimentalRadio):
632
    """BTECH's UV-5001 and alike radios"""
633
    VENDOR = "BTECH"
634
    MODEL = ""
635
    IDENT = ""
636
    BANDS = 2
637
    COLOR_LCD = False
638
    COLOR_LCD2 = False  # BTech Original GMRS Radios
639
    COLOR_LCD3 = False  # Color HT Radios
640
    COLOR_LCD4 = False  # Waterproof Mobile Radios
641
    NAME_LENGTH = 6
642
    UPLOAD_MEM_SIZE = 0X3100
643
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
644
                     chirp_common.PowerLevel("Low", watts=10)]
645
    _vhf_range = (130000000, 180000000)
646
    _220_range = (200000000, 271000000)
647
    _uhf_range = (400000000, 521000000)
648
    _350_range = (350000000, 391000000)
649
    _upper = 199
650
    _magic = MSTRING
651
    _fileid = None
652
    _id2 = False
653
    btech3 = False
654
    _gmrs = False
655

    
656
    @classmethod
657
    def get_prompts(cls):
658
        rp = chirp_common.RadioPrompts()
659
        rp.experimental = \
660
            ('This driver is experimental.\n'
661
             '\n'
662
             'Please keep a copy of your memories with the original software '
663
             'if you treasure them, this driver is new and may contain'
664
             ' bugs.\n'
665
             '\n'
666
             )
667
        rp.pre_download = _(dedent("""\
668
            Follow these instructions to download your info:
669

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

    
675
            """))
676
        rp.pre_upload = _(dedent("""\
677
            Follow these instructions to upload your info:
678

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

    
684
            """))
685
        return rp
686

    
687
    def get_features(self):
688
        """Get the radio's features"""
689

    
690
        # we will use the following var as global
691
        global POWER_LEVELS
692

    
693
        rf = chirp_common.RadioFeatures()
694
        rf.has_settings = True
695
        rf.has_bank = False
696
        rf.has_tuning_step = False
697
        rf.can_odd_split = True
698
        rf.has_name = True
699
        rf.has_offset = True
700
        rf.has_mode = True
701
        rf.has_dtcs = True
702
        rf.has_rx_dtcs = True
703
        rf.has_dtcs_polarity = True
704
        rf.has_ctone = True
705
        rf.has_cross = True
706
        rf.valid_modes = MODES
707
        rf.valid_characters = VALID_CHARS
708
        rf.valid_name_length = self.NAME_LENGTH
709
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
710
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
711
        rf.valid_cross_modes = [
712
            "Tone->Tone",
713
            "DTCS->",
714
            "->DTCS",
715
            "Tone->DTCS",
716
            "DTCS->Tone",
717
            "->Tone",
718
            "DTCS->DTCS"]
719
        rf.valid_skips = SKIP_VALUES
720
        rf.valid_dtcs_codes = DTCS
721
        rf.valid_tuning_steps = STEPS
722
        rf.memory_bounds = (0, self._upper)
723

    
724
        # power levels
725
        POWER_LEVELS = self._power_levels
726
        rf.valid_power_levels = POWER_LEVELS
727

    
728
        # normal dual bands
729
        rf.valid_bands = [self._vhf_range, self._uhf_range]
730

    
731
        # 220 band
732
        if self.BANDS == 3 or self.BANDS == 4:
733
            rf.valid_bands.append(self._220_range)
734

    
735
        # 350 band
736
        if self.BANDS == 4:
737
            rf.valid_bands.append(self._350_range)
738

    
739
        return rf
740

    
741
    def validate_memory(self, mem):
742
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
743

    
744
        _msg_duplex1 = 'Memory location only supports "Low"'
745
        _msg_duplex2 = 'Memory location only supports "off"'
746
        _msg_duplex3 = 'Memory location only supports "(None)", "+" or "off"'
747

    
748
        # ## need to evaluate this code
749
        """
750
        if self._gmrs:
751
            if mem.number < 1 or mem.number > 30:
752
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
753
                    if mem.duplex not in ['', 'off']:
754
                        # warn user wrong Duplex
755
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
756
                    if mem.power != self._power_levels[2]:
757
                        # warn user wrong Duplex
758
                        msgs.append(chirp_common.ValidationError(_msg_duplex1))
759

    
760
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
761
                    if mem.duplex not in ['off', ]:
762
                        # warn user wrong Duplex
763
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
764

    
765
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
766
                    if mem.duplex not in ['', '+', 'off']:
767
                        # warn user wrong Duplex
768
                        msgs.append(chirp_common.ValidationError(_msg_duplex3))
769
        """
770

    
771
        return msgs
772

    
773
    def sync_in(self):
774
        """Download from radio"""
775
        data = _download(self)
776
        self._mmap = memmap.MemoryMap(data)
777
        self.process_mmap()
778

    
779
    def sync_out(self):
780
        """Upload to radio"""
781
        try:
782
            _upload(self)
783
        except errors.RadioError:
784
            raise
785
        except Exception, e:
786
            raise errors.RadioError("Error: %s" % e)
787

    
788
    def get_raw_memory(self, number):
789
        return repr(self._memobj.memory[number])
790

    
791
    def _decode_tone(self, val):
792
        """Parse the tone data to decode from mem, it returns:
793
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
794
        pol = None
795

    
796
        if val in [0, 65535]:
797
            return '', None, None
798
        elif val > 0x0258:
799
            a = val / 10.0
800
            return 'Tone', a, pol
801
        else:
802
            if val > 0x69:
803
                index = val - 0x6A
804
                pol = "R"
805
            else:
806
                index = val - 1
807
                pol = "N"
808

    
809
            tone = DTCS[index]
810
            return 'DTCS', tone, pol
811

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

    
836
    def get_memory(self, number):
837
        """Get the mem representation from the radio image"""
838
        _mem = self._memobj.memory[number]
839
        _names = self._memobj.names[number]
840

    
841
        # Create a high-level memory object to return to the UI
842
        mem = chirp_common.Memory()
843

    
844
        # Memory number
845
        mem.number = number
846

    
847
        if _mem.get_raw()[0] == "\xFF":
848
            mem.empty = True
849
            return mem
850

    
851
        # Freq and offset
852
        mem.freq = int(_mem.rxfreq) * 10
853
        # tx freq can be blank
854
        if _mem.get_raw()[4] == "\xFF":
855
            # TX freq not set
856
            mem.offset = 0
857
            mem.duplex = "off"
858
        else:
859
            # TX freq set
860
            offset = (int(_mem.txfreq) * 10) - mem.freq
861
            if offset != 0:
862
                if _split(self.get_features(), mem.freq, int(
863
                          _mem.txfreq) * 10):
864
                    mem.duplex = "split"
865
                    mem.offset = int(_mem.txfreq) * 10
866
                elif offset < 0:
867
                    mem.offset = abs(offset)
868
                    mem.duplex = "-"
869
                elif offset > 0:
870
                    mem.offset = offset
871
                    mem.duplex = "+"
872
            else:
873
                mem.offset = 0
874

    
875
        # name TAG of the channel
876
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
877

    
878
        # power
879
        mem.power = POWER_LEVELS[int(_mem.power)]
880

    
881
        # wide/narrow
882
        mem.mode = MODES[int(_mem.wide)]
883

    
884
        # skip
885
        mem.skip = SKIP_VALUES[_mem.add]
886

    
887
        # tone data
888
        rxtone = txtone = None
889
        txtone = self._decode_tone(_mem.txtone)
890
        rxtone = self._decode_tone(_mem.rxtone)
891
        chirp_common.split_tone_decode(mem, txtone, rxtone)
892

    
893
        # Extra
894
        mem.extra = RadioSettingGroup("extra", "Extra")
895

    
896
        if not self.COLOR_LCD or \
897
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
898
            scramble = RadioSetting("scramble", "Scramble",
899
                                    RadioSettingValueBoolean(bool(
900
                                        _mem.scramble)))
901
            mem.extra.append(scramble)
902

    
903
        bcl = RadioSetting("bcl", "Busy channel lockout",
904
                           RadioSettingValueBoolean(bool(_mem.bcl)))
905
        mem.extra.append(bcl)
906

    
907
        pttid = RadioSetting("pttid", "PTT ID",
908
                             RadioSettingValueList(PTTID_LIST,
909
                                                   PTTID_LIST[_mem.pttid]))
910
        mem.extra.append(pttid)
911

    
912
        # validating scode
913
        scode = _mem.scode if _mem.scode != 15 else 0
914
        pttidcode = RadioSetting("scode", "PTT ID signal code",
915
                                 RadioSettingValueList(
916
                                     PTTIDCODE_LIST,
917
                                     PTTIDCODE_LIST[scode]))
918
        mem.extra.append(pttidcode)
919

    
920
        optsig = RadioSetting("optsig", "Optional signaling",
921
                              RadioSettingValueList(
922
                                  OPTSIG_LIST,
923
                                  OPTSIG_LIST[_mem.optsig]))
924
        mem.extra.append(optsig)
925

    
926
        spmute = RadioSetting("spmute", "Speaker mute",
927
                              RadioSettingValueList(
928
                                  SPMUTE_LIST,
929
                                  SPMUTE_LIST[_mem.spmute]))
930
        mem.extra.append(spmute)
931

    
932
        return mem
933

    
934
    def set_memory(self, mem):
935
        """Set the memory data in the eeprom img from the UI"""
936
        # get the eprom representation of this channel
937
        _mem = self._memobj.memory[mem.number]
938
        _names = self._memobj.names[mem.number]
939

    
940
        mem_was_empty = False
941
        # same method as used in get_memory for determining if mem is empty
942
        # doing this BEFORE overwriting it with new values ...
943
        if _mem.get_raw()[0] == "\xFF":
944
            LOG.debug("This mem was empty before")
945
            mem_was_empty = True
946

    
947
        # if empty memmory
948
        if mem.empty:
949
            # the channel itself
950
            _mem.set_raw("\xFF" * 16)
951
            # the name tag
952
            _names.set_raw("\xFF" * 16)
953
            return
954

    
955
        if mem_was_empty:
956
            # Zero the whole memory if we're making it unempty for
957
            # the first time
958
            LOG.debug('Zeroing new memory')
959
            _mem.set_raw('\x00' * 16)
960

    
961
        if self._gmrs:
962
            if mem.number >= 1 and mem.number <= 30:
963
                GMRS_FREQ = int(GMRS_FREQS[mem.number - 1] * 1000000)
964
                mem.freq = GMRS_FREQ
965
                if mem.number <= 22:
966
                    mem.duplex = ''
967
                    mem.offset = 0
968
                    mem.mode = "FM"
969
                    mem.power = POWER_LEVELS[0]
970
                    if mem.number >= 8 and mem.number <= 14:
971
                        mem.duplex = 'off'
972
                        mem.offset = 0
973
                        mem.mode = "NFM"
974
                        mem.power = POWER_LEVELS[2]
975
                if mem.number > 22:
976
                    mem.duplex = '+'
977
                    mem.offset = 5000000
978
                    mem.mode = "FM"
979
                    mem.power = POWER_LEVELS[0]
980
            elif float(mem.freq) / 1000000 in GMRS_FREQS:
981
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
982
                    mem.duplex = ''
983
                    mem.offset = 0
984
                    mem.mode = "FM"
985
                    mem.power = POWER_LEVELS[0]
986
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
987
                    mem.duplex = 'off'
988
                    mem.offset = 0
989
                    mem.mode = "NFM"
990
                    mem.power = POWER_LEVELS[2]
991
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
992
                    if mem.duplex == '+':
993
                        mem.offset = 5000000
994
                    else:
995
                        mem.offset = 0
996
                    mem.mode = "FM"
997
                    mem.power = POWER_LEVELS[0]
998
            else:
999
                mem.duplex = 'off'
1000
                mem.offset = 0
1001

    
1002
        # frequency
1003
        _mem.rxfreq = mem.freq / 10
1004

    
1005
        # duplex
1006
        if mem.duplex == "+":
1007
            _mem.txfreq = (mem.freq + mem.offset) / 10
1008
        elif mem.duplex == "-":
1009
            _mem.txfreq = (mem.freq - mem.offset) / 10
1010
        elif mem.duplex == "off":
1011
            for i in _mem.txfreq:
1012
                i.set_raw("\xFF")
1013
        elif mem.duplex == "split":
1014
            _mem.txfreq = mem.offset / 10
1015
        else:
1016
            _mem.txfreq = mem.freq / 10
1017

    
1018
        # tone data
1019
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
1020
            chirp_common.split_tone_encode(mem)
1021
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
1022
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
1023

    
1024
        # name TAG of the channel
1025
        if len(mem.name) < self.NAME_LENGTH:
1026
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
1027
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
1028
        _names.name = str(mem.name).replace(" ", "\xFF")
1029

    
1030
        # power, # default power level is high
1031
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
1032

    
1033
        # wide/narrow
1034
        _mem.wide = MODES.index(mem.mode)
1035

    
1036
        # scan add property
1037
        _mem.add = SKIP_VALUES.index(mem.skip)
1038

    
1039
        # reseting unknowns, this have to be set by hand
1040
        _mem.unknown0 = 0
1041
        _mem.unknown1 = 0
1042
        _mem.unknown2 = 0
1043
        _mem.unknown3 = 0
1044
        _mem.unknown4 = 0
1045
        _mem.unknown5 = 0
1046
        _mem.unknown6 = 0
1047

    
1048
        def _zero_settings():
1049
            _mem.spmute = 0
1050
            _mem.optsig = 0
1051
            _mem.scramble = 0
1052
            _mem.bcl = 0
1053
            _mem.pttid = 0
1054
            _mem.scode = 0
1055

    
1056
        if self.COLOR_LCD and _mem.scramble:
1057
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1058
            _mem.scramble = 0
1059

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

    
1080
        return mem
1081

    
1082
    def get_settings(self):
1083
        """Translate the bit in the mem_struct into settings in the UI"""
1084
        _mem = self._memobj
1085
        basic = RadioSettingGroup("basic", "Basic Settings")
1086
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1087
        other = RadioSettingGroup("other", "Other Settings")
1088
        work = RadioSettingGroup("work", "Work Mode Settings")
1089
        top = RadioSettings(basic, advanced, other, work)
1090

    
1091
        # Basic
1092
        if self.COLOR_LCD:
1093
            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
1094
                               RadioSettingValueList(
1095
                                   self.LIST_TMR,
1096
                                   self.LIST_TMR[_mem.settings.tmr]))
1097
            basic.append(tmr)
1098
        else:
1099
            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1100
                               RadioSettingValueBoolean(_mem.settings.tdr))
1101
            basic.append(tdr)
1102

    
1103
        sql = RadioSetting("settings.sql", "Squelch level",
1104
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1105
        basic.append(sql)
1106

    
1107
        if self.MODEL == "GMRS-50X1":
1108
            autolk = RadioSetting("settings.autolk", "Auto keylock",
1109
                                  RadioSettingValueBoolean(
1110
                                      _mem.settings.autolk))
1111
            basic.append(autolk)
1112

    
1113
        if self.MODEL == "DB25-G":
1114
            rs = RadioSettingValueInteger(0, 127, _mem.settings.mgain2)
1115
            mgain2 = RadioSetting("settings.mgain2", "Mic gain", rs)
1116
            basic.append(mgain2)
1117

    
1118
        tot = RadioSetting("settings.tot", "Time out timer",
1119
                           RadioSettingValueList(
1120
                               LIST_TOT,
1121
                               LIST_TOT[_mem.settings.tot]))
1122
        basic.append(tot)
1123

    
1124
        if self.MODEL == "KT-8R":
1125
                save = RadioSetting("settings.save", "Battery Save",
1126
                                    RadioSettingValueBoolean(
1127
                                        _mem.settings.save))
1128
                basic.append(save)
1129

    
1130
        if not self.MODEL == "KT-8R" and not self.MODEL == "WP-9900":
1131
            if self.VENDOR == "BTECH" or self.COLOR_LCD:
1132
                apo = RadioSetting("settings.apo", "Auto power off timer",
1133
                                   RadioSettingValueList(
1134
                                       LIST_APO,
1135
                                       LIST_APO[_mem.settings.apo]))
1136
                basic.append(apo)
1137
            else:
1138
                toa = RadioSetting("settings.apo", "Time out alert timer",
1139
                                   RadioSettingValueList(
1140
                                       LIST_OFF1TO10,
1141
                                       LIST_OFF1TO10[_mem.settings.apo]))
1142
                basic.append(toa)
1143

    
1144
        abr = RadioSetting("settings.abr", "Backlight timer",
1145
                           RadioSettingValueList(
1146
                               LIST_OFF1TO50,
1147
                               LIST_OFF1TO50[_mem.settings.abr]))
1148
        basic.append(abr)
1149

    
1150
        beep = RadioSetting("settings.beep", "Key beep",
1151
                            RadioSettingValueBoolean(_mem.settings.beep))
1152
        basic.append(beep)
1153

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

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

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

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

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

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

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

    
1199
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
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
                langua = RadioSetting("settings.langua", "Language",
1259
                                      RadioSettingValueList(
1260
                                          LIST_LANGUA,
1261
                                          LIST_LANGUA[_mem.settings.langua]))
1262
                basic.append(langua)
1263

    
1264
        if self.MODEL == "KT-8R":
1265
            voice = RadioSetting("settings.voice", "Voice prompt",
1266
                                 RadioSettingValueList(
1267
                                     LIST_VOICE,
1268
                                     LIST_VOICE[_mem.settings.voice]))
1269
            basic.append(voice)
1270

    
1271
        if self.MODEL == "KT-8R" or self.COLOR_LCD4:
1272
            vox = RadioSetting("settings.vox", "VOX",
1273
                               RadioSettingValueList(
1274
                                   LIST_VOX,
1275
                                   LIST_VOX[_mem.settings.vox]))
1276
            basic.append(vox)
1277

    
1278
            voxt = RadioSetting("settings.voxt", "VOX delay time",
1279
                                RadioSettingValueList(
1280
                                    LIST_VOXT,
1281
                                    LIST_VOXT[_mem.settings.voxt]))
1282
            basic.append(voxt)
1283

    
1284
        if self.VENDOR == "BTECH":
1285
            if self.COLOR_LCD:
1286
                val = RadioSettingValueList(LIST_SYNC,
1287
                                            LIST_SYNC[_mem.settings.sync])
1288
                sync = RadioSetting("settings.sync",
1289
                                    "Channel display sync", val)
1290
                basic.append(sync)
1291
            else:
1292
                sync = RadioSetting("settings.sync", "A/B channel sync",
1293
                                    RadioSettingValueBoolean(
1294
                                        _mem.settings.sync))
1295
                basic.append(sync)
1296
        else:
1297
            autolk = RadioSetting("settings.autolock", "Auto keylock",
1298
                                  RadioSettingValueBoolean(
1299
                                      _mem.settings.autolock))
1300
            basic.append(autolk)
1301

    
1302
        if not self.COLOR_LCD:
1303
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1304
                                  RadioSettingValueList(
1305
                                      LIST_PONMSG,
1306
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1307
            basic.append(ponmsg)
1308

    
1309
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3 or
1310
                                   self.COLOR_LCD4):
1311
            mainfc = RadioSetting("settings.mainfc",
1312
                                  "Main LCD foreground color",
1313
                                  RadioSettingValueList(
1314
                                      LIST_COLOR9,
1315
                                      LIST_COLOR9[_mem.settings.mainfc]))
1316
            basic.append(mainfc)
1317

    
1318
            if not self.COLOR_LCD4:
1319
                mainbc = RadioSetting("settings.mainbc",
1320
                                      "Main LCD background color",
1321
                                      RadioSettingValueList(
1322
                                          LIST_COLOR9,
1323
                                          LIST_COLOR9[_mem.settings.mainbc]))
1324
                basic.append(mainbc)
1325

    
1326
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1327
                                  RadioSettingValueList(
1328
                                      LIST_COLOR9,
1329
                                      LIST_COLOR9[_mem.settings.menufc]))
1330
            basic.append(menufc)
1331

    
1332
            if not self.COLOR_LCD4:
1333
                val = RadioSettingValueList(LIST_COLOR9,
1334
                                            LIST_COLOR9[_mem.settings.menubc])
1335
                menubc = RadioSetting("settings.menubc",
1336
                                      "Menu background color", val)
1337
                basic.append(menubc)
1338

    
1339
            stafc = RadioSetting("settings.stafc",
1340
                                 "Top status foreground color",
1341
                                 RadioSettingValueList(
1342
                                     LIST_COLOR9,
1343
                                     LIST_COLOR9[_mem.settings.stafc]))
1344
            basic.append(stafc)
1345

    
1346
            if not self.COLOR_LCD4:
1347
                stabc = RadioSetting("settings.stabc",
1348
                                     "Top status background color",
1349
                                     RadioSettingValueList(
1350
                                         LIST_COLOR9,
1351
                                         LIST_COLOR9[_mem.settings.stabc]))
1352
                basic.append(stabc)
1353

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

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

    
1369
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1370
                                RadioSettingValueList(
1371
                                    LIST_COLOR9,
1372
                                    LIST_COLOR9[_mem.settings.rxfc]))
1373
            basic.append(rxfc)
1374

    
1375
            txfc = RadioSetting("settings.txfc",
1376
                                "Transmitting character color",
1377
                                RadioSettingValueList(
1378
                                    LIST_COLOR9,
1379
                                    LIST_COLOR9[_mem.settings.txfc]))
1380
            basic.append(txfc)
1381

    
1382
            if not self.COLOR_LCD4:
1383
                txdisp = RadioSetting("settings.txdisp",
1384
                                      "Transmitting status display",
1385
                                      RadioSettingValueList(
1386
                                          LIST_TXDISP,
1387
                                          LIST_TXDISP[_mem.settings.txdisp]))
1388
                basic.append(txdisp)
1389

    
1390
        elif self.COLOR_LCD2 or self.COLOR_LCD3:
1391
            stfc = RadioSetting("settings.stfc",
1392
                                "ST-FC",
1393
                                RadioSettingValueList(
1394
                                    LIST_COLOR8,
1395
                                    LIST_COLOR8[_mem.settings.stfc]))
1396
            basic.append(stfc)
1397

    
1398
            mffc = RadioSetting("settings.mffc",
1399
                                "MF-FC",
1400
                                RadioSettingValueList(
1401
                                    LIST_COLOR8,
1402
                                    LIST_COLOR8[_mem.settings.mffc]))
1403
            basic.append(mffc)
1404

    
1405
            sfafc = RadioSetting("settings.sfafc",
1406
                                 "SFA-FC",
1407
                                 RadioSettingValueList(
1408
                                     LIST_COLOR8,
1409
                                     LIST_COLOR8[_mem.settings.sfafc]))
1410
            basic.append(sfafc)
1411

    
1412
            sfbfc = RadioSetting("settings.sfbfc",
1413
                                 "SFB-FC",
1414
                                 RadioSettingValueList(
1415
                                     LIST_COLOR8,
1416
                                     LIST_COLOR8[_mem.settings.sfbfc]))
1417
            basic.append(sfbfc)
1418

    
1419
            sfcfc = RadioSetting("settings.sfcfc",
1420
                                 "SFC-FC",
1421
                                 RadioSettingValueList(
1422
                                     LIST_COLOR8,
1423
                                     LIST_COLOR8[_mem.settings.sfcfc]))
1424
            basic.append(sfcfc)
1425

    
1426
            sfdfc = RadioSetting("settings.sfdfc",
1427
                                 "SFD-FC",
1428
                                 RadioSettingValueList(
1429
                                     LIST_COLOR8,
1430
                                     LIST_COLOR8[_mem.settings.sfdfc]))
1431
            basic.append(sfdfc)
1432

    
1433
            subfc = RadioSetting("settings.subfc",
1434
                                 "SUB-FC",
1435
                                 RadioSettingValueList(
1436
                                     LIST_COLOR8,
1437
                                     LIST_COLOR8[_mem.settings.subfc]))
1438
            basic.append(subfc)
1439

    
1440
            fmfc = RadioSetting("settings.fmfc",
1441
                                "FM-FC",
1442
                                RadioSettingValueList(
1443
                                    LIST_COLOR8,
1444
                                    LIST_COLOR8[_mem.settings.fmfc]))
1445
            basic.append(fmfc)
1446

    
1447
            sigfc = RadioSetting("settings.sigfc",
1448
                                 "SIG-FC",
1449
                                 RadioSettingValueList(
1450
                                     LIST_COLOR8,
1451
                                     LIST_COLOR8[_mem.settings.sigfc]))
1452
            basic.append(sigfc)
1453

    
1454
            if not self.MODEL == "KT-8R":
1455
                modfc = RadioSetting("settings.modfc",
1456
                                     "MOD-FC",
1457
                                     RadioSettingValueList(
1458
                                         LIST_COLOR8,
1459
                                         LIST_COLOR8[_mem.settings.modfc]))
1460
                basic.append(modfc)
1461

    
1462
            menufc = RadioSetting("settings.menufc",
1463
                                  "MENUFC",
1464
                                  RadioSettingValueList(
1465
                                      LIST_COLOR8,
1466
                                      LIST_COLOR8[_mem.settings.menufc]))
1467
            basic.append(menufc)
1468

    
1469
            txfc = RadioSetting("settings.txfc",
1470
                                "TX-FC",
1471
                                RadioSettingValueList(
1472
                                    LIST_COLOR8,
1473
                                    LIST_COLOR8[_mem.settings.txfc]))
1474
            basic.append(txfc)
1475

    
1476
            if self.MODEL == "KT-8R":
1477
                rxfc = RadioSetting("settings.rxfc",
1478
                                    "RX-FC",
1479
                                    RadioSettingValueList(
1480
                                        LIST_COLOR8,
1481
                                        LIST_COLOR8[_mem.settings.rxfc]))
1482
                basic.append(rxfc)
1483

    
1484
            if not self.MODEL == "KT-8R":
1485
                txdisp = RadioSetting("settings.txdisp",
1486
                                      "Transmitting status display",
1487
                                      RadioSettingValueList(
1488
                                          LIST_TXDISP,
1489
                                          LIST_TXDISP[_mem.settings.txdisp]))
1490
                basic.append(txdisp)
1491
        elif self.COLOR_LCD4:
1492
            asfc = RadioSetting("settings.asfc",
1493
                                "Above Stat fore color",
1494
                                RadioSettingValueList(
1495
                                    LIST_COLOR8,
1496
                                    LIST_COLOR8[_mem.settings.asfc]))
1497
            basic.append(asfc)
1498

    
1499
            mainfc = RadioSetting("settings.mainfc",
1500
                                  "Main fore color",
1501
                                  RadioSettingValueList(
1502
                                     LIST_COLOR8,
1503
                                     LIST_COLOR8[_mem.settings.mainfc]))
1504
            basic.append(mainfc)
1505

    
1506
            a_fc = RadioSetting("settings.a_fc",
1507
                                "A - fore color",
1508
                                RadioSettingValueList(
1509
                                    LIST_COLOR8,
1510
                                    LIST_COLOR8[_mem.settings.a_fc]))
1511
            basic.append(a_fc)
1512

    
1513
            b_fc = RadioSetting("settings.b_fc",
1514
                                "B - fore color",
1515
                                RadioSettingValueList(
1516
                                    LIST_COLOR8,
1517
                                    LIST_COLOR8[_mem.settings.b_fc]))
1518
            basic.append(b_fc)
1519

    
1520
            c_fc = RadioSetting("settings.c_fc",
1521
                                "C - fore color",
1522
                                RadioSettingValueList(
1523
                                    LIST_COLOR8,
1524
                                    LIST_COLOR8[_mem.settings.c_fc]))
1525
            basic.append(c_fc)
1526

    
1527
            subfc = RadioSetting("settings.subfc",
1528
                                 "Sub fore color",
1529
                                 RadioSettingValueList(
1530
                                     LIST_COLOR8,
1531
                                     LIST_COLOR8[_mem.settings.subfc]))
1532
            basic.append(subfc)
1533

    
1534
            battfc = RadioSetting("settings.battfc",
1535
                                  "Battery fore color",
1536
                                  RadioSettingValueList(
1537
                                      LIST_COLOR8,
1538
                                      LIST_COLOR8[_mem.settings.battfc]))
1539
            basic.append(battfc)
1540

    
1541
            sigfc = RadioSetting("settings.sigfc",
1542
                                 "Signal fore color",
1543
                                 RadioSettingValueList(
1544
                                     LIST_COLOR8,
1545
                                     LIST_COLOR8[_mem.settings.sigfc]))
1546
            basic.append(sigfc)
1547

    
1548
            menufc = RadioSetting("settings.menufc",
1549
                                  "Menu fore color",
1550
                                  RadioSettingValueList(
1551
                                      LIST_COLOR8,
1552
                                      LIST_COLOR8[_mem.settings.menufc]))
1553
            basic.append(menufc)
1554

    
1555
            txfc = RadioSetting("settings.txfc",
1556
                                "TX fore color",
1557
                                RadioSettingValueList(
1558
                                    LIST_COLOR8,
1559
                                    LIST_COLOR8[_mem.settings.txfc]))
1560
            basic.append(txfc)
1561

    
1562
            rxfc = RadioSetting("settings.rxfc",
1563
                                "RX fore color",
1564
                                RadioSettingValueList(
1565
                                    LIST_COLOR8,
1566
                                    LIST_COLOR8[_mem.settings.rxfc]))
1567
            basic.append(rxfc)
1568
        else:
1569
            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1570
                                 RadioSettingValueList(
1571
                                     LIST_COLOR4,
1572
                                     LIST_COLOR4[_mem.settings.wtled]))
1573
            basic.append(wtled)
1574

    
1575
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1576
                                 RadioSettingValueList(
1577
                                     LIST_COLOR4,
1578
                                     LIST_COLOR4[_mem.settings.rxled]))
1579
            basic.append(rxled)
1580

    
1581
            txled = RadioSetting("settings.txled", "TX backlight Color",
1582
                                 RadioSettingValueList(
1583
                                     LIST_COLOR4,
1584
                                     LIST_COLOR4[_mem.settings.txled]))
1585
            basic.append(txled)
1586

    
1587
        anil = RadioSetting("settings.anil", "ANI length",
1588
                            RadioSettingValueList(
1589
                                LIST_ANIL,
1590
                                LIST_ANIL[_mem.settings.anil]))
1591
        basic.append(anil)
1592

    
1593
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1594
                            RadioSettingValueList(
1595
                                LIST_REPS,
1596
                                LIST_REPS[_mem.settings.reps]))
1597
        basic.append(reps)
1598

    
1599
        if self.COLOR_LCD4:
1600
            dsub = RadioSetting("settings.dsub", "Subtone display",
1601
                                RadioSettingValueBoolean(
1602
                                    _mem.settings.dsub))
1603
            basic.append(dsub)
1604

    
1605
        # ## if not self.MODEL == "GMRS-50X1" and not self.MODEL == "KT-8R" \
1606
        # ##         and not self.MODEL == "WP-9900":
1607
        model_list = ["GMRS-50X1", "KT-8R", "WP-9900"]
1608
        if self.MODEL not in model_list:
1609
            repm = RadioSetting("settings.repm", "Relay condition",
1610
                                RadioSettingValueList(
1611
                                    LIST_REPM,
1612
                                    LIST_REPM[_mem.settings.repm]))
1613
            basic.append(repm)
1614

    
1615
        if self.VENDOR == "BTECH" or self.COLOR_LCD:
1616
            if self.COLOR_LCD:
1617
                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
1618
                                     RadioSettingValueList(
1619
                                         LIST_OFF1TO50,
1620
                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
1621
                basic.append(tmrmr)
1622
            else:
1623
                tdrab = RadioSetting("settings.tdrab", "TDR return time",
1624
                                     RadioSettingValueList(
1625
                                         LIST_OFF1TO50,
1626
                                         LIST_OFF1TO50[_mem.settings.tdrab]))
1627
                basic.append(tdrab)
1628

    
1629
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1630
                               RadioSettingValueBoolean(_mem.settings.ste))
1631
            basic.append(ste)
1632

    
1633
            if self.COLOR_LCD4:
1634
                rpste = RadioSetting("settings.rpste", "Repeater STE",
1635
                                     RadioSettingValueList(
1636
                                         LIST_OFF1TO10,
1637
                                         LIST_OFF1TO10[_mem.settings.rpste]))
1638
                basic.append(rpste)
1639
            else:
1640
                rpste = RadioSetting("settings.rpste", "Repeater STE",
1641
                                     RadioSettingValueList(
1642
                                         LIST_OFF1TO9,
1643
                                         LIST_OFF1TO9[_mem.settings.rpste]))
1644
                basic.append(rpste)
1645

    
1646
            if self.COLOR_LCD4:
1647
                rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1648
                                     RadioSettingValueList(
1649
                                         LIST_OFF1TO60,
1650
                                         LIST_OFF1TO60[_mem.settings.rptdl]))
1651
                basic.append(rptdl)
1652
            else:
1653
                rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1654
                                     RadioSettingValueList(
1655
                                         LIST_RPTDL,
1656
                                         LIST_RPTDL[_mem.settings.rptdl]))
1657
                basic.append(rptdl)
1658

    
1659
        if self.MODEL == "DB25-G":
1660
            mgain = RadioSetting("settings.mgain", "Auto power-on",
1661
                                 RadioSettingValueBoolean(_mem.settings.mgain))
1662
            basic.append(mgain)
1663

    
1664
        if str(_mem.fingerprint.fp) in BTECH3:
1665
            mgain = RadioSetting("settings.mgain", "Mic gain",
1666
                                 RadioSettingValueInteger(0, 120,
1667
                                                          _mem.settings.mgain))
1668
            basic.append(mgain)
1669

    
1670
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1671
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1672
                                 RadioSettingValueInteger(0, 60,
1673
                                                          _mem.settings.dtmfg))
1674
            basic.append(dtmfg)
1675

    
1676
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1677
            mgain = RadioSetting("settings.mgain", "Mic gain",
1678
                                 RadioSettingValueInteger(0, 127,
1679
                                                          _mem.settings.mgain))
1680
            basic.append(mgain)
1681

    
1682
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1683
                                  RadioSettingValueList(
1684
                                      LIST_SKIPTX,
1685
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1686
            basic.append(skiptx)
1687

    
1688
            scmode = RadioSetting("settings.scmode", "Scan mode",
1689
                                  RadioSettingValueList(
1690
                                      LIST_SCMODE,
1691
                                      LIST_SCMODE[_mem.settings.scmode]))
1692
            basic.append(scmode)
1693

    
1694
        if self.MODEL == "KT-8R":
1695
            tmrtx = RadioSetting("settings.tmrtx", "TX in multi-standby",
1696
                                 RadioSettingValueList(
1697
                                     LIST_TMRTX,
1698
                                     LIST_TMRTX[_mem.settings.tmrtx]))
1699
            basic.append(tmrtx)
1700

    
1701
        # Advanced
1702
        def _filter(name):
1703
            filtered = ""
1704
            for char in str(name):
1705
                if char in VALID_CHARS:
1706
                    filtered += char
1707
                else:
1708
                    filtered += " "
1709
            return filtered
1710

    
1711
        if self.COLOR_LCD and not (self.COLOR_LCD2 or self.COLOR_LCD3 or
1712
                                   self.COLOR_LCD4):
1713
            _msg = self._memobj.poweron_msg
1714
            line1 = RadioSetting("poweron_msg.line1",
1715
                                 "Power-on message line 1",
1716
                                 RadioSettingValueString(0, 8, _filter(
1717
                                                         _msg.line1)))
1718
            advanced.append(line1)
1719
            line2 = RadioSetting("poweron_msg.line2",
1720
                                 "Power-on message line 2",
1721
                                 RadioSettingValueString(0, 8, _filter(
1722
                                                         _msg.line2)))
1723
            advanced.append(line2)
1724
            line3 = RadioSetting("poweron_msg.line3",
1725
                                 "Power-on message line 3",
1726
                                 RadioSettingValueString(0, 8, _filter(
1727
                                                         _msg.line3)))
1728
            advanced.append(line3)
1729
            line4 = RadioSetting("poweron_msg.line4",
1730
                                 "Power-on message line 4",
1731
                                 RadioSettingValueString(0, 8, _filter(
1732
                                                         _msg.line4)))
1733
            advanced.append(line4)
1734
            line5 = RadioSetting("poweron_msg.line5",
1735
                                 "Power-on message line 5",
1736
                                 RadioSettingValueString(0, 8, _filter(
1737
                                                         _msg.line5)))
1738
            advanced.append(line5)
1739
            line6 = RadioSetting("poweron_msg.line6",
1740
                                 "Power-on message line 6",
1741
                                 RadioSettingValueString(0, 8, _filter(
1742
                                                         _msg.line6)))
1743
            advanced.append(line6)
1744
            line7 = RadioSetting("poweron_msg.line7",
1745
                                 "Power-on message line 7",
1746
                                 RadioSettingValueString(0, 8, _filter(
1747
                                                         _msg.line7)))
1748
            advanced.append(line7)
1749
            line8 = RadioSetting("poweron_msg.line8", "Static message",
1750
                                 RadioSettingValueString(0, 8, _filter(
1751
                                                         _msg.line8)))
1752
            advanced.append(line8)
1753
        elif self.COLOR_LCD2 or self.COLOR_LCD3 or self.COLOR_LCD4:
1754
            _msg = self._memobj.static_msg
1755
            line = RadioSetting("static_msg.line", "Static message",
1756
                                RadioSettingValueString(0, 16, _filter(
1757
                                    _msg.line)))
1758
            advanced.append(line)
1759
        else:
1760
            _msg = self._memobj.poweron_msg
1761
            line1 = RadioSetting("poweron_msg.line1",
1762
                                 "Power-on message line 1",
1763
                                 RadioSettingValueString(0, 6, _filter(
1764
                                                         _msg.line1)))
1765
            advanced.append(line1)
1766
            line2 = RadioSetting("poweron_msg.line2",
1767
                                 "Power-on message line 2",
1768
                                 RadioSettingValueString(0, 6, _filter(
1769
                                                         _msg.line2)))
1770
            advanced.append(line2)
1771

    
1772
        if self.MODEL in ("UV-2501", "UV-5001"):
1773
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1774
                                   RadioSettingValueBoolean(
1775
                                       _mem.settings2.vfomren))
1776
            advanced.append(vfomren)
1777

    
1778
            reseten = RadioSetting("settings2.reseten", "RESET",
1779
                                   RadioSettingValueBoolean(
1780
                                       _mem.settings2.reseten))
1781
            advanced.append(reseten)
1782

    
1783
            menuen = RadioSetting("settings2.menuen", "Menu",
1784
                                  RadioSettingValueBoolean(
1785
                                      _mem.settings2.menuen))
1786
            advanced.append(menuen)
1787

    
1788
        # Other
1789
        def convert_bytes_to_limit(bytes):
1790
            limit = ""
1791
            for byte in bytes:
1792
                if byte < 10:
1793
                    limit += chr(byte + 0x30)
1794
                else:
1795
                    break
1796
            return limit
1797

    
1798
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1799
            _ranges = self._memobj.ranges220
1800
            ranges = "ranges220"
1801
        else:
1802
            _ranges = self._memobj.ranges
1803
            ranges = "ranges"
1804

    
1805
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1806
        val = RadioSettingValueString(0, 3, _limit)
1807
        val.set_mutable(False)
1808
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1809
        other.append(vhf_low)
1810

    
1811
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1812
        val = RadioSettingValueString(0, 3, _limit)
1813
        val.set_mutable(False)
1814
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1815
        other.append(vhf_high)
1816

    
1817
        if self.BANDS == 3 or self.BANDS == 4:
1818
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1819
            val = RadioSettingValueString(0, 3, _limit)
1820
            val.set_mutable(False)
1821
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1822
            other.append(vhf2_low)
1823

    
1824
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1825
            val = RadioSettingValueString(0, 3, _limit)
1826
            val.set_mutable(False)
1827
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1828
            other.append(vhf2_high)
1829

    
1830
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1831
        val = RadioSettingValueString(0, 3, _limit)
1832
        val.set_mutable(False)
1833
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1834
        other.append(uhf_low)
1835

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

    
1842
        if self.BANDS == 4:
1843
            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
1844
            val = RadioSettingValueString(0, 3, _limit)
1845
            val.set_mutable(False)
1846
            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
1847
            other.append(uhf2_low)
1848

    
1849
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1850
            val = RadioSettingValueString(0, 3, _limit)
1851
            val.set_mutable(False)
1852
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1853
            other.append(uhf2_high)
1854

    
1855
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1856
        val.set_mutable(False)
1857
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1858
        other.append(fp)
1859

    
1860
        # Work
1861
        if self.COLOR_LCD:
1862
            dispab = RadioSetting("settings2.dispab", "Display",
1863
                                  RadioSettingValueList(
1864
                                      LIST_ABCD,
1865
                                      LIST_ABCD[_mem.settings2.dispab]))
1866
            work.append(dispab)
1867
        else:
1868
            dispab = RadioSetting("settings2.dispab", "Display",
1869
                                  RadioSettingValueList(
1870
                                      LIST_AB,
1871
                                      LIST_AB[_mem.settings2.dispab]))
1872
            work.append(dispab)
1873

    
1874
        if self.COLOR_LCD:
1875
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1876
                                  RadioSettingValueList(
1877
                                      LIST_VFOMR,
1878
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1879
            work.append(vfomra)
1880

    
1881
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1882
                                  RadioSettingValueList(
1883
                                      LIST_VFOMR,
1884
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1885
            work.append(vfomrb)
1886

    
1887
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1888
                                  RadioSettingValueList(
1889
                                      LIST_VFOMR,
1890
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1891
            work.append(vfomrc)
1892

    
1893
            if not self.COLOR_LCD4:
1894
                vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
1895
                                      RadioSettingValueList(
1896
                                          LIST_VFOMR,
1897
                                          LIST_VFOMR[_mem.settings2.vfomrd]))
1898
                work.append(vfomrd)
1899
        else:
1900
            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1901
                                 RadioSettingValueList(
1902
                                     LIST_VFOMR,
1903
                                     LIST_VFOMR[_mem.settings2.vfomr]))
1904
            work.append(vfomr)
1905

    
1906
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1907
                               RadioSettingValueBoolean(
1908
                                   _mem.settings2.keylock))
1909
        work.append(keylock)
1910

    
1911
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1912
                             RadioSettingValueInteger(0, self._upper,
1913
                                                      _mem.settings2.mrcha))
1914
        work.append(mrcha)
1915

    
1916
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1917
                             RadioSettingValueInteger(0, self._upper,
1918
                                                      _mem.settings2.mrchb))
1919
        work.append(mrchb)
1920

    
1921
        if self.COLOR_LCD:
1922
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1923
                                 RadioSettingValueInteger(
1924
                                     0, self._upper, _mem.settings2.mrchc))
1925
            work.append(mrchc)
1926

    
1927
            if not self.COLOR_LCD4:
1928
                mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1929
                                     RadioSettingValueInteger(
1930
                                         0, self._upper, _mem.settings2.mrchd))
1931
                work.append(mrchd)
1932

    
1933
        def convert_bytes_to_freq(bytes):
1934
            real_freq = 0
1935
            for byte in bytes:
1936
                real_freq = (real_freq * 10) + byte
1937
            return chirp_common.format_freq(real_freq * 10)
1938

    
1939
        def my_validate(value):
1940
            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
1941
            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
1942
            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
1943
            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
1944
            if self.BANDS == 3 or self.BANDS == 4:
1945
                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
1946
                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
1947
            if self.BANDS == 4:
1948
                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
1949
                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
1950

    
1951
            value = chirp_common.parse_freq(value)
1952
            msg = ("Can't be less then %i.0000")
1953
            if value > 99000000 and value < _vhf_lower * 1000000:
1954
                raise InvalidValueError(msg % (_vhf_lower))
1955
            msg = ("Can't be betweeb %i.9975-%i.0000")
1956
            if self.BANDS == 2:
1957
                if (_vhf_upper + 1) * 1000000 <= value and \
1958
                        value < _uhf_lower * 1000000:
1959
                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
1960
            if self.BANDS == 3:
1961
                if (_vhf_upper + 1) * 1000000 <= value and \
1962
                        value < _vhf2_lower * 1000000:
1963
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1964
                if (_vhf2_upper + 1) * 1000000 <= value and \
1965
                        value < _uhf_lower * 1000000:
1966
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
1967
            if self.BANDS == 4:
1968
                if (_vhf_upper + 1) * 1000000 <= value and \
1969
                        value < _vhf2_lower * 1000000:
1970
                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
1971
                if (_vhf2_upper + 1) * 1000000 <= value and \
1972
                        value < _uhf2_lower * 1000000:
1973
                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
1974
                if (_uhf2_upper + 1) * 1000000 <= value and \
1975
                        value < _uhf_lower * 1000000:
1976
                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
1977
            msg = ("Can't be greater then %i.9975")
1978
            if value > 99000000 and value >= _uhf_upper * 1000000:
1979
                raise InvalidValueError(msg % (_uhf_upper))
1980
            return chirp_common.format_freq(value)
1981

    
1982
        def apply_freq(setting, obj):
1983
            value = chirp_common.parse_freq(str(setting.value)) / 10
1984
            for i in range(7, -1, -1):
1985
                obj.freq[i] = value % 10
1986
                value /= 10
1987

    
1988
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1989
                                        _mem.vfo.a.freq))
1990
        val1a.set_validate_callback(my_validate)
1991
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1992
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1993
        work.append(vfoafreq)
1994

    
1995
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1996
                                        _mem.vfo.b.freq))
1997
        val1b.set_validate_callback(my_validate)
1998
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1999
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
2000
        work.append(vfobfreq)
2001

    
2002
        if self.COLOR_LCD:
2003
            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2004
                                            _mem.vfo.c.freq))
2005
            val1c.set_validate_callback(my_validate)
2006
            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
2007
            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
2008
            work.append(vfocfreq)
2009

    
2010
            if not self.COLOR_LCD4:
2011
                val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2012
                                                _mem.vfo.d.freq))
2013
                val1d.set_validate_callback(my_validate)
2014
                vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
2015
                vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
2016
                work.append(vfodfreq)
2017

    
2018
        if not self.MODEL == "GMRS-50X1":
2019
            vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
2020
                                      RadioSettingValueList(
2021
                                          LIST_SHIFT,
2022
                                          LIST_SHIFT[_mem.vfo.a.shiftd]))
2023
            work.append(vfoashiftd)
2024

    
2025
            vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
2026
                                      RadioSettingValueList(
2027
                                          LIST_SHIFT,
2028
                                          LIST_SHIFT[_mem.vfo.b.shiftd]))
2029
            work.append(vfobshiftd)
2030

    
2031
            if self.COLOR_LCD:
2032
                vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
2033
                                          RadioSettingValueList(
2034
                                              LIST_SHIFT,
2035
                                              LIST_SHIFT[_mem.vfo.c.shiftd]))
2036
                work.append(vfocshiftd)
2037

    
2038
                if not self.COLOR_LCD4:
2039
                    val = RadioSettingValueList(LIST_SHIFT,
2040
                                                LIST_SHIFT[_mem.vfo.d.shiftd])
2041
                    vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
2042
                                              val)
2043
                    work.append(vfodshiftd)
2044

    
2045
        def convert_bytes_to_offset(bytes):
2046
            real_offset = 0
2047
            for byte in bytes:
2048
                real_offset = (real_offset * 10) + byte
2049
            return chirp_common.format_freq(real_offset * 1000)
2050

    
2051
        def apply_offset(setting, obj):
2052
            value = chirp_common.parse_freq(str(setting.value)) / 1000
2053
            for i in range(5, -1, -1):
2054
                obj.offset[i] = value % 10
2055
                value /= 10
2056

    
2057
        if not self.MODEL == "GMRS-50X1":
2058
            if self.COLOR_LCD:
2059
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2060
                                                _mem.vfo.a.offset))
2061
                vfoaoffset = RadioSetting("vfo.a.offset",
2062
                                          "VFO A offset (0.000-999.999)",
2063
                                          val1a)
2064
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2065
                work.append(vfoaoffset)
2066

    
2067
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2068
                                                _mem.vfo.b.offset))
2069
                vfoboffset = RadioSetting("vfo.b.offset",
2070
                                          "VFO B offset (0.000-999.999)",
2071
                                          val1b)
2072
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2073
                work.append(vfoboffset)
2074

    
2075
                val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2076
                                                _mem.vfo.c.offset))
2077
                vfocoffset = RadioSetting("vfo.c.offset",
2078
                                          "VFO C offset (0.000-999.999)",
2079
                                          val1c)
2080
                vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
2081
                work.append(vfocoffset)
2082

    
2083
                if not self.COLOR_LCD4:
2084
                    val1d = RadioSettingValueString(0, 10,
2085
                                                    convert_bytes_to_offset(
2086
                                                        _mem.vfo.d.offset))
2087
                    vfodoffset = RadioSetting("vfo.d.offset",
2088
                                              "VFO D offset (0.000-999.999)",
2089
                                              val1d)
2090
                    vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
2091
                    work.append(vfodoffset)
2092
            else:
2093
                val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2094
                                                _mem.vfo.a.offset))
2095
                vfoaoffset = RadioSetting("vfo.a.offset",
2096
                                          "VFO A offset (0.000-99.999)", val1a)
2097
                vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
2098
                work.append(vfoaoffset)
2099

    
2100
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2101
                                                _mem.vfo.b.offset))
2102
                vfoboffset = RadioSetting("vfo.b.offset",
2103
                                          "VFO B offset (0.000-99.999)", val1b)
2104
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2105
                work.append(vfoboffset)
2106

    
2107
        if not self.MODEL == "GMRS-50X1":
2108
            vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
2109
                                   RadioSettingValueList(
2110
                                       LIST_TXP,
2111
                                       LIST_TXP[_mem.vfo.a.power]))
2112
            work.append(vfoatxp)
2113

    
2114
            vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
2115
                                   RadioSettingValueList(
2116
                                       LIST_TXP,
2117
                                       LIST_TXP[_mem.vfo.b.power]))
2118
            work.append(vfobtxp)
2119

    
2120
            if self.COLOR_LCD:
2121
                vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
2122
                                       RadioSettingValueList(
2123
                                           LIST_TXP,
2124
                                           LIST_TXP[_mem.vfo.c.power]))
2125
                work.append(vfoctxp)
2126

    
2127
                if not self.COLOR_LCD4:
2128
                    vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
2129
                                           RadioSettingValueList(
2130
                                               LIST_TXP,
2131
                                               LIST_TXP[_mem.vfo.d.power]))
2132
                    work.append(vfodtxp)
2133

    
2134
        if not self.MODEL == "GMRS-50X1":
2135
            vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
2136
                                    RadioSettingValueList(
2137
                                        LIST_WIDE,
2138
                                        LIST_WIDE[_mem.vfo.a.wide]))
2139
            work.append(vfoawide)
2140

    
2141
            vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
2142
                                    RadioSettingValueList(
2143
                                        LIST_WIDE,
2144
                                        LIST_WIDE[_mem.vfo.b.wide]))
2145
            work.append(vfobwide)
2146

    
2147
            if self.COLOR_LCD:
2148
                vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
2149
                                        RadioSettingValueList(
2150
                                            LIST_WIDE,
2151
                                            LIST_WIDE[_mem.vfo.c.wide]))
2152
                work.append(vfocwide)
2153

    
2154
                if not self.COLOR_LCD4:
2155
                    vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
2156
                                            RadioSettingValueList(
2157
                                                LIST_WIDE,
2158
                                                LIST_WIDE[_mem.vfo.d.wide]))
2159
                    work.append(vfodwide)
2160

    
2161
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
2162
                                RadioSettingValueList(
2163
                                    LIST_STEP,
2164
                                    LIST_STEP[_mem.vfo.a.step]))
2165
        work.append(vfoastep)
2166

    
2167
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
2168
                                RadioSettingValueList(
2169
                                    LIST_STEP,
2170
                                    LIST_STEP[_mem.vfo.b.step]))
2171
        work.append(vfobstep)
2172

    
2173
        if self.COLOR_LCD:
2174
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
2175
                                    RadioSettingValueList(
2176
                                        LIST_STEP,
2177
                                        LIST_STEP[_mem.vfo.c.step]))
2178
            work.append(vfocstep)
2179

    
2180
            if not self.COLOR_LCD4:
2181
                vfodstep = RadioSetting("vfo.d.step", "VFO D step",
2182
                                        RadioSettingValueList(
2183
                                            LIST_STEP,
2184
                                            LIST_STEP[_mem.vfo.d.step]))
2185
                work.append(vfodstep)
2186

    
2187
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
2188
                                  RadioSettingValueList(
2189
                                      OPTSIG_LIST,
2190
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
2191
        work.append(vfoaoptsig)
2192

    
2193
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
2194
                                  RadioSettingValueList(
2195
                                      OPTSIG_LIST,
2196
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
2197
        work.append(vfoboptsig)
2198

    
2199
        if self.COLOR_LCD:
2200
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
2201
                                      RadioSettingValueList(
2202
                                          OPTSIG_LIST,
2203
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
2204
            work.append(vfocoptsig)
2205

    
2206
            if not self.COLOR_LCD4:
2207
                val = RadioSettingValueList(OPTSIG_LIST,
2208
                                            OPTSIG_LIST[_mem.vfo.d.optsig])
2209
                vfodoptsig = RadioSetting("vfo.d.optsig",
2210
                                          "VFO D optional signal", val)
2211
                work.append(vfodoptsig)
2212

    
2213
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
2214
                                  RadioSettingValueList(
2215
                                      SPMUTE_LIST,
2216
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
2217
        work.append(vfoaspmute)
2218

    
2219
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
2220
                                  RadioSettingValueList(
2221
                                      SPMUTE_LIST,
2222
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
2223
        work.append(vfobspmute)
2224

    
2225
        if self.COLOR_LCD:
2226
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
2227
                                      RadioSettingValueList(
2228
                                          SPMUTE_LIST,
2229
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
2230
            work.append(vfocspmute)
2231

    
2232
            if not self.COLOR_LCD4:
2233
                vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
2234
                                          RadioSettingValueList(
2235
                                              SPMUTE_LIST,
2236
                                              SPMUTE_LIST[_mem.vfo.d.spmute]))
2237
                work.append(vfodspmute)
2238

    
2239
        if not self.COLOR_LCD or \
2240
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
2241
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
2242
                                   RadioSettingValueBoolean(
2243
                                       _mem.vfo.a.scramble))
2244
            work.append(vfoascr)
2245

    
2246
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
2247
                                   RadioSettingValueBoolean(
2248
                                       _mem.vfo.b.scramble))
2249
            work.append(vfobscr)
2250

    
2251
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
2252
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
2253
                                   RadioSettingValueBoolean(
2254
                                       _mem.vfo.c.scramble))
2255
            work.append(vfocscr)
2256

    
2257
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
2258
                                   RadioSettingValueBoolean(
2259
                                       _mem.vfo.d.scramble))
2260
            work.append(vfodscr)
2261

    
2262
        if not self.MODEL == "GMRS-50X1":
2263
            vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
2264
                                     RadioSettingValueList(
2265
                                         PTTIDCODE_LIST,
2266
                                         PTTIDCODE_LIST[_mem.vfo.a.scode]))
2267
            work.append(vfoascode)
2268

    
2269
            vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
2270
                                     RadioSettingValueList(
2271
                                         PTTIDCODE_LIST,
2272
                                         PTTIDCODE_LIST[_mem.vfo.b.scode]))
2273
            work.append(vfobscode)
2274

    
2275
            if self.COLOR_LCD:
2276
                vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
2277
                                         RadioSettingValueList(
2278
                                             PTTIDCODE_LIST,
2279
                                             PTTIDCODE_LIST[_mem.vfo.c.scode]))
2280
                work.append(vfocscode)
2281

    
2282
                if not self.COLOR_LCD4:
2283
                    val = RadioSettingValueList(PTTIDCODE_LIST,
2284
                                                PTTIDCODE_LIST[
2285
                                                    _mem.vfo.d.scode])
2286
                    vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
2287
                                             val)
2288
                    work.append(vfodscode)
2289

    
2290
        if not self.MODEL == "GMRS-50X1":
2291
            pttid = RadioSetting("settings.pttid", "PTT ID",
2292
                                 RadioSettingValueList(
2293
                                     PTTID_LIST,
2294
                                     PTTID_LIST[_mem.settings.pttid]))
2295
            work.append(pttid)
2296

    
2297
        if not self.COLOR_LCD:
2298
            # FM presets
2299
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
2300
            top.append(fm_presets)
2301

    
2302
            def fm_validate(value):
2303
                if value == 0:
2304
                    return chirp_common.format_freq(value)
2305
                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
2306
                    msg = ("FM-Preset-Frequency: " +
2307
                           "Must be between 87.5 and 108 MHz")
2308
                    raise InvalidValueError(msg)
2309
                return value
2310

    
2311
            def apply_fm_preset_name(setting, obj):
2312
                valstring = str(setting.value)
2313
                for i in range(0, 6):
2314
                    if valstring[i] in VALID_CHARS:
2315
                        obj[i] = valstring[i]
2316
                    else:
2317
                        obj[i] = '0xff'
2318

    
2319
            def apply_fm_freq(setting, obj):
2320
                value = chirp_common.parse_freq(str(setting.value)) / 10
2321
                for i in range(7, -1, -1):
2322
                    obj.freq[i] = value % 10
2323
                    value /= 10
2324

    
2325
            _presets = self._memobj.fm_radio_preset
2326
            i = 1
2327
            for preset in _presets:
2328
                line = RadioSetting("fm_presets_" + str(i),
2329
                                    "Station name " + str(i),
2330
                                    RadioSettingValueString(0, 6, _filter(
2331
                                        preset.broadcast_station_name)))
2332
                line.set_apply_callback(apply_fm_preset_name,
2333
                                        preset.broadcast_station_name)
2334

    
2335
                val = RadioSettingValueFloat(0, 108,
2336
                                             convert_bytes_to_freq(
2337
                                                 preset.freq))
2338
                fmfreq = RadioSetting("fm_presets_" + str(i) + "_freq",
2339
                                      "Frequency " + str(i), val)
2340
                val.set_validate_callback(fm_validate)
2341
                fmfreq.set_apply_callback(apply_fm_freq, preset)
2342
                fm_presets.append(line)
2343
                fm_presets.append(fmfreq)
2344

    
2345
                i = i + 1
2346

    
2347
        # DTMF-Setting
2348
        dtmf_enc_settings = RadioSettingGroup("dtmf_enc_settings",
2349
                                              "DTMF Encoding Settings")
2350
        dtmf_dec_settings = RadioSettingGroup("dtmf_dec_settings",
2351
                                              "DTMF Decoding Settings")
2352
        top.append(dtmf_enc_settings)
2353
        top.append(dtmf_dec_settings)
2354
        txdisable = RadioSetting("dtmf_settings.txdisable",
2355
                                 "TX-Disable",
2356
                                 RadioSettingValueBoolean(
2357
                                     _mem.dtmf_settings.txdisable))
2358
        dtmf_enc_settings.append(txdisable)
2359

    
2360
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
2361
                                 "RX-Disable",
2362
                                 RadioSettingValueBoolean(
2363
                                     _mem.dtmf_settings.rxdisable))
2364
        dtmf_enc_settings.append(rxdisable)
2365

    
2366
        if _mem.dtmf_settings.dtmfspeed_on > 0x0F:
2367
            val = 0x03
2368
        else:
2369
            val = _mem.dtmf_settings.dtmfspeed_on
2370
        dtmfspeed_on = RadioSetting(
2371
            "dtmf_settings.dtmfspeed_on",
2372
            "DTMF Speed (On Time)",
2373
            RadioSettingValueList(LIST_DTMF_SPEED,
2374
                                  LIST_DTMF_SPEED[
2375
                                      val]))
2376
        dtmf_enc_settings.append(dtmfspeed_on)
2377

    
2378
        if _mem.dtmf_settings.dtmfspeed_off > 0x0F:
2379
            val = 0x03
2380
        else:
2381
            val = _mem.dtmf_settings.dtmfspeed_off
2382
        dtmfspeed_off = RadioSetting(
2383
            "dtmf_settings.dtmfspeed_off",
2384
            "DTMF Speed (Off Time)",
2385
            RadioSettingValueList(LIST_DTMF_SPEED,
2386
                                  LIST_DTMF_SPEED[
2387
                                      val]))
2388
        dtmf_enc_settings.append(dtmfspeed_off)
2389

    
2390
        def memory2string(dmtf_mem):
2391
            dtmf_string = ""
2392
            for digit in dmtf_mem:
2393
                if digit != 255:
2394
                    index = LIST_DTMF_VALUES.index(digit)
2395
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2396
            return dtmf_string
2397

    
2398
        def apply_dmtf_frame(setting, obj):
2399
            LOG.debug("Setting DTMF-Code: " + str(setting.value))
2400
            val_string = str(setting.value)
2401
            for i in range(0, 16):
2402
                obj[i] = 255
2403
            i = 0
2404
            for current_char in val_string:
2405
                current_char = current_char.upper()
2406
                index = LIST_DTMF_DIGITS.index(current_char)
2407
                obj[i] = LIST_DTMF_VALUES[index]
2408
                i = i + 1
2409

    
2410
        codes = self._memobj.dtmf_codes
2411
        i = 1
2412
        for dtmfcode in codes:
2413
            val = RadioSettingValueString(0, 16, memory2string(
2414
                                              dtmfcode.code),
2415
                                          False, CHARSET_DTMF_DIGITS)
2416
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
2417
                                "DMTF Code " + str(i), val)
2418
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
2419
            dtmf_enc_settings.append(line)
2420
            i = i + 1
2421

    
2422
        line = RadioSetting("dtmf_settings.mastervice",
2423
                            "Master and Vice ID",
2424
                            RadioSettingValueBoolean(
2425
                                _mem.dtmf_settings.mastervice))
2426
        dtmf_dec_settings.append(line)
2427

    
2428
        val = RadioSettingValueString(0, 16, memory2string(
2429
                                          _mem.dtmf_settings.masterid),
2430
                                      False, CHARSET_DTMF_DIGITS)
2431
        line = RadioSetting("dtmf_settings.masterid",
2432
                            "Master Control ID ", val)
2433
        line.set_apply_callback(apply_dmtf_frame,
2434
                                _mem.dtmf_settings.masterid)
2435
        dtmf_dec_settings.append(line)
2436

    
2437
        line = RadioSetting("dtmf_settings.minspection",
2438
                            "Master Inspection",
2439
                            RadioSettingValueBoolean(
2440
                                _mem.dtmf_settings.minspection))
2441
        dtmf_dec_settings.append(line)
2442

    
2443
        line = RadioSetting("dtmf_settings.mmonitor",
2444
                            "Master Monitor",
2445
                            RadioSettingValueBoolean(
2446
                                _mem.dtmf_settings.mmonitor))
2447
        dtmf_dec_settings.append(line)
2448

    
2449
        line = RadioSetting("dtmf_settings.mstun",
2450
                            "Master Stun",
2451
                            RadioSettingValueBoolean(
2452
                                _mem.dtmf_settings.mstun))
2453
        dtmf_dec_settings.append(line)
2454

    
2455
        line = RadioSetting("dtmf_settings.mkill",
2456
                            "Master Kill",
2457
                            RadioSettingValueBoolean(
2458
                                _mem.dtmf_settings.mkill))
2459
        dtmf_dec_settings.append(line)
2460

    
2461
        line = RadioSetting("dtmf_settings.mrevive",
2462
                            "Master Revive",
2463
                            RadioSettingValueBoolean(
2464
                                _mem.dtmf_settings.mrevive))
2465
        dtmf_dec_settings.append(line)
2466

    
2467
        val = RadioSettingValueString(0, 16, memory2string(
2468
                                          _mem.dtmf_settings.viceid),
2469
                                      False, CHARSET_DTMF_DIGITS)
2470
        line = RadioSetting("dtmf_settings.viceid",
2471
                            "Vice Control ID ", val)
2472
        line.set_apply_callback(apply_dmtf_frame,
2473
                                _mem.dtmf_settings.viceid)
2474
        dtmf_dec_settings.append(line)
2475

    
2476
        line = RadioSetting("dtmf_settings.vinspection",
2477
                            "Vice Inspection",
2478
                            RadioSettingValueBoolean(
2479
                                _mem.dtmf_settings.vinspection))
2480
        dtmf_dec_settings.append(line)
2481

    
2482
        line = RadioSetting("dtmf_settings.vmonitor",
2483
                            "Vice Monitor",
2484
                            RadioSettingValueBoolean(
2485
                                _mem.dtmf_settings.vmonitor))
2486
        dtmf_dec_settings.append(line)
2487

    
2488
        line = RadioSetting("dtmf_settings.vstun",
2489
                            "Vice Stun",
2490
                            RadioSettingValueBoolean(
2491
                                _mem.dtmf_settings.vstun))
2492
        dtmf_dec_settings.append(line)
2493

    
2494
        line = RadioSetting("dtmf_settings.vkill",
2495
                            "Vice Kill",
2496
                            RadioSettingValueBoolean(
2497
                                _mem.dtmf_settings.vkill))
2498
        dtmf_dec_settings.append(line)
2499

    
2500
        line = RadioSetting("dtmf_settings.vrevive",
2501
                            "Vice Revive",
2502
                            RadioSettingValueBoolean(
2503
                                _mem.dtmf_settings.vrevive))
2504
        dtmf_dec_settings.append(line)
2505

    
2506
        val = RadioSettingValueString(0, 16, memory2string(
2507
                                          _mem.dtmf_settings.inspection),
2508
                                      False, CHARSET_DTMF_DIGITS)
2509
        line = RadioSetting("dtmf_settings.inspection",
2510
                            "Inspection", val)
2511
        line.set_apply_callback(apply_dmtf_frame,
2512
                                _mem.dtmf_settings.inspection)
2513
        dtmf_dec_settings.append(line)
2514

    
2515
        val = RadioSettingValueString(0, 16, memory2string(
2516
                                          _mem.dtmf_settings.alarmcode),
2517
                                      False, CHARSET_DTMF_DIGITS)
2518
        line = RadioSetting("dtmf_settings.alarmcode",
2519
                            "Alarm", val)
2520
        line.set_apply_callback(apply_dmtf_frame,
2521
                                _mem.dtmf_settings.alarmcode)
2522
        dtmf_dec_settings.append(line)
2523

    
2524
        val = RadioSettingValueString(0, 16, memory2string(
2525
                                          _mem.dtmf_settings.kill),
2526
                                      False, CHARSET_DTMF_DIGITS)
2527
        line = RadioSetting("dtmf_settings.kill",
2528
                            "Kill", val)
2529
        line.set_apply_callback(apply_dmtf_frame,
2530
                                _mem.dtmf_settings.kill)
2531
        dtmf_dec_settings.append(line)
2532

    
2533
        val = RadioSettingValueString(0, 16, memory2string(
2534
                                          _mem.dtmf_settings.monitor),
2535
                                      False, CHARSET_DTMF_DIGITS)
2536
        line = RadioSetting("dtmf_settings.monitor",
2537
                            "Monitor", val)
2538
        line.set_apply_callback(apply_dmtf_frame,
2539
                                _mem.dtmf_settings.monitor)
2540
        dtmf_dec_settings.append(line)
2541

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

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

    
2560
        def apply_dmtf_listvalue(setting, obj):
2561
            LOG.debug("Setting value: " + str(setting.value) + " from list")
2562
            val = str(setting.value)
2563
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2564
            val = LIST_DTMF_SPECIAL_VALUES[index]
2565
            obj.set_value(val)
2566

    
2567
        if _mem.dtmf_settings.groupcode not in LIST_DTMF_SPECIAL_VALUES:
2568
            val = 0x0B
2569
        else:
2570
            val = _mem.dtmf_settings.groupcode
2571
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2572
        line = RadioSetting(
2573
            "dtmf_settings.groupcode",
2574
            "Group Code",
2575
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2576
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2577
        line.set_apply_callback(apply_dmtf_listvalue,
2578
                                _mem.dtmf_settings.groupcode)
2579
        dtmf_dec_settings.append(line)
2580

    
2581
        if _mem.dtmf_settings.spacecode not in LIST_DTMF_SPECIAL_VALUES:
2582
            val = 0x0C
2583
        else:
2584
            val = _mem.dtmf_settings.spacecode
2585
        idx = LIST_DTMF_SPECIAL_VALUES.index(val)
2586
        line = RadioSetting(
2587
            "dtmf_settings.spacecode",
2588
            "Space Code",
2589
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2590
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2591
        line.set_apply_callback(apply_dmtf_listvalue,
2592
                                _mem.dtmf_settings.spacecode)
2593
        dtmf_dec_settings.append(line)
2594

    
2595
        if self.COLOR_LCD:
2596
            if _mem.dtmf_settings.resettime > 0x63:
2597
                val = 0x4F
2598
            else:
2599
                val = _mem.dtmf_settings.resettime
2600
            line = RadioSetting(
2601
                "dtmf_settings.resettime",
2602
                "Reset time",
2603
                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
2604
                                      LIST_5TONE_RESET_COLOR[
2605
                                          val]))
2606
            dtmf_dec_settings.append(line)
2607
        else:
2608
            line = RadioSetting(
2609
                "dtmf_settings.resettime",
2610
                "Reset time",
2611
                RadioSettingValueList(LIST_5TONE_RESET,
2612
                                      LIST_5TONE_RESET[
2613
                                          _mem.dtmf_settings.resettime]))
2614
            dtmf_dec_settings.append(line)
2615

    
2616
        if _mem.dtmf_settings.delayproctime > 0x27:
2617
            val = 0x04
2618
        else:
2619
            val = _mem.dtmf_settings.delayproctime
2620
        line = RadioSetting(
2621
            "dtmf_settings.delayproctime",
2622
            "Delay processing time",
2623
            RadioSettingValueList(LIST_DTMF_DELAY,
2624
                                  LIST_DTMF_DELAY[
2625
                                      val]))
2626
        dtmf_dec_settings.append(line)
2627

    
2628
        # 5 Tone Settings
2629
        stds_5tone = RadioSettingGroup("stds_5tone", "Standards")
2630
        codes_5tone = RadioSettingGroup("codes_5tone", "Codes")
2631

    
2632
        group_5tone = RadioSettingGroup("group_5tone", "5 Tone Settings")
2633
        group_5tone.append(stds_5tone)
2634
        group_5tone.append(codes_5tone)
2635

    
2636
        top.append(group_5tone)
2637

    
2638
        def apply_list_value(setting, obj):
2639
            options = setting.value.get_options()
2640
            obj.set_value(options.index(str(setting.value)))
2641

    
2642
        _5tone_standards = self._memobj._5tone_std_settings
2643
        i = 0
2644
        for standard in _5tone_standards:
2645
            std_5tone = RadioSettingGroup("std_5tone_" + str(i),
2646
                                          LIST_5TONE_STANDARDS[i])
2647
            stds_5tone.append(std_5tone)
2648

    
2649
            period = standard.period
2650
            if period == 255:
2651
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2652
                          " is not yet configured. Setting to 70ms.")
2653
                period = 5
2654

    
2655
            if period <= len(LIST_5TONE_STANDARD_PERIODS):
2656
                line = RadioSetting(
2657
                    "_5tone_std_settings_" + str(i) + "_period",
2658
                    "Period (ms)", RadioSettingValueList
2659
                    (LIST_5TONE_STANDARD_PERIODS,
2660
                     LIST_5TONE_STANDARD_PERIODS[period]))
2661
                line.set_apply_callback(apply_list_value, standard.period)
2662
                std_5tone.append(line)
2663
            else:
2664
                LOG.debug("Invalid value for 5tone period! Disabling.")
2665

    
2666
            group_tone = standard.group_tone
2667
            if group_tone == 255:
2668
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2669
                          " is not yet configured. Setting to A.")
2670
                group_tone = 10
2671

    
2672
            if group_tone <= len(LIST_5TONE_DIGITS):
2673
                line = RadioSetting(
2674
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2675
                    "Group Tone",
2676
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2677
                                          LIST_5TONE_DIGITS[
2678
                                              group_tone]))
2679
                line.set_apply_callback(apply_list_value,
2680
                                        standard.group_tone)
2681
                std_5tone.append(line)
2682
            else:
2683
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2684

    
2685
            repeat_tone = standard.repeat_tone
2686
            if repeat_tone == 255:
2687
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2688
                          " is not yet configured. Setting to E.")
2689
                repeat_tone = 14
2690

    
2691
            if repeat_tone <= len(LIST_5TONE_DIGITS):
2692
                line = RadioSetting(
2693
                    "_5tone_std_settings_" + str(i) + "_repttone",
2694
                    "Repeat Tone",
2695
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2696
                                          LIST_5TONE_DIGITS[
2697
                                              repeat_tone]))
2698
                line.set_apply_callback(apply_list_value,
2699
                                        standard.repeat_tone)
2700
                std_5tone.append(line)
2701
            else:
2702
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2703
            i = i + 1
2704

    
2705
        def my_apply_5tonestdlist_value(setting, obj):
2706
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2707
                obj.set_value(0xFF)
2708
            else:
2709
                obj.set_value(LIST_5TONE_STANDARDS.
2710
                              index(str(setting.value)))
2711

    
2712
        def apply_5tone_frame(setting, obj):
2713
            LOG.debug("Setting 5 Tone: " + str(setting.value))
2714
            valstring = str(setting.value)
2715
            if len(valstring) == 0:
2716
                for i in range(0, 5):
2717
                    obj[i] = 255
2718
            else:
2719
                validFrame = True
2720
                for i in range(0, 5):
2721
                    currentChar = valstring[i].upper()
2722
                    if currentChar in LIST_5TONE_DIGITS:
2723
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2724
                    else:
2725
                        validFrame = False
2726
                        LOG.debug("invalid char: " + str(currentChar))
2727
                if not validFrame:
2728
                    LOG.debug("setting whole frame to FF")
2729
                    for i in range(0, 5):
2730
                        obj[i] = 255
2731

    
2732
        def validate_5tone_frame(value):
2733
            if (len(str(value)) != 5) and (len(str(value)) != 0):
2734
                msg = ("5 Tone must have 5 digits or 0 digits")
2735
                raise InvalidValueError(msg)
2736
            for digit in str(value):
2737
                if digit.upper() not in LIST_5TONE_DIGITS:
2738
                    msg = (str(digit) + " is not a valid digit for 5tones")
2739
                    raise InvalidValueError(msg)
2740
            return value
2741

    
2742
        def frame2string(frame):
2743
            frameString = ""
2744
            for digit in frame:
2745
                if digit != 255:
2746
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2747
            return frameString
2748

    
2749
        _5tone_codes = self._memobj._5tone_codes
2750
        i = 1
2751
        for code in _5tone_codes:
2752
            code_5tone = RadioSettingGroup("code_5tone_" + str(i),
2753
                                           "5 Tone code " + str(i))
2754
            codes_5tone.append(code_5tone)
2755
            if (code.standard == 255):
2756
                currentVal = 15
2757
            else:
2758
                currentVal = code.standard
2759
            line = RadioSetting("_5tone_code_" + str(i) + "_std",
2760
                                " Standard",
2761
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2762
                                                      LIST_5TONE_STANDARDS[
2763
                                                          currentVal]))
2764
            line.set_apply_callback(my_apply_5tonestdlist_value,
2765
                                    code.standard)
2766
            code_5tone.append(line)
2767

    
2768
            val = RadioSettingValueString(0, 6,
2769
                                          frame2string(code.frame1), False)
2770
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1",
2771
                                " Frame 1", val)
2772
            val.set_validate_callback(validate_5tone_frame)
2773
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2774
            code_5tone.append(line)
2775

    
2776
            val = RadioSettingValueString(0, 6,
2777
                                          frame2string(code.frame2), False)
2778
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2779
                                " Frame 2", val)
2780
            val.set_validate_callback(validate_5tone_frame)
2781
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2782
            code_5tone.append(line)
2783

    
2784
            val = RadioSettingValueString(0, 6,
2785
                                          frame2string(code.frame3), False)
2786
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2787
                                " Frame 3", val)
2788
            val.set_validate_callback(validate_5tone_frame)
2789
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2790
            code_5tone.append(line)
2791
            i = i + 1
2792

    
2793
        _5_tone_decode1 = RadioSetting(
2794
            "_5tone_settings._5tone_decode_call_frame1",
2795
            "5 Tone decode call Frame 1",
2796
            RadioSettingValueBoolean(
2797
                _mem._5tone_settings._5tone_decode_call_frame1))
2798
        group_5tone.append(_5_tone_decode1)
2799

    
2800
        _5_tone_decode2 = RadioSetting(
2801
            "_5tone_settings._5tone_decode_call_frame2",
2802
            "5 Tone decode call Frame 2",
2803
            RadioSettingValueBoolean(
2804
                _mem._5tone_settings._5tone_decode_call_frame2))
2805
        group_5tone.append(_5_tone_decode2)
2806

    
2807
        _5_tone_decode3 = RadioSetting(
2808
            "_5tone_settings._5tone_decode_call_frame3",
2809
            "5 Tone decode call Frame 3",
2810
            RadioSettingValueBoolean(
2811
                _mem._5tone_settings._5tone_decode_call_frame3))
2812
        group_5tone.append(_5_tone_decode3)
2813

    
2814
        _5_tone_decode_disp1 = RadioSetting(
2815
            "_5tone_settings._5tone_decode_disp_frame1",
2816
            "5 Tone decode disp Frame 1",
2817
            RadioSettingValueBoolean(
2818
                _mem._5tone_settings._5tone_decode_disp_frame1))
2819
        group_5tone.append(_5_tone_decode_disp1)
2820

    
2821
        _5_tone_decode_disp2 = RadioSetting(
2822
            "_5tone_settings._5tone_decode_disp_frame2",
2823
            "5 Tone decode disp Frame 2",
2824
            RadioSettingValueBoolean(
2825
                _mem._5tone_settings._5tone_decode_disp_frame2))
2826
        group_5tone.append(_5_tone_decode_disp2)
2827

    
2828
        _5_tone_decode_disp3 = RadioSetting(
2829
            "_5tone_settings._5tone_decode_disp_frame3",
2830
            "5 Tone decode disp Frame 3",
2831
            RadioSettingValueBoolean(
2832
                _mem._5tone_settings._5tone_decode_disp_frame3))
2833
        group_5tone.append(_5_tone_decode_disp3)
2834

    
2835
        decode_standard = _mem._5tone_settings.decode_standard
2836
        if decode_standard == 255:
2837
            decode_standard = 0
2838
        if decode_standard <= len(LIST_5TONE_STANDARDS_without_none):
2839
            line = RadioSetting("_5tone_settings.decode_standard",
2840
                                "5 Tone-decode Standard",
2841
                                RadioSettingValueList(
2842
                                    LIST_5TONE_STANDARDS_without_none,
2843
                                    LIST_5TONE_STANDARDS_without_none[
2844
                                        decode_standard]))
2845
            group_5tone.append(line)
2846
        else:
2847
            LOG.debug("Invalid decode std...")
2848

    
2849
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2850
        if _5tone_delay1 == 255:
2851
            _5tone_delay1 = 20
2852

    
2853
        if _5tone_delay1 <= len(LIST_5TONE_DELAY):
2854
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2855
                                         LIST_5TONE_DELAY[
2856
                                             _5tone_delay1])
2857
            line = RadioSetting("_5tone_settings._5tone_delay1",
2858
                                "5 Tone Delay Frame 1", list)
2859
            group_5tone.append(line)
2860
        else:
2861
            LOG.debug(
2862
                "Invalid value for 5tone delay (frame1) ! Disabling.")
2863

    
2864
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2865
        if _5tone_delay2 == 255:
2866
            _5tone_delay2 = 20
2867
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2868

    
2869
        if _5tone_delay2 <= len(LIST_5TONE_DELAY):
2870
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2871
                                         LIST_5TONE_DELAY[
2872
                                             _5tone_delay2])
2873
            line = RadioSetting("_5tone_settings._5tone_delay2",
2874
                                "5 Tone Delay Frame 2", list)
2875
            group_5tone.append(line)
2876
        else:
2877
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2878

    
2879
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2880
        if _5tone_delay3 == 255:
2881
            _5tone_delay3 = 20
2882
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2883

    
2884
        if _5tone_delay3 <= len(LIST_5TONE_DELAY):
2885
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2886
                                         LIST_5TONE_DELAY[
2887
                                             _5tone_delay3])
2888
            line = RadioSetting("_5tone_settings._5tone_delay3",
2889
                                "5 Tone Delay Frame 3", list)
2890
            group_5tone.append(line)
2891
        else:
2892
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2893

    
2894
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2895
        if ext_length == 255:
2896
            ext_length = 0
2897
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2898

    
2899
        if ext_length <= len(LIST_5TONE_DELAY):
2900
            list = RadioSettingValueList(
2901
                LIST_5TONE_DELAY,
2902
                LIST_5TONE_DELAY[
2903
                    ext_length])
2904
            line = RadioSetting(
2905
                "_5tone_settings._5tone_first_digit_ext_length",
2906
                "First digit extend length", list)
2907
            group_5tone.append(line)
2908
        else:
2909
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2910

    
2911
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2912
        if decode_reset_time == 255:
2913
            decode_reset_time = 59
2914
            LOG.debug("Decode reset time unconfigured. resetting.")
2915
        if decode_reset_time <= len(LIST_5TONE_RESET):
2916
            list = RadioSettingValueList(
2917
                LIST_5TONE_RESET,
2918
                LIST_5TONE_RESET[
2919
                    decode_reset_time])
2920
            line = RadioSetting("_5tone_settings.decode_reset_time",
2921
                                "Decode reset time", list)
2922
            group_5tone.append(line)
2923
        else:
2924
            LOG.debug("Invalid value decode reset time! Disabling.")
2925

    
2926
        # 2 Tone
2927
        encode_2tone = RadioSettingGroup("encode_2tone", "2 Tone Encode")
2928
        decode_2tone = RadioSettingGroup("decode_2tone", "2 Code Decode")
2929

    
2930
        top.append(encode_2tone)
2931
        top.append(decode_2tone)
2932

    
2933
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2934
        if duration_1st_tone == 255:
2935
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2936
                      "configured. Setting to 600ms")
2937
            duration_1st_tone = 60
2938

    
2939
        if duration_1st_tone <= len(LIST_5TONE_DELAY):
2940
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2941
                                        LIST_5TONE_DELAY[
2942
                                            duration_1st_tone])
2943
            line = RadioSetting("_2tone.duration_1st_tone",
2944
                                "Duration 1st Tone", val)
2945
            encode_2tone.append(line)
2946

    
2947
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2948
        if duration_2nd_tone == 255:
2949
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2950
                      "configured. Setting to 600ms")
2951
            duration_2nd_tone = 60
2952

    
2953
        if duration_2nd_tone <= len(LIST_5TONE_DELAY):
2954
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2955
                                        LIST_5TONE_DELAY[
2956
                                            duration_2nd_tone])
2957
            line = RadioSetting("_2tone.duration_2nd_tone",
2958
                                "Duration 2nd Tone", val)
2959
            encode_2tone.append(line)
2960

    
2961
        duration_gap = self._memobj._2tone.duration_gap
2962
        if duration_gap == 255:
2963
            LOG.debug("Duration of gap is not yet " +
2964
                      "configured. Setting to 300ms")
2965
            duration_gap = 30
2966

    
2967
        if duration_gap <= len(LIST_5TONE_DELAY):
2968
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2969
                                RadioSettingValueList(LIST_5TONE_DELAY,
2970
                                                      LIST_5TONE_DELAY[
2971
                                                          duration_gap]))
2972
            encode_2tone.append(line)
2973

    
2974
        def _2tone_validate(value):
2975
            if value == 0:
2976
                return 65535
2977
            if value == 65535:
2978
                return value
2979
            if not (300 <= value and value <= 3000):
2980
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2981
                raise InvalidValueError(msg)
2982
            return value
2983

    
2984
        def apply_2tone_freq(setting, obj):
2985
            val = int(setting.value)
2986
            if (val == 0) or (val == 65535):
2987
                obj.set_value(65535)
2988
            else:
2989
                obj.set_value(val)
2990

    
2991
        i = 1
2992
        for code in self._memobj._2tone._2tone_encode:
2993
            code_2tone = RadioSettingGroup("code_2tone_" + str(i),
2994
                                           "Encode Code " + str(i))
2995
            encode_2tone.append(code_2tone)
2996

    
2997
            tmp = code.freq1
2998
            if tmp == 65535:
2999
                tmp = 0
3000
            val1 = RadioSettingValueInteger(0, 65535, tmp)
3001
            freq1 = RadioSetting("2tone_code_" + str(i) + "_freq1",
3002
                                 "Frequency 1", val1)
3003
            val1.set_validate_callback(_2tone_validate)
3004
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
3005
            code_2tone.append(freq1)
3006

    
3007
            tmp = code.freq2
3008
            if tmp == 65535:
3009
                tmp = 0
3010
            val2 = RadioSettingValueInteger(0, 65535, tmp)
3011
            freq2 = RadioSetting("2tone_code_" + str(i) + "_freq2",
3012
                                 "Frequency 2", val2)
3013
            val2.set_validate_callback(_2tone_validate)
3014
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
3015
            code_2tone.append(freq2)
3016

    
3017
            i = i + 1
3018

    
3019
        decode_reset_time = _mem._2tone.reset_time
3020
        if decode_reset_time == 255:
3021
            decode_reset_time = 59
3022
            LOG.debug("Decode reset time unconfigured. resetting.")
3023
        if decode_reset_time <= len(LIST_5TONE_RESET):
3024
            list = RadioSettingValueList(
3025
                LIST_5TONE_RESET,
3026
                LIST_5TONE_RESET[
3027
                    decode_reset_time])
3028
            line = RadioSetting("_2tone.reset_time",
3029
                                "Decode reset time", list)
3030
            decode_2tone.append(line)
3031
        else:
3032
            LOG.debug("Invalid value decode reset time! Disabling.")
3033

    
3034
        def apply_2tone_freq_pair(setting, obj):
3035
            val = int(setting.value)
3036
            derived_val = 65535
3037
            frqname = str(setting._name[-5:])
3038
            derivedname = "derived_from_" + frqname
3039

    
3040
            if (val == 0):
3041
                val = 65535
3042
                derived_val = 65535
3043
            else:
3044
                derived_val = int(round(2304000.0/val))
3045

    
3046
            obj[frqname].set_value(val)
3047
            obj[derivedname].set_value(derived_val)
3048

    
3049
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " +
3050
                      derivedname + ": " + str(derived_val))
3051

    
3052
        i = 1
3053
        for decode_code in self._memobj._2tone._2tone_decode:
3054
            _2tone_dec_code = RadioSettingGroup("code_2tone_" + str(i),
3055
                                                "Decode Code " + str(i))
3056
            decode_2tone.append(_2tone_dec_code)
3057

    
3058
            j = 1
3059
            for dec in decode_code.decs:
3060
                val = dec.dec
3061
                if val == 255:
3062
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +
3063
                              " is not yet configured. Setting to 0.")
3064
                    val = 0
3065

    
3066
                if val <= len(LIST_2TONE_DEC):
3067
                    line = RadioSetting(
3068
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
3069
                        "Dec " + str(j), RadioSettingValueList
3070
                        (LIST_2TONE_DEC,
3071
                         LIST_2TONE_DEC[val]))
3072
                    line.set_apply_callback(apply_list_value, dec.dec)
3073
                    _2tone_dec_code.append(line)
3074
                else:
3075
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
3076

    
3077
                val = dec.response
3078
                if val == 255:
3079
                    LOG.debug("Response for Code " +
3080
                              str(i) + " Dec " + str(j) +
3081
                              " is not yet configured. Setting to 0.")
3082
                    val = 0
3083

    
3084
                if val <= len(LIST_2TONE_RESPONSE):
3085
                    line = RadioSetting(
3086
                        "_2tone_dec_settings_" +
3087
                        str(i) + "_resp_" + str(j),
3088
                        "Response " + str(j), RadioSettingValueList
3089
                        (LIST_2TONE_RESPONSE,
3090
                         LIST_2TONE_RESPONSE[val]))
3091
                    line.set_apply_callback(apply_list_value, dec.response)
3092
                    _2tone_dec_code.append(line)
3093
                else:
3094
                    LOG.debug(
3095
                        "Invalid value for 2tone response! Disabling.")
3096

    
3097
                val = dec.alert
3098
                if val == 255:
3099
                    LOG.debug("Alert for Code " +
3100
                              str(i) + " Dec " + str(j) +
3101
                              " is not yet configured. Setting to 0.")
3102
                    val = 0
3103

    
3104
                if val <= len(PTTIDCODE_LIST):
3105
                    line = RadioSetting(
3106
                        "_2tone_dec_settings_" +
3107
                        str(i) + "_alert_" + str(j),
3108
                        "Alert " + str(j), RadioSettingValueList
3109
                        (PTTIDCODE_LIST,
3110
                         PTTIDCODE_LIST[val]))
3111
                    line.set_apply_callback(apply_list_value, dec.alert)
3112
                    _2tone_dec_code.append(line)
3113
                else:
3114
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
3115
                j = j + 1
3116

    
3117
            freq = self._memobj._2tone.freqs[i-1]
3118
            for char in ['A', 'B', 'C', 'D']:
3119
                setting_name = "freq" + str(char)
3120

    
3121
                tmp = freq[setting_name]
3122
                if tmp == 65535:
3123
                    tmp = 0
3124
                if tmp != 0:
3125
                    expected = int(round(2304000.0/tmp))
3126
                    from_mem = freq["derived_from_" + setting_name]
3127
                    if expected != from_mem:
3128
                        LOG.error("Expected " + str(expected) +
3129
                                  " but read " + str(from_mem) +
3130
                                  ". Disabling 2Tone Decode Freqs!")
3131
                        break
3132
                val = RadioSettingValueInteger(0, 65535, tmp)
3133
                frq = RadioSetting("2tone_dec_" + str(i) +
3134
                                   "_freq" + str(char),
3135
                                   ("Decode Frequency " + str(char)), val)
3136
                val.set_validate_callback(_2tone_validate)
3137
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
3138
                _2tone_dec_code.append(frq)
3139

    
3140
            i = i + 1
3141

    
3142
        return top
3143

    
3144
    def set_settings(self, settings):
3145
        _settings = self._memobj.settings
3146
        for element in settings:
3147
            if not isinstance(element, RadioSetting):
3148
                if element.get_name() == "fm_preset":
3149
                    self._set_fm_preset(element)
3150
                else:
3151
                    self.set_settings(element)
3152
                    continue
3153
            else:
3154
                try:
3155
                    name = element.get_name()
3156
                    if "." in name:
3157
                        bits = name.split(".")
3158
                        obj = self._memobj
3159
                        for bit in bits[:-1]:
3160
                            if "/" in bit:
3161
                                bit, index = bit.split("/", 1)
3162
                                index = int(index)
3163
                                obj = getattr(obj, bit)[index]
3164
                            else:
3165
                                obj = getattr(obj, bit)
3166
                        setting = bits[-1]
3167
                    else:
3168
                        obj = _settings
3169
                        setting = element.get_name()
3170

    
3171
                    if element.has_apply_callback():
3172
                        LOG.debug("Using apply callback")
3173
                        element.run_apply_callback()
3174
                    elif setting == "volume" and self.MODEL == "WP-9900":
3175
                        setattr(obj, setting, int(element.value) - 1)
3176
                    elif element.value.get_mutable():
3177
                        LOG.debug("Setting %s = %s" % (setting, element.value))
3178
                        setattr(obj, setting, element.value)
3179
                except Exception, e:
3180
                    LOG.debug(element.get_name())
3181
                    raise
3182

    
3183
    @classmethod
3184
    def match_model(cls, filedata, filename):
3185
        match_size = False
3186
        match_model = False
3187

    
3188
        # testing the file data size
3189
        if len(filedata) == MEM_SIZE:
3190
            match_size = True
3191

    
3192
        # testing the firmware model fingerprint
3193
        match_model = model_match(cls, filedata)
3194

    
3195
        if match_size and match_model:
3196
            return True
3197
        else:
3198
            return False
3199

    
3200

    
3201
MEM_FORMAT = """
3202
#seekto 0x0000;
3203
struct {
3204
  lbcd rxfreq[4];
3205
  lbcd txfreq[4];
3206
  ul16 rxtone;
3207
  ul16 txtone;
3208
  u8 unknown0:4,
3209
     scode:4;
3210
  u8 unknown1:2,
3211
     spmute:2,
3212
     unknown2:2,
3213
     optsig:2;
3214
  u8 unknown3:3,
3215
     scramble:1,
3216
     unknown4:3,
3217
     power:1;
3218
  u8 unknown5:1,
3219
     wide:1,
3220
     unknown6:2,
3221
     bcl:1,
3222
     add:1,
3223
     pttid:2;
3224
} memory[200];
3225

    
3226
#seekto 0x0E00;
3227
struct {
3228
  u8 tdr;
3229
  u8 unknown1;
3230
  u8 sql;
3231
  u8 unknown2[2];
3232
  u8 tot;
3233
  u8 apo;           // BTech radios use this as the Auto Power Off time
3234
                    // other radios use this as pre-Time Out Alert
3235
  u8 unknown3;
3236
  u8 abr;
3237
  u8 beep;
3238
  u8 unknown4[4];
3239
  u8 dtmfst;
3240
  u8 unknown5[2];
3241
  u8 prisc;
3242
  u8 prich;
3243
  u8 screv;
3244
  u8 unknown6[2];
3245
  u8 pttid;
3246
  u8 pttlt;
3247
  u8 unknown7;
3248
  u8 emctp;
3249
  u8 emcch;
3250
  u8 ringt;
3251
  u8 unknown8;
3252
  u8 camdf;
3253
  u8 cbmdf;
3254
  u8 sync;          // BTech radios use this as the display sync setting
3255
                    // other radios use this as the auto keypad lock setting
3256
  u8 ponmsg;
3257
  u8 wtled;
3258
  u8 rxled;
3259
  u8 txled;
3260
  u8 unknown9[5];
3261
  u8 anil;
3262
  u8 reps;
3263
  u8 repm;
3264
  u8 tdrab;
3265
  u8 ste;
3266
  u8 rpste;
3267
  u8 rptdl;
3268
  u8 mgain;
3269
  u8 dtmfg;
3270
} settings;
3271

    
3272
#seekto 0x0E80;
3273
struct {
3274
  u8 unknown1;
3275
  u8 vfomr;
3276
  u8 keylock;
3277
  u8 unknown2;
3278
  u8 unknown3:4,
3279
     vfomren:1,
3280
     unknown4:1,
3281
     reseten:1,
3282
     menuen:1;
3283
  u8 unknown5[11];
3284
  u8 dispab;
3285
  u8 mrcha;
3286
  u8 mrchb;
3287
  u8 menu;
3288
} settings2;
3289

    
3290
#seekto 0x0EC0;
3291
struct {
3292
  char line1[6];
3293
  char line2[6];
3294
} poweron_msg;
3295

    
3296
struct settings_vfo {
3297
  u8 freq[8];
3298
  u8 offset[6];
3299
  u8 unknown2[2];
3300
  ul16 rxtone;
3301
  ul16 txtone;
3302
  u8 scode;
3303
  u8 spmute;
3304
  u8 optsig;
3305
  u8 scramble;
3306
  u8 wide;
3307
  u8 power;
3308
  u8 shiftd;
3309
  u8 step;
3310
  u8 unknown3[4];
3311
};
3312

    
3313
#seekto 0x0F00;
3314
struct {
3315
  struct settings_vfo a;
3316
  struct settings_vfo b;
3317
} vfo;
3318

    
3319
#seekto 0x1000;
3320
struct {
3321
  char name[6];
3322
  u8 unknown1[10];
3323
} names[200];
3324

    
3325
#seekto 0x2400;
3326
struct {
3327
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3328
  u8 group_tone;
3329
  u8 repeat_tone;
3330
  u8 unused[13];
3331
} _5tone_std_settings[15];
3332

    
3333
#seekto 0x2500;
3334
struct {
3335
  u8 frame1[5];
3336
  u8 frame2[5];
3337
  u8 frame3[5];
3338
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3339
} _5tone_codes[15];
3340

    
3341
#seekto 0x25F0;
3342
struct {
3343
  u8 _5tone_delay1; // * 10ms
3344
  u8 _5tone_delay2; // * 10ms
3345
  u8 _5tone_delay3; // * 10ms
3346
  u8 _5tone_first_digit_ext_length;
3347
  u8 unknown1;
3348
  u8 unknown2;
3349
  u8 unknown3;
3350
  u8 unknown4;
3351
  u8 decode_standard;
3352
  u8 unknown5:5,
3353
     _5tone_decode_call_frame3:1,
3354
     _5tone_decode_call_frame2:1,
3355
     _5tone_decode_call_frame1:1;
3356
  u8 unknown6:5,
3357
     _5tone_decode_disp_frame3:1,
3358
     _5tone_decode_disp_frame2:1,
3359
     _5tone_decode_disp_frame1:1;
3360
  u8 decode_reset_time; // * 100 + 100ms
3361
} _5tone_settings;
3362

    
3363
#seekto 0x2900;
3364
struct {
3365
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3366
} dtmf_codes[15];
3367

    
3368
#seekto 0x29F0;
3369
struct {
3370
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3371
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3372
  u8 unknown0[14];
3373
  u8 inspection[16];
3374
  u8 monitor[16];
3375
  u8 alarmcode[16];
3376
  u8 stun[16];
3377
  u8 kill[16];
3378
  u8 revive[16];
3379
  u8 unknown1[16];
3380
  u8 unknown2[16];
3381
  u8 unknown3[16];
3382
  u8 unknown4[16];
3383
  u8 unknown5[16];
3384
  u8 unknown6[16];
3385
  u8 unknown7[16];
3386
  u8 masterid[16];
3387
  u8 viceid[16];
3388
  u8 unused01:7,
3389
     mastervice:1;
3390
  u8 unused02:3,
3391
     mrevive:1,
3392
     mkill:1,
3393
     mstun:1,
3394
     mmonitor:1,
3395
     minspection:1;
3396
  u8 unused03:3,
3397
     vrevive:1,
3398
     vkill:1,
3399
     vstun:1,
3400
     vmonitor:1,
3401
     vinspection:1;
3402
  u8 unused04:6,
3403
     txdisable:1,
3404
     rxdisable:1;
3405
  u8 groupcode;
3406
  u8 spacecode;
3407
  u8 delayproctime; // * 100 + 100ms
3408
  u8 resettime;     // * 100 + 100ms
3409
} dtmf_settings;
3410

    
3411
#seekto 0x2D00;
3412
struct {
3413
  struct {
3414
    ul16 freq1;
3415
    u8 unused01[6];
3416
    ul16 freq2;
3417
    u8 unused02[6];
3418
  } _2tone_encode[15];
3419
  u8 duration_1st_tone; // *10ms
3420
  u8 duration_2nd_tone; // *10ms
3421
  u8 duration_gap;      // *10ms
3422
  u8 unused03[13];
3423
  struct {
3424
    struct {
3425
      u8 dec;      // one out of LIST_2TONE_DEC
3426
      u8 response; // one out of LIST_2TONE_RESPONSE
3427
      u8 alert;    // 1-16
3428
    } decs[4];
3429
    u8 unused04[4];
3430
  } _2tone_decode[15];
3431
  u8 unused05[16];
3432

    
3433
  struct {
3434
    ul16 freqA;
3435
    ul16 freqB;
3436
    ul16 freqC;
3437
    ul16 freqD;
3438
    // unknown what those values mean, but they are
3439
    // derived from configured frequencies
3440
    ul16 derived_from_freqA; // 2304000/freqA
3441
    ul16 derived_from_freqB; // 2304000/freqB
3442
    ul16 derived_from_freqC; // 2304000/freqC
3443
    ul16 derived_from_freqD; // 2304000/freqD
3444
  }freqs[15];
3445
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3446
} _2tone;
3447

    
3448
#seekto 0x3000;
3449
struct {
3450
  u8 freq[8];
3451
  char broadcast_station_name[6];
3452
  u8 unknown[2];
3453
} fm_radio_preset[16];
3454

    
3455
#seekto 0x3C90;
3456
struct {
3457
  u8 vhf_low[3];
3458
  u8 vhf_high[3];
3459
  u8 uhf_low[3];
3460
  u8 uhf_high[3];
3461
} ranges;
3462

    
3463
// the UV-2501+220 & KT8900R has different zones for storing ranges
3464

    
3465
#seekto 0x3CD0;
3466
struct {
3467
  u8 vhf_low[3];
3468
  u8 vhf_high[3];
3469
  u8 unknown1[4];
3470
  u8 unknown2[6];
3471
  u8 vhf2_low[3];
3472
  u8 vhf2_high[3];
3473
  u8 unknown3[4];
3474
  u8 unknown4[6];
3475
  u8 uhf_low[3];
3476
  u8 uhf_high[3];
3477
} ranges220;
3478

    
3479
#seekto 0x3F70;
3480
struct {
3481
  char fp[6];
3482
} fingerprint;
3483

    
3484
"""
3485

    
3486

    
3487
class BTech(BTechMobileCommon):
3488
    """BTECH's UV-5001 and alike radios"""
3489
    BANDS = 2
3490
    COLOR_LCD = False
3491
    NAME_LENGTH = 6
3492

    
3493
    def set_options(self):
3494
        """This is to read the options from the image and set it in the
3495
        environment, for now just the limits of the freqs in the VHF/UHF
3496
        ranges"""
3497

    
3498
        # setting the correct ranges for each radio type
3499
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3500
            # the model 2501+220 has a segment in 220
3501
            # and a different position in the memmap
3502
            # also the QYT KT8900R
3503
            ranges = self._memobj.ranges220
3504
        else:
3505
            ranges = self._memobj.ranges
3506

    
3507
        # the normal dual bands
3508
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3509
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3510

    
3511
        # DEBUG
3512
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3513
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3514

    
3515
        # 220Mhz radios case
3516
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3517
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3518
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3519
            self._220_range = vhf2
3520

    
3521
        # set the class with the real data
3522
        self._vhf_range = vhf
3523
        self._uhf_range = uhf
3524

    
3525
    def process_mmap(self):
3526
        """Process the mem map into the mem object"""
3527

    
3528
        # Get it
3529
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3530

    
3531
        # load specific parameters from the radio image
3532
        self.set_options()
3533

    
3534

    
3535
# Declaring Aliases (Clones of the real radios)
3536
class JT2705M(chirp_common.Alias):
3537
    VENDOR = "Jetstream"
3538
    MODEL = "JT2705M"
3539

    
3540

    
3541
class JT6188Mini(chirp_common.Alias):
3542
    VENDOR = "Juentai"
3543
    MODEL = "JT-6188 Mini"
3544

    
3545

    
3546
class JT6188Plus(chirp_common.Alias):
3547
    VENDOR = "Juentai"
3548
    MODEL = "JT-6188 Plus"
3549

    
3550

    
3551
class SSGT890(chirp_common.Alias):
3552
    VENDOR = "Sainsonic"
3553
    MODEL = "GT-890"
3554

    
3555

    
3556
class ZastoneMP300(chirp_common.Alias):
3557
    VENDOR = "Zastone"
3558
    MODEL = "MP-300"
3559

    
3560

    
3561
# real radios
3562
@directory.register
3563
class UV2501(BTech):
3564
    """Baofeng Tech UV2501"""
3565
    MODEL = "UV-2501"
3566
    _fileid = [UV2501G3_fp,
3567
               UV2501G2_fp,
3568
               UV2501pp2_fp,
3569
               UV2501pp_fp]
3570

    
3571

    
3572
@directory.register
3573
class UV2501_220(BTech):
3574
    """Baofeng Tech UV2501+220"""
3575
    MODEL = "UV-2501+220"
3576
    BANDS = 3
3577
    _magic = MSTRING_220
3578
    _fileid = [UV2501_220G3_fp,
3579
               UV2501_220G2_fp,
3580
               UV2501_220_fp,
3581
               UV2501_220pp_fp]
3582

    
3583

    
3584
@directory.register
3585
class UV5001(BTech):
3586
    """Baofeng Tech UV5001"""
3587
    MODEL = "UV-5001"
3588
    _fileid = [UV5001G3_fp,
3589
               UV5001G22_fp,
3590
               UV5001G2_fp,
3591
               UV5001alpha_fp,
3592
               UV5001pp_fp]
3593
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3594
                     chirp_common.PowerLevel("Low", watts=10)]
3595

    
3596

    
3597
@directory.register
3598
class MINI8900(BTech):
3599
    """WACCOM MINI-8900"""
3600
    VENDOR = "WACCOM"
3601
    MODEL = "MINI-8900"
3602
    _magic = MSTRING_MINI8900
3603
    _fileid = [MINI8900_fp, ]
3604
    # Clones
3605
    ALIASES = [JT6188Plus, ]
3606

    
3607

    
3608
@directory.register
3609
class KTUV980(BTech):
3610
    """QYT KT-UV980"""
3611
    VENDOR = "QYT"
3612
    MODEL = "KT-UV980"
3613
    _vhf_range = (136000000, 175000000)
3614
    _uhf_range = (400000000, 481000000)
3615
    _magic = MSTRING_MINI8900
3616
    _fileid = [KTUV980_fp, ]
3617
    # Clones
3618
    ALIASES = [JT2705M, ]
3619

    
3620
# Please note that there is a version of this radios that is a clone of the
3621
# Waccom Mini8900, maybe an early version?
3622

    
3623

    
3624
class OTGRadioV1(chirp_common.Alias):
3625
    VENDOR = 'OTGSTUFF'
3626
    MODEL = 'OTG Radio v1'
3627

    
3628

    
3629
@directory.register
3630
class KT9800(BTech):
3631
    """QYT KT8900"""
3632
    VENDOR = "QYT"
3633
    MODEL = "KT8900"
3634
    _vhf_range = (136000000, 175000000)
3635
    _uhf_range = (400000000, 481000000)
3636
    _magic = MSTRING_KT8900
3637
    _fileid = [KT8900_fp,
3638
               KT8900_fp1,
3639
               KT8900_fp2,
3640
               KT8900_fp3,
3641
               KT8900_fp4,
3642
               KT8900_fp5,
3643
               KT8900_fp6,
3644
               KT8900_fp7]
3645
    # Clones
3646
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
3647

    
3648

    
3649
@directory.register
3650
class KT9800R(BTech):
3651
    """QYT KT8900R"""
3652
    VENDOR = "QYT"
3653
    MODEL = "KT8900R"
3654
    BANDS = 3
3655
    _vhf_range = (136000000, 175000000)
3656
    _220_range = (240000000, 271000000)
3657
    _uhf_range = (400000000, 481000000)
3658
    _magic = MSTRING_KT8900R
3659
    _fileid = [KT8900R_fp,
3660
               KT8900R_fp1,
3661
               KT8900R_fp2,
3662
               KT8900R_fp3,
3663
               KT8900R_fp4,
3664
               KT8900R_fp5]
3665

    
3666

    
3667
@directory.register
3668
class LT588UV(BTech):
3669
    """LUITON LT-588UV"""
3670
    VENDOR = "LUITON"
3671
    MODEL = "LT-588UV"
3672
    _vhf_range = (136000000, 175000000)
3673
    _uhf_range = (400000000, 481000000)
3674
    _magic = MSTRING_KT8900
3675
    _fileid = [LT588UV_fp,
3676
               LT588UV_fp1]
3677
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
3678
                     chirp_common.PowerLevel("Low", watts=10)]
3679

    
3680

    
3681
COLOR_MEM_FORMAT = """
3682
#seekto 0x0000;
3683
struct {
3684
  lbcd rxfreq[4];
3685
  lbcd txfreq[4];
3686
  ul16 rxtone;
3687
  ul16 txtone;
3688
  u8 unknown0:4,
3689
     scode:4;
3690
  u8 unknown1:2,
3691
     spmute:2,
3692
     unknown2:2,
3693
     optsig:2;
3694
  u8 unknown3:3,
3695
     scramble:1,
3696
     unknown4:2,
3697
     power:2;
3698
  u8 unknown5:1,
3699
     wide:1,
3700
     unknown6:2,
3701
     bcl:1,
3702
     add:1,
3703
     pttid:2;
3704
} memory[200];
3705

    
3706
#seekto 0x0E00;
3707
struct {
3708
  u8 tmr;
3709
  u8 unknown1;
3710
  u8 sql;
3711
  u8 unknown2;
3712
  u8 mgain2;
3713
  u8 tot;
3714
  u8 apo;
3715
  u8 unknown3;
3716
  u8 abr;
3717
  u8 beep;
3718
  u8 unknown4[4];
3719
  u8 dtmfst;
3720
  u8 unknown5[2];
3721
  u8 screv;
3722
  u8 unknown6[2];
3723
  u8 pttid;
3724
  u8 pttlt;
3725
  u8 unknown7;
3726
  u8 emctp;
3727
  u8 emcch;
3728
  u8 sigbp;
3729
  u8 unknown8;
3730
  u8 camdf;
3731
  u8 cbmdf;
3732
  u8 ccmdf;
3733
  u8 cdmdf;
3734
  u8 langua;
3735
  u8 sync;          // BTech radios use this as the display sync
3736
                    // setting, other radios use this as the auto
3737
                    // keypad lock setting
3738
  u8 mainfc;
3739
  u8 mainbc;
3740
  u8 menufc;
3741
  u8 menubc;
3742
  u8 stafc;
3743
  u8 stabc;
3744
  u8 sigfc;
3745
  u8 sigbc;
3746
  u8 rxfc;
3747
  u8 txfc;
3748
  u8 txdisp;
3749
  u8 unknown9[5];
3750
  u8 anil;
3751
  u8 reps;
3752
  u8 repm;
3753
  u8 tmrmr;
3754
  u8 ste;
3755
  u8 rpste;
3756
  u8 rptdl;
3757
  u8 dtmfg;
3758
  u8 mgain;         // used by db25-g for ponyey
3759
  u8 skiptx;
3760
  u8 scmode;
3761
} settings;
3762

    
3763
#seekto 0x0E80;
3764
struct {
3765
  u8 unknown1;
3766
  u8 vfomr;
3767
  u8 keylock;
3768
  u8 unknown2;
3769
  u8 unknown3:4,
3770
     vfomren:1,
3771
     unknown4:1,
3772
     reseten:1,
3773
     menuen:1;
3774
  u8 unknown5[11];
3775
  u8 dispab;
3776
  u8 unknown6[2];
3777
  u8 menu;
3778
  u8 unknown7[7];
3779
  u8 vfomra;
3780
  u8 vfomrb;
3781
  u8 vfomrc;
3782
  u8 vfomrd;
3783
  u8 mrcha;
3784
  u8 mrchb;
3785
  u8 mrchc;
3786
  u8 mrchd;
3787
} settings2;
3788

    
3789
struct settings_vfo {
3790
  u8 freq[8];
3791
  u8 offset[6];
3792
  u8 unknown2[2];
3793
  ul16 rxtone;
3794
  ul16 txtone;
3795
  u8 scode;
3796
  u8 spmute;
3797
  u8 optsig;
3798
  u8 scramble;
3799
  u8 wide;
3800
  u8 power;
3801
  u8 shiftd;
3802
  u8 step;
3803
  u8 unknown3[4];
3804
};
3805

    
3806
#seekto 0x0F00;
3807
struct {
3808
  struct settings_vfo a;
3809
  struct settings_vfo b;
3810
  struct settings_vfo c;
3811
  struct settings_vfo d;
3812
} vfo;
3813

    
3814
#seekto 0x0F80;
3815
struct {
3816
  char line1[8];
3817
  char line2[8];
3818
  char line3[8];
3819
  char line4[8];
3820
  char line5[8];
3821
  char line6[8];
3822
  char line7[8];
3823
  char line8[8];
3824
} poweron_msg;
3825

    
3826
#seekto 0x1000;
3827
struct {
3828
  char name[8];
3829
  u8 unknown1[8];
3830
} names[200];
3831

    
3832
#seekto 0x2400;
3833
struct {
3834
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3835
  u8 group_tone;
3836
  u8 repeat_tone;
3837
  u8 unused[13];
3838
} _5tone_std_settings[15];
3839

    
3840
#seekto 0x2500;
3841
struct {
3842
  u8 frame1[5];
3843
  u8 frame2[5];
3844
  u8 frame3[5];
3845
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3846
} _5tone_codes[15];
3847

    
3848
#seekto 0x25F0;
3849
struct {
3850
  u8 _5tone_delay1; // * 10ms
3851
  u8 _5tone_delay2; // * 10ms
3852
  u8 _5tone_delay3; // * 10ms
3853
  u8 _5tone_first_digit_ext_length;
3854
  u8 unknown1;
3855
  u8 unknown2;
3856
  u8 unknown3;
3857
  u8 unknown4;
3858
  u8 decode_standard;
3859
  u8 unknown5:5,
3860
     _5tone_decode_call_frame3:1,
3861
     _5tone_decode_call_frame2:1,
3862
     _5tone_decode_call_frame1:1;
3863
  u8 unknown6:5,
3864
     _5tone_decode_disp_frame3:1,
3865
     _5tone_decode_disp_frame2:1,
3866
     _5tone_decode_disp_frame1:1;
3867
  u8 decode_reset_time; // * 100 + 100ms
3868
} _5tone_settings;
3869

    
3870
#seekto 0x2900;
3871
struct {
3872
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3873
} dtmf_codes[15];
3874

    
3875
#seekto 0x29F0;
3876
struct {
3877
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3878
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3879
  u8 unknown0[14];
3880
  u8 inspection[16];
3881
  u8 monitor[16];
3882
  u8 alarmcode[16];
3883
  u8 stun[16];
3884
  u8 kill[16];
3885
  u8 revive[16];
3886
  u8 unknown1[16];
3887
  u8 unknown2[16];
3888
  u8 unknown3[16];
3889
  u8 unknown4[16];
3890
  u8 unknown5[16];
3891
  u8 unknown6[16];
3892
  u8 unknown7[16];
3893
  u8 masterid[16];
3894
  u8 viceid[16];
3895
  u8 unused01:7,
3896
     mastervice:1;
3897
  u8 unused02:3,
3898
     mrevive:1,
3899
     mkill:1,
3900
     mstun:1,
3901
     mmonitor:1,
3902
     minspection:1;
3903
  u8 unused03:3,
3904
     vrevive:1,
3905
     vkill:1,
3906
     vstun:1,
3907
     vmonitor:1,
3908
     vinspection:1;
3909
  u8 unused04:6,
3910
     txdisable:1,
3911
     rxdisable:1;
3912
  u8 groupcode;
3913
  u8 spacecode;
3914
  u8 delayproctime; // * 100 + 100ms
3915
  u8 resettime;     // * 100 + 100ms
3916
} dtmf_settings;
3917

    
3918
#seekto 0x2D00;
3919
struct {
3920
  struct {
3921
    ul16 freq1;
3922
    u8 unused01[6];
3923
    ul16 freq2;
3924
    u8 unused02[6];
3925
  } _2tone_encode[15];
3926
  u8 duration_1st_tone; // *10ms
3927
  u8 duration_2nd_tone; // *10ms
3928
  u8 duration_gap;      // *10ms
3929
  u8 unused03[13];
3930
  struct {
3931
    struct {
3932
      u8 dec;      // one out of LIST_2TONE_DEC
3933
      u8 response; // one out of LIST_2TONE_RESPONSE
3934
      u8 alert;    // 1-16
3935
    } decs[4];
3936
    u8 unused04[4];
3937
  } _2tone_decode[15];
3938
  u8 unused05[16];
3939

    
3940
  struct {
3941
    ul16 freqA;
3942
    ul16 freqB;
3943
    ul16 freqC;
3944
    ul16 freqD;
3945
    // unknown what those values mean, but they are
3946
    // derived from configured frequencies
3947
    ul16 derived_from_freqA; // 2304000/freqA
3948
    ul16 derived_from_freqB; // 2304000/freqB
3949
    ul16 derived_from_freqC; // 2304000/freqC
3950
    ul16 derived_from_freqD; // 2304000/freqD
3951
  }freqs[15];
3952
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3953
} _2tone;
3954

    
3955
#seekto 0x3D80;
3956
struct {
3957
  u8 vhf_low[3];
3958
  u8 vhf_high[3];
3959
  u8 unknown1[4];
3960
  u8 unknown2[6];
3961
  u8 vhf2_low[3];
3962
  u8 vhf2_high[3];
3963
  u8 unknown3[4];
3964
  u8 unknown4[6];
3965
  u8 uhf_low[3];
3966
  u8 uhf_high[3];
3967
  u8 unknown5[4];
3968
  u8 unknown6[6];
3969
  u8 uhf2_low[3];
3970
  u8 uhf2_high[3];
3971
} ranges;
3972

    
3973
#seekto 0x3F70;
3974
struct {
3975
  char fp[6];
3976
} fingerprint;
3977

    
3978
"""
3979

    
3980

    
3981
class BTechColor(BTechMobileCommon):
3982
    """BTECH's Color LCD Mobile and alike radios"""
3983
    COLOR_LCD = True
3984
    NAME_LENGTH = 8
3985
    LIST_TMR = LIST_TMR16
3986

    
3987
    def process_mmap(self):
3988
        """Process the mem map into the mem object"""
3989

    
3990
        # Get it
3991
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3992

    
3993
        # load specific parameters from the radio image
3994
        self.set_options()
3995

    
3996
    def set_options(self):
3997
        """This is to read the options from the image and set it in the
3998
        environment, for now just the limits of the freqs in the VHF/UHF
3999
        ranges"""
4000

    
4001
        # setting the correct ranges for each radio type
4002
        ranges = self._memobj.ranges
4003

    
4004
        # the normal dual bands
4005
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4006
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4007

    
4008
        # DEBUG
4009
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4010
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4011

    
4012
        # the additional bands
4013
        if self.MODEL in ["UV-25X4", "KT7900D"]:
4014
            # 200Mhz band
4015
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4016
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4017
            self._220_range = vhf2
4018

    
4019
            # 350Mhz band
4020
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4021
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4022
            self._350_range = uhf2
4023

    
4024
        # set the class with the real data
4025
        self._vhf_range = vhf
4026
        self._uhf_range = uhf
4027

    
4028

    
4029
# Declaring Aliases (Clones of the real radios)
4030
class SKT8900D(chirp_common.Alias):
4031
    VENDOR = "Surecom"
4032
    MODEL = "S-KT8900D"
4033

    
4034

    
4035
class QB25(chirp_common.Alias):
4036
    VENDOR = "Radioddity"
4037
    MODEL = "QB25"
4038

    
4039

    
4040
# real radios
4041
@directory.register
4042
class UV25X2(BTechColor):
4043
    """Baofeng Tech UV25X2"""
4044
    MODEL = "UV-25X2"
4045
    BANDS = 2
4046
    _vhf_range = (130000000, 180000000)
4047
    _uhf_range = (400000000, 521000000)
4048
    _magic = MSTRING_UV25X2
4049
    _fileid = [UV25X2_fp, ]
4050

    
4051

    
4052
@directory.register
4053
class UV25X4(BTechColor):
4054
    """Baofeng Tech UV25X4"""
4055
    MODEL = "UV-25X4"
4056
    BANDS = 4
4057
    _vhf_range = (130000000, 180000000)
4058
    _220_range = (200000000, 271000000)
4059
    _uhf_range = (400000000, 521000000)
4060
    _350_range = (350000000, 391000000)
4061
    _magic = MSTRING_UV25X4
4062
    _fileid = [UV25X4_fp, ]
4063

    
4064

    
4065
@directory.register
4066
class UV50X2(BTechColor):
4067
    """Baofeng Tech UV50X2"""
4068
    MODEL = "UV-50X2"
4069
    BANDS = 2
4070
    _vhf_range = (130000000, 180000000)
4071
    _uhf_range = (400000000, 521000000)
4072
    _magic = MSTRING_UV25X2
4073
    _fileid = [UV50X2_fp, ]
4074
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4075
                     chirp_common.PowerLevel("Low", watts=10)]
4076

    
4077

    
4078
@directory.register
4079
class KT7900D(BTechColor):
4080
    """QYT KT7900D"""
4081
    VENDOR = "QYT"
4082
    MODEL = "KT7900D"
4083
    BANDS = 4
4084
    LIST_TMR = LIST_TMR15
4085
    _vhf_range = (136000000, 175000000)
4086
    _220_range = (200000000, 271000000)
4087
    _uhf_range = (400000000, 481000000)
4088
    _350_range = (350000000, 371000000)
4089
    _magic = MSTRING_KT8900D
4090
    _fileid = [KT7900D_fp, KT7900D_fp1, KT7900D_fp2, KT7900D_fp3, KT7900D_fp4,
4091
               KT7900D_fp5, KT7900D_fp6, KT7900D_fp7, QB25_fp, ]
4092
    # Clones
4093
    ALIASES = [SKT8900D, QB25, ]
4094

    
4095

    
4096
@directory.register
4097
class KT8900D(BTechColor):
4098
    """QYT KT8900D"""
4099
    VENDOR = "QYT"
4100
    MODEL = "KT8900D"
4101
    BANDS = 2
4102
    LIST_TMR = LIST_TMR15
4103
    _vhf_range = (136000000, 175000000)
4104
    _uhf_range = (400000000, 481000000)
4105
    _magic = MSTRING_KT8900D
4106
    _fileid = [KT8900D_fp3, KT8900D_fp2, KT8900D_fp1, KT8900D_fp]
4107

    
4108
    # Clones
4109
    ALIASES = [OTGRadioV1]
4110

    
4111

    
4112
@directory.register
4113
class KT5800(BTechColor):
4114
    """QYT KT5800"""
4115
    VENDOR = "QYT"
4116
    MODEL = "KT5800"
4117
    BANDS = 2
4118
    LIST_TMR = LIST_TMR15
4119
    _vhf_range = (136000000, 175000000)
4120
    _uhf_range = (400000000, 481000000)
4121
    _magic = MSTRING_KT8900D
4122
    _fileid = [KT5800_fp, ]
4123

    
4124

    
4125
@directory.register
4126
class KT980PLUS(BTechColor):
4127
    """QYT KT980PLUS"""
4128
    VENDOR = "QYT"
4129
    MODEL = "KT980PLUS"
4130
    BANDS = 2
4131
    LIST_TMR = LIST_TMR15
4132
    _vhf_range = (136000000, 175000000)
4133
    _uhf_range = (400000000, 481000000)
4134
    _magic = MSTRING_KT8900D
4135
    _fileid = [KT980PLUS_fp1, KT980PLUS_fp]
4136
    _power_levels = [chirp_common.PowerLevel("High", watts=75),
4137
                     chirp_common.PowerLevel("Low", watts=55)]
4138

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

    
4144

    
4145
@directory.register
4146
class DB25G(BTechColor):
4147
    """Radioddity DB25-G"""
4148
    VENDOR = "Radioddity"
4149
    MODEL = "DB25-G"
4150
    BANDS = 2
4151
    LIST_TMR = LIST_TMR15
4152
    _vhf_range = (136000000, 175000000)
4153
    _uhf_range = (400000000, 481000000)
4154
    _magic = MSTRING_KT8900D
4155
    _fileid = [DB25G_fp1, DB25G_fp]
4156
    _gmrs = True
4157
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
4158
                     chirp_common.PowerLevel("Mid", watts=15),
4159
                     chirp_common.PowerLevel("Low", watts=5)]
4160

    
4161
    @classmethod
4162
    def match_model(cls, filedata, filename):
4163
        # This model is only ever matched via metadata
4164
        return False
4165

    
4166

    
4167
GMRS_MEM_FORMAT = """
4168
#seekto 0x0000;
4169
struct {
4170
  lbcd rxfreq[4];
4171
  lbcd txfreq[4];
4172
  ul16 rxtone;
4173
  ul16 txtone;
4174
  u8 unknown0:4,
4175
     scode:4;
4176
  u8 unknown1:2,
4177
     spmute:2,
4178
     unknown2:2,
4179
     optsig:2;
4180
  u8 unknown3:3,
4181
     scramble:1,
4182
     unknown4:2,
4183
     power:2;
4184
  u8 unknown5:1,
4185
     wide:1,
4186
     unknown6:2,
4187
     bcl:1,
4188
     add:1,
4189
     pttid:2;
4190
} memory[256];
4191

    
4192
#seekto 0x1000;
4193
struct {
4194
  char name[7];
4195
  u8 unknown1[9];
4196
} names[256];
4197

    
4198
#seekto 0x2400;
4199
struct {
4200
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4201
  u8 group_tone;
4202
  u8 repeat_tone;
4203
  u8 unused[13];
4204
} _5tone_std_settings[15];
4205

    
4206
#seekto 0x2500;
4207
struct {
4208
  u8 frame1[5];
4209
  u8 frame2[5];
4210
  u8 frame3[5];
4211
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4212
} _5tone_codes[15];
4213

    
4214
#seekto 0x25F0;
4215
struct {
4216
  u8 _5tone_delay1; // * 10ms
4217
  u8 _5tone_delay2; // * 10ms
4218
  u8 _5tone_delay3; // * 10ms
4219
  u8 _5tone_first_digit_ext_length;
4220
  u8 unknown1;
4221
  u8 unknown2;
4222
  u8 unknown3;
4223
  u8 unknown4;
4224
  u8 decode_standard;
4225
  u8 unknown5:5,
4226
     _5tone_decode_call_frame3:1,
4227
     _5tone_decode_call_frame2:1,
4228
     _5tone_decode_call_frame1:1;
4229
  u8 unknown6:5,
4230
     _5tone_decode_disp_frame3:1,
4231
     _5tone_decode_disp_frame2:1,
4232
     _5tone_decode_disp_frame1:1;
4233
  u8 decode_reset_time; // * 100 + 100ms
4234
} _5tone_settings;
4235

    
4236
#seekto 0x2900;
4237
struct {
4238
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4239
} dtmf_codes[15];
4240

    
4241
#seekto 0x29F0;
4242
struct {
4243
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4244
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4245
  u8 unknown0[14];
4246
  u8 inspection[16];
4247
  u8 monitor[16];
4248
  u8 alarmcode[16];
4249
  u8 stun[16];
4250
  u8 kill[16];
4251
  u8 revive[16];
4252
  u8 unknown1[16];
4253
  u8 unknown2[16];
4254
  u8 unknown3[16];
4255
  u8 unknown4[16];
4256
  u8 unknown5[16];
4257
  u8 unknown6[16];
4258
  u8 unknown7[16];
4259
  u8 masterid[16];
4260
  u8 viceid[16];
4261
  u8 unused01:7,
4262
     mastervice:1;
4263
  u8 unused02:3,
4264
     mrevive:1,
4265
     mkill:1,
4266
     mstun:1,
4267
     mmonitor:1,
4268
     minspection:1;
4269
  u8 unused03:3,
4270
     vrevive:1,
4271
     vkill:1,
4272
     vstun:1,
4273
     vmonitor:1,
4274
     vinspection:1;
4275
  u8 unused04:6,
4276
     txdisable:1,
4277
     rxdisable:1;
4278
  u8 groupcode;
4279
  u8 spacecode;
4280
  u8 delayproctime; // * 100 + 100ms
4281
  u8 resettime;     // * 100 + 100ms
4282
} dtmf_settings;
4283

    
4284
#seekto 0x2D00;
4285
struct {
4286
  struct {
4287
    ul16 freq1;
4288
    u8 unused01[6];
4289
    ul16 freq2;
4290
    u8 unused02[6];
4291
  } _2tone_encode[15];
4292
  u8 duration_1st_tone; // *10ms
4293
  u8 duration_2nd_tone; // *10ms
4294
  u8 duration_gap;      // *10ms
4295
  u8 unused03[13];
4296
  struct {
4297
    struct {
4298
      u8 dec;      // one out of LIST_2TONE_DEC
4299
      u8 response; // one out of LIST_2TONE_RESPONSE
4300
      u8 alert;    // 1-16
4301
    } decs[4];
4302
    u8 unused04[4];
4303
  } _2tone_decode[15];
4304
  u8 unused05[16];
4305

    
4306
  struct {
4307
    ul16 freqA;
4308
    ul16 freqB;
4309
    ul16 freqC;
4310
    ul16 freqD;
4311
    // unknown what those values mean, but they are
4312
    // derived from configured frequencies
4313
    ul16 derived_from_freqA; // 2304000/freqA
4314
    ul16 derived_from_freqB; // 2304000/freqB
4315
    ul16 derived_from_freqC; // 2304000/freqC
4316
    ul16 derived_from_freqD; // 2304000/freqD
4317
  }freqs[15];
4318
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4319
} _2tone;
4320

    
4321
#seekto 0x3000;
4322
struct {
4323
  u8 freq[8];
4324
  char broadcast_station_name[6];
4325
  u8 unknown[2];
4326
} fm_radio_preset[16];
4327

    
4328
#seekto 0x3200;
4329
struct {
4330
  u8 tmr;
4331
  u8 unknown1;
4332
  u8 sql;
4333
  u8 unknown2;
4334
  u8 autolk;
4335
  u8 tot;
4336
  u8 apo;
4337
  u8 unknown3;
4338
  u8 abr;
4339
  u8 beep;
4340
  u8 unknown4[4];
4341
  u8 dtmfst;
4342
  u8 unknown5[2];
4343
  u8 screv;
4344
  u8 unknown6[2];
4345
  u8 pttid;
4346
  u8 pttlt;
4347
  u8 unknown7;
4348
  u8 emctp;
4349
  u8 emcch;
4350
  u8 sigbp;
4351
  u8 unknown8;
4352
  u8 camdf;
4353
  u8 cbmdf;
4354
  u8 ccmdf;
4355
  u8 cdmdf;
4356
  u8 langua;
4357
  u8 sync;
4358

    
4359

    
4360
  u8 stfc;
4361
  u8 mffc;
4362
  u8 sfafc;
4363
  u8 sfbfc;
4364
  u8 sfcfc;
4365
  u8 sfdfc;
4366
  u8 subfc;
4367
  u8 fmfc;
4368
  u8 sigfc;
4369
  u8 modfc;
4370
  u8 menufc;
4371
  u8 txfc;
4372
  u8 txdisp;
4373
  u8 unknown9[5];
4374
  u8 anil;
4375
  u8 reps;
4376
  u8 repm;
4377
  u8 tmrmr;
4378
  u8 ste;
4379
  u8 rpste;
4380
  u8 rptdl;
4381
  u8 dtmfg;
4382
  u8 mgain;
4383
  u8 skiptx;
4384
  u8 scmode;
4385
} settings;
4386

    
4387
#seekto 0x3280;
4388
struct {
4389
  u8 unknown1;
4390
  u8 vfomr;
4391
  u8 keylock;
4392
  u8 unknown2;
4393
  u8 unknown3:4,
4394
     vfomren:1,
4395
     unknown4:1,
4396
     reseten:1,
4397
     menuen:1;
4398
  u8 unknown5[11];
4399
  u8 dispab;
4400
  u8 unknown6[2];
4401
  u8 smenu;
4402
  u8 unknown7[7];
4403
  u8 vfomra;
4404
  u8 vfomrb;
4405
  u8 vfomrc;
4406
  u8 vfomrd;
4407
  u8 mrcha;
4408
  u8 mrchb;
4409
  u8 mrchc;
4410
  u8 mrchd;
4411
} settings2;
4412

    
4413
struct settings_vfo {
4414
  u8 freq[8];
4415
  u8 offset[6];
4416
  u8 unknown2[2];
4417
  ul16 rxtone;
4418
  ul16 txtone;
4419
  u8 scode;
4420
  u8 spmute;
4421
  u8 optsig;
4422
  u8 scramble;
4423
  u8 wide;
4424
  u8 power;
4425
  u8 shiftd;
4426
  u8 step;
4427
  u8 unknown3[4];
4428
};
4429

    
4430
#seekto 0x3300;
4431
struct {
4432
  struct settings_vfo a;
4433
  struct settings_vfo b;
4434
  struct settings_vfo c;
4435
  struct settings_vfo d;
4436
} vfo;
4437

    
4438
#seekto 0x3D80;
4439
struct {
4440
  u8 vhf_low[3];
4441
  u8 vhf_high[3];
4442
  u8 unknown1[4];
4443
  u8 unknown2[6];
4444
  u8 vhf2_low[3];
4445
  u8 vhf2_high[3];
4446
  u8 unknown3[4];
4447
  u8 unknown4[6];
4448
  u8 uhf_low[3];
4449
  u8 uhf_high[3];
4450
  u8 unknown5[4];
4451
  u8 unknown6[6];
4452
  u8 uhf2_low[3];
4453
  u8 uhf2_high[3];
4454
} ranges;
4455

    
4456
#seekto 0x33B0;
4457
struct {
4458
  char line[16];
4459
} static_msg;
4460

    
4461
#seekto 0x3F70;
4462
struct {
4463
  char fp[6];
4464
} fingerprint;
4465

    
4466
"""
4467

    
4468

    
4469
class BTechGMRS(BTechMobileCommon):
4470
    """BTECH's GMRS Mobile"""
4471
    COLOR_LCD = True
4472
    COLOR_LCD2 = True
4473
    NAME_LENGTH = 7
4474
    UPLOAD_MEM_SIZE = 0X3400
4475

    
4476
    def process_mmap(self):
4477
        """Process the mem map into the mem object"""
4478

    
4479
        # Get it
4480
        self._memobj = bitwise.parse(GMRS_MEM_FORMAT, self._mmap)
4481

    
4482
        # load specific parameters from the radio image
4483
        self.set_options()
4484

    
4485
    def set_options(self):
4486
        """This is to read the options from the image and set it in the
4487
        environment, for now just the limits of the freqs in the VHF/UHF
4488
        ranges"""
4489

    
4490
        # setting the correct ranges for each radio type
4491
        ranges = self._memobj.ranges
4492

    
4493
        # the normal dual bands
4494
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4495
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4496

    
4497
        # DEBUG
4498
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4499
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4500

    
4501
        # set the class with the real data
4502
        self._vhf_range = vhf
4503
        self._uhf_range = uhf
4504

    
4505

    
4506
# real radios
4507
@directory.register
4508
class GMRS50X1(BTechGMRS):
4509
    """Baofeng Tech GMRS50X1"""
4510
    MODEL = "GMRS-50X1"
4511
    BANDS = 2
4512
    LIST_TMR = LIST_TMR16
4513
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4514
                     chirp_common.PowerLevel("Mid", watts=10),
4515
                     chirp_common.PowerLevel("Low", watts=5)]
4516
    _vhf_range = (136000000, 175000000)
4517
    _uhf_range = (400000000, 521000000)
4518
    _upper = 255
4519
    _magic = MSTRING_GMRS50X1
4520
    _fileid = [GMRS50X1_fp1, GMRS50X1_fp, ]
4521

    
4522

    
4523
COLORHT_MEM_FORMAT = """
4524
#seekto 0x0000;
4525
struct {
4526
  lbcd rxfreq[4];
4527
  lbcd txfreq[4];
4528
  ul16 rxtone;
4529
  ul16 txtone;
4530
  u8 unknown0:4,
4531
     scode:4;
4532
  u8 unknown1:2,
4533
     spmute:2,
4534
     unknown2:2,
4535
     optsig:2;
4536
  u8 unknown3:3,
4537
     scramble:1,
4538
     unknown4:3,
4539
     power:1;
4540
  u8 unknown5:1,
4541
     wide:1,
4542
     unknown6:2,
4543
     bcl:1,
4544
     add:1,
4545
     pttid:2;
4546
} memory[200];
4547

    
4548
#seekto 0x0E00;
4549
struct {
4550
  u8 tmr;
4551
  u8 unknownE01;
4552
  u8 sql;
4553
  u8 unknownE03[2];
4554
  u8 tot;
4555
  u8 save;
4556
  u8 unknownE07;
4557
  u8 abr;
4558
  u8 beep;
4559
  u8 unknownE0A[4];
4560
  u8 dsub;
4561
  u8 dtmfst;
4562
  u8 screv;
4563
  u8 unknownE11[3];
4564
  u8 pttid;
4565
  u8 unknownE15;
4566
  u8 pttlt;
4567
  u8 unknownE17;
4568
  u8 emctp;
4569
  u8 emcch;
4570
  u8 sigbp;
4571
  u8 unknownE1B;
4572
  u8 camdf;
4573
  u8 cbmdf;
4574
  u8 ccmdf;
4575
  u8 cdmdf;
4576
  u8 langua;
4577
  u8 voice;
4578
  u8 vox;
4579
  u8 voxt;
4580
  u8 sync;          // BTech radios use this as the display sync setting
4581
                    // other radios use this as the auto keypad lock setting
4582
  u8 stfc;
4583
  u8 mffc;
4584
  u8 sfafc;
4585
  u8 sfbfc;
4586
  u8 sfcfc;
4587
  u8 sfdfc;
4588
  u8 subfc;
4589
  u8 fmfc;
4590
  u8 sigfc;
4591
  u8 menufc;
4592
  u8 txfc;
4593
  u8 rxfc;
4594
  u8 unknownE31[5];
4595
  u8 anil;
4596
  u8 reps;
4597
  u8 tmrmr;
4598
  u8 ste;
4599
  u8 rpste;
4600
  u8 rptdl;
4601
  u8 dtmfg;
4602
  u8 tmrtx;
4603
} settings;
4604

    
4605
#seekto 0x0E80;
4606
struct {
4607
  u8 unknown1;
4608
  u8 vfomr;
4609
  u8 keylock;
4610
  u8 unknown2;
4611
  u8 unknown3:4,
4612
     vfomren:1,
4613
     unknown4:1,
4614
     reseten:1,
4615
     menuen:1;
4616
  u8 unknown5[11];
4617
  u8 dispab;
4618
  u8 unknown6[2];
4619
  u8 menu;
4620
  u8 unknown7[7];
4621
  u8 vfomra;
4622
  u8 vfomrb;
4623
  u8 vfomrc;
4624
  u8 vfomrd;
4625
  u8 mrcha;
4626
  u8 mrchb;
4627
  u8 mrchc;
4628
  u8 mrchd;
4629
} settings2;
4630

    
4631
struct settings_vfo {
4632
  u8 freq[8];
4633
  u8 offset[6];
4634
  u8 unknown2[2];
4635
  ul16 rxtone;
4636
  ul16 txtone;
4637
  u8 scode;
4638
  u8 spmute;
4639
  u8 optsig;
4640
  u8 scramble;
4641
  u8 wide;
4642
  u8 power;
4643
  u8 shiftd;
4644
  u8 step;
4645
  u8 unknown3[4];
4646
};
4647

    
4648
#seekto 0x0F00;
4649
struct {
4650
  struct settings_vfo a;
4651
  struct settings_vfo b;
4652
  struct settings_vfo c;
4653
  struct settings_vfo d;
4654
} vfo;
4655

    
4656
#seekto 0x0FE0;
4657
struct {
4658
  char line[16];
4659
} static_msg;
4660

    
4661
#seekto 0x1000;
4662
struct {
4663
  char name[8];
4664
  u8 unknown1[8];
4665
} names[200];
4666

    
4667
#seekto 0x2400;
4668
struct {
4669
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4670
  u8 group_tone;
4671
  u8 repeat_tone;
4672
  u8 unused[13];
4673
} _5tone_std_settings[15];
4674

    
4675
#seekto 0x2500;
4676
struct {
4677
  u8 frame1[5];
4678
  u8 frame2[5];
4679
  u8 frame3[5];
4680
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4681
} _5tone_codes[15];
4682

    
4683
#seekto 0x25F0;
4684
struct {
4685
  u8 _5tone_delay1; // * 10ms
4686
  u8 _5tone_delay2; // * 10ms
4687
  u8 _5tone_delay3; // * 10ms
4688
  u8 _5tone_first_digit_ext_length;
4689
  u8 unknown1;
4690
  u8 unknown2;
4691
  u8 unknown3;
4692
  u8 unknown4;
4693
  u8 decode_standard;
4694
  u8 unknown5:5,
4695
     _5tone_decode_call_frame3:1,
4696
     _5tone_decode_call_frame2:1,
4697
     _5tone_decode_call_frame1:1;
4698
  u8 unknown6:5,
4699
     _5tone_decode_disp_frame3:1,
4700
     _5tone_decode_disp_frame2:1,
4701
     _5tone_decode_disp_frame1:1;
4702
  u8 decode_reset_time; // * 100 + 100ms
4703
} _5tone_settings;
4704

    
4705
#seekto 0x2900;
4706
struct {
4707
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4708
} dtmf_codes[15];
4709

    
4710
#seekto 0x29F0;
4711
struct {
4712
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
4713
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
4714
  u8 unknown0[14];
4715
  u8 inspection[16];
4716
  u8 monitor[16];
4717
  u8 alarmcode[16];
4718
  u8 stun[16];
4719
  u8 kill[16];
4720
  u8 revive[16];
4721
  u8 unknown1[16];
4722
  u8 unknown2[16];
4723
  u8 unknown3[16];
4724
  u8 unknown4[16];
4725
  u8 unknown5[16];
4726
  u8 unknown6[16];
4727
  u8 unknown7[16];
4728
  u8 masterid[16];
4729
  u8 viceid[16];
4730
  u8 unused01:7,
4731
     mastervice:1;
4732
  u8 unused02:3,
4733
     mrevive:1,
4734
     mkill:1,
4735
     mstun:1,
4736
     mmonitor:1,
4737
     minspection:1;
4738
  u8 unused03:3,
4739
     vrevive:1,
4740
     vkill:1,
4741
     vstun:1,
4742
     vmonitor:1,
4743
     vinspection:1;
4744
  u8 unused04:6,
4745
     txdisable:1,
4746
     rxdisable:1;
4747
  u8 groupcode;
4748
  u8 spacecode;
4749
  u8 delayproctime; // * 100 + 100ms
4750
  u8 resettime;     // * 100 + 100ms
4751
} dtmf_settings;
4752

    
4753
#seekto 0x2D00;
4754
struct {
4755
  struct {
4756
    ul16 freq1;
4757
    u8 unused01[6];
4758
    ul16 freq2;
4759
    u8 unused02[6];
4760
  } _2tone_encode[15];
4761
  u8 duration_1st_tone; // *10ms
4762
  u8 duration_2nd_tone; // *10ms
4763
  u8 duration_gap;      // *10ms
4764
  u8 unused03[13];
4765
  struct {
4766
    struct {
4767
      u8 dec;      // one out of LIST_2TONE_DEC
4768
      u8 response; // one out of LIST_2TONE_RESPONSE
4769
      u8 alert;    // 1-16
4770
    } decs[4];
4771
    u8 unused04[4];
4772
  } _2tone_decode[15];
4773
  u8 unused05[16];
4774

    
4775
  struct {
4776
    ul16 freqA;
4777
    ul16 freqB;
4778
    ul16 freqC;
4779
    ul16 freqD;
4780
    // unknown what those values mean, but they are
4781
    // derived from configured frequencies
4782
    ul16 derived_from_freqA; // 2304000/freqA
4783
    ul16 derived_from_freqB; // 2304000/freqB
4784
    ul16 derived_from_freqC; // 2304000/freqC
4785
    ul16 derived_from_freqD; // 2304000/freqD
4786
  }freqs[15];
4787
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
4788
} _2tone;
4789

    
4790
#seekto 0x3D80;
4791
struct {
4792
  u8 vhf_low[3];
4793
  u8 vhf_high[3];
4794
  u8 unknown1[4];
4795
  u8 unknown2[6];
4796
  u8 vhf2_low[3];
4797
  u8 vhf2_high[3];
4798
  u8 unknown3[4];
4799
  u8 unknown4[6];
4800
  u8 uhf_low[3];
4801
  u8 uhf_high[3];
4802
  u8 unknown5[4];
4803
  u8 unknown6[6];
4804
  u8 uhf2_low[3];
4805
  u8 uhf2_high[3];
4806
} ranges;
4807

    
4808
#seekto 0x3F70;
4809
struct {
4810
  char fp[6];
4811
} fingerprint;
4812

    
4813
"""
4814

    
4815

    
4816
class QYTColorHT(BTechMobileCommon):
4817
    """QTY's Color LCD Handheld and alike radios"""
4818
    COLOR_LCD = True
4819
    COLOR_LCD3 = True
4820
    NAME_LENGTH = 8
4821
    LIST_TMR = LIST_TMR15
4822

    
4823
    def process_mmap(self):
4824
        """Process the mem map into the mem object"""
4825

    
4826
        # Get it
4827
        self._memobj = bitwise.parse(COLORHT_MEM_FORMAT, self._mmap)
4828

    
4829
        # load specific parameters from the radio image
4830
        self.set_options()
4831

    
4832
    def set_options(self):
4833
        """This is to read the options from the image and set it in the
4834
        environment, for now just the limits of the freqs in the VHF/UHF
4835
        ranges"""
4836

    
4837
        # setting the correct ranges for each radio type
4838
        ranges = self._memobj.ranges
4839

    
4840
        # the normal dual bands
4841
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4842
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4843

    
4844
        # DEBUG
4845
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4846
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4847

    
4848
        # the additional bands
4849
        if self.MODEL in ["KT-8R"]:
4850
            # 200Mhz band
4851
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4852
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4853
            self._220_range = vhf2
4854

    
4855
            # 350Mhz band
4856
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4857
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4858
            self._350_range = uhf2
4859

    
4860
        # set the class with the real data
4861
        self._vhf_range = vhf
4862
        self._uhf_range = uhf
4863

    
4864

    
4865
# real radios
4866
@directory.register
4867
class KT8R(QYTColorHT):
4868
    """QYT KT8R"""
4869
    VENDOR = "QYT"
4870
    MODEL = "KT-8R"
4871
    BANDS = 4
4872
    LIST_TMR = LIST_TMR16
4873
    _vhf_range = (136000000, 175000000)
4874
    _220_range = (200000000, 261000000)
4875
    _uhf_range = (400000000, 481000000)
4876
    _350_range = (350000000, 391000000)
4877
    _magic = MSTRING_KT8R
4878
    _fileid = [KT8R_fp2, KT8R_fp1, KT8R_fp, ]
4879
    _power_levels = [chirp_common.PowerLevel("High", watts=5),
4880
                     chirp_common.PowerLevel("Low", watts=1)]
4881

    
4882

    
4883
COLOR9900_MEM_FORMAT = """
4884
#seekto 0x0000;
4885
struct {
4886
  lbcd rxfreq[4];
4887
  lbcd txfreq[4];
4888
  ul16 rxtone;
4889
  ul16 txtone;
4890
  u8 unknown0:4,
4891
     scode:4;
4892
  u8 unknown1:2,
4893
     spmute:2,
4894
     unknown2:2,
4895
     optsig:2;
4896
  u8 unknown3:3,
4897
     scramble:1,
4898
     unknown4:2,
4899
     power:2;
4900
  u8 unknown5:1,
4901
     wide:1,
4902
     unknown6:2,
4903
     bcl:1,
4904
     add:1,
4905
     pttid:2;
4906
} memory[200];
4907

    
4908
#seekto 0x0E00;
4909
struct {
4910
  u8 tmr;             // e00      00 confirmed
4911
  u8 unknown1;        // e01
4912
  u8 sql;             // e02      02 confirmed
4913
  u8 unknown2[2];     // e03-e04
4914
  u8 tot;             // e05      05 confirmed
4915
  u8 volume;          // e06      06 confirmed
4916
  u8 unknown3;        // e07
4917
  u8 abr;             // e08      08 confirmed
4918
  u8 beep;            // e09      09 confirmed
4919
  u8 unknown4[4];     // e0a-e0d
4920
  u8 dsub;            // e0e      14 confirmed
4921
  u8 dtmfst;          // e0f      15 confirmed
4922
  u8 unknown_e10;     // e10
4923
  u8 unknown_e11;     // e11
4924
  u8 screv;           // e12      18 confirmed
4925
  u8 unknown_e13;     // e13
4926
  u8 unknown_e14;     // e14
4927
  u8 pttid;           // e15      21 confirmed
4928
  u8 pttlt;           // e16      22 confirmed
4929
  u8 unknown7;        // e17
4930
  u8 emctp;           // e18      24 confirmed
4931
  u8 emcch;           // e19      25 confirmed
4932
  u8 sigbp;           // e1a      26 confirmed
4933
  u8 unknown8;        // e1b
4934
  u8 camdf;           // e1c      28 confirmed
4935
  u8 cbmdf;           // e1d      29 confirmed
4936
  u8 ccmdf;           // e1e      30 confirmed
4937
  u8 language;        // e1f      31 confirmed
4938
  u8 tmrtx;           // e20      32 confirmed
4939
  u8 vox;             // e21      33 confirmed
4940
  u8 voxt;            // e22      34 confirmed
4941
  u8 autolock;        // e23      35 confirmed
4942
  u8 asfc;            // e24      36 confirmed  above stat fore color
4943
  u8 mainfc;          // e25      37 confirmed  main fore color
4944
  u8 a_fc;            // e26      38 confirmed  a - fore color
4945
  u8 b_fc;            // e27      39 confirmed  b - fore color
4946
  u8 c_fc;            // e28      40 confirmed  c - fore color
4947
  u8 subfc;           // e29      41 confirmed  sub fore color
4948
  u8 battfc;          // e2a      42 confirmed  batt fore color
4949
  u8 sigfc;           // e2b      43 confirmed  signale fore color
4950
  u8 menufc;          // e2c      44 confirmed  menu fore color
4951
  u8 txfc;            // e2d      45 confirmed  tx fore color
4952
  u8 rxfc;            // e2e      46 confirmed  rx fore color
4953
  u8 unknown_e2f;     // e2f
4954
  u8 unknown_e30;     // e30
4955
  u8 unknown9[3];     // e31-e33
4956
  u8 anil;            // e34      52 confirmed
4957
  u8 reps;            // e35      53 confirmed
4958
  u8 tmrmr;           // e36      54 confirmed
4959
  u8 ste;             // e37      55 confirmed
4960
  u8 rpste;           // e38      56 confirmed
4961
  u8 rptdl;           // e39      57 confirmed
4962
  u8 dtmfg;           // e3a      58 confirmed
4963
} settings;
4964

    
4965
#seekto 0x0E80;
4966
struct {
4967
  u8 unknown1;
4968
  u8 vfomr;
4969
  u8 keylock;
4970
  u8 unknown2;
4971
  u8 unknown3:4,
4972
     vfomren:1,
4973
     unknown4:1,
4974
     reseten:1,
4975
     menuen:1;
4976
  u8 unknown5[11];
4977
  u8 dispab;
4978
  u8 unknown6[2];
4979
  u8 menu;
4980
  u8 unknown7[7];
4981
  u8 vfomra;
4982
  u8 vfomrb;
4983
  u8 vfomrc;
4984
  u8 vfomrd;
4985
  u8 mrcha;
4986
  u8 mrchb;
4987
  u8 mrchc;
4988
  u8 mrchd;
4989
} settings2;
4990

    
4991
struct settings_vfo {
4992
  u8 freq[8];
4993
  u8 offset[6];
4994
  u8 unknown2[2];
4995
  ul16 rxtone;
4996
  ul16 txtone;
4997
  u8 scode;
4998
  u8 spmute;
4999
  u8 optsig;
5000
  u8 scramble;
5001
  u8 wide;
5002
  u8 power;
5003
  u8 shiftd;
5004
  u8 step;
5005
  u8 unknown3[4];
5006
};
5007

    
5008
#seekto 0x0F00;
5009
struct {
5010
  struct settings_vfo a;
5011
  struct settings_vfo b;
5012
  struct settings_vfo c;
5013
  struct settings_vfo d;
5014
} vfo;
5015

    
5016
#seekto 0x0F80;
5017
struct {
5018
  char line1[8];
5019
  char line2[8];
5020
  char line3[8];
5021
  char line4[8];
5022
  char line5[8];
5023
  char line6[8];
5024
  char line7[8];
5025
  char line8[8];
5026
} poweron_msg;
5027

    
5028
#seekto 0x0FE0;
5029
struct {
5030
  char line[16];
5031
} static_msg;
5032

    
5033
#seekto 0x1000;
5034
struct {
5035
  char name[7];
5036
  u8 unknown1[9];
5037
} names[200];
5038

    
5039
#seekto 0x2400;
5040
struct {
5041
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5042
  u8 group_tone;
5043
  u8 repeat_tone;
5044
  u8 unused[13];
5045
} _5tone_std_settings[15];
5046

    
5047
#seekto 0x2500;
5048
struct {
5049
  u8 frame1[5];
5050
  u8 frame2[5];
5051
  u8 frame3[5];
5052
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5053
} _5tone_codes[15];
5054

    
5055
#seekto 0x25F0;
5056
struct {
5057
  u8 _5tone_delay1; // * 10ms
5058
  u8 _5tone_delay2; // * 10ms
5059
  u8 _5tone_delay3; // * 10ms
5060
  u8 _5tone_first_digit_ext_length;
5061
  u8 unknown1;
5062
  u8 unknown2;
5063
  u8 unknown3;
5064
  u8 unknown4;
5065
  u8 decode_standard;
5066
  u8 unknown5:5,
5067
     _5tone_decode_call_frame3:1,
5068
     _5tone_decode_call_frame2:1,
5069
     _5tone_decode_call_frame1:1;
5070
  u8 unknown6:5,
5071
     _5tone_decode_disp_frame3:1,
5072
     _5tone_decode_disp_frame2:1,
5073
     _5tone_decode_disp_frame1:1;
5074
  u8 decode_reset_time; // * 100 + 100ms
5075
} _5tone_settings;
5076

    
5077
#seekto 0x2900;
5078
struct {
5079
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5080
} dtmf_codes[15];
5081

    
5082
#seekto 0x29F0;
5083
struct {
5084
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10      // 9f0
5085
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10      // 9f1
5086
  u8 unknown0[14];                                             // 9f2-9ff
5087
  u8 inspection[16];                                           // a00-a0f
5088
  u8 monitor[16];                                              // a10-a1f
5089
  u8 alarmcode[16];                                            // a20-a2f
5090
  u8 stun[16];                                                 // a30-a3f
5091
  u8 kill[16];                                                 // a40-a4f
5092
  u8 revive[16];                                               // a50-a5f
5093
  u8 unknown1[16];                                             // a60-a6f
5094
  u8 unknown2[16];                                             // a70-a7f
5095
  u8 unknown3[16];                                             // a80-a8f
5096
  u8 unknown4[16];                                             // a90-a9f
5097
  u8 unknown5[16];                                             // aa0-aaf
5098
  u8 unknown6[16];                                             // ab0-abf
5099
  u8 unknown7[16];                                             // ac0-acf
5100
  u8 masterid[16];                                             // ad0-adf
5101
  u8 viceid[16];                                               // ae0-aef
5102
  u8 unused01:7,                                               // af0
5103
     mastervice:1;
5104
  u8 unused02:3,                                               // af1
5105
     mrevive:1,
5106
     mkill:1,
5107
     mstun:1,
5108
     mmonitor:1,
5109
     minspection:1;
5110
  u8 unused03:3,                                               // af2
5111
     vrevive:1,
5112
     vkill:1,
5113
     vstun:1,
5114
     vmonitor:1,
5115
     vinspection:1;
5116
  u8 unused04:6,                                               // af3
5117
     txdisable:1,
5118
     rxdisable:1;
5119
  u8 groupcode;                                                // af4
5120
  u8 spacecode;                                                // af5
5121
  u8 delayproctime; // * 100 + 100ms                           // af6
5122
  u8 resettime;     // * 100 + 100ms                           // af7
5123
} dtmf_settings;
5124

    
5125
#seekto 0x2D00;
5126
struct {
5127
  struct {
5128
    ul16 freq1;
5129
    u8 unused01[6];
5130
    ul16 freq2;
5131
    u8 unused02[6];
5132
  } _2tone_encode[15];
5133
  u8 duration_1st_tone; // *10ms
5134
  u8 duration_2nd_tone; // *10ms
5135
  u8 duration_gap;      // *10ms
5136
  u8 unused03[13];
5137
  struct {
5138
    struct {
5139
      u8 dec;      // one out of LIST_2TONE_DEC
5140
      u8 response; // one out of LIST_2TONE_RESPONSE
5141
      u8 alert;    // 1-16
5142
    } decs[4];
5143
    u8 unused04[4];
5144
  } _2tone_decode[15];
5145
  u8 unused05[16];
5146

    
5147
  struct {
5148
    ul16 freqA;
5149
    ul16 freqB;
5150
    ul16 freqC;
5151
    ul16 freqD;
5152
    // unknown what those values mean, but they are
5153
    // derived from configured frequencies
5154
    ul16 derived_from_freqA; // 2304000/freqA
5155
    ul16 derived_from_freqB; // 2304000/freqB
5156
    ul16 derived_from_freqC; // 2304000/freqC
5157
    ul16 derived_from_freqD; // 2304000/freqD
5158
  }freqs[15];
5159
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
5160
} _2tone;
5161

    
5162
#seekto 0x3D80;
5163
struct {
5164
  u8 vhf_low[3];
5165
  u8 vhf_high[3];
5166
  u8 unknown1[4];
5167
  u8 unknown2[6];
5168
  u8 vhf2_low[3];
5169
  u8 vhf2_high[3];
5170
  u8 unknown3[4];
5171
  u8 unknown4[6];
5172
  u8 uhf_low[3];
5173
  u8 uhf_high[3];
5174
  u8 unknown5[4];
5175
  u8 unknown6[6];
5176
  u8 uhf2_low[3];
5177
  u8 uhf2_high[3];
5178
} ranges;
5179

    
5180
#seekto 0x3F70;
5181
struct {
5182
  char fp[6];
5183
} fingerprint;
5184

    
5185
"""
5186

    
5187

    
5188
COLOR20V2_MEM_FORMAT = """
5189
#seekto 0x0000;
5190
struct {
5191
  lbcd rxfreq[4];
5192
  lbcd txfreq[4];
5193
  ul16 rxtone;
5194
  ul16 txtone;
5195
  u8 unknown0:4,
5196
     scode:4;
5197
  u8 unknown1:2,
5198
     spmute:2,
5199
     unknown2:2,
5200
     optsig:2;
5201
  u8 unknown3:3,
5202
     scramble:1,
5203
     unknown4:2,
5204
     power:2;
5205
  u8 unknown5:1,
5206
     wide:1,
5207
     unknown6:2,
5208
     bcl:1,
5209
     add:1,
5210
     pttid:2;
5211
} memory[200];
5212

    
5213
#seekto 0x0E00;
5214
struct {
5215
  u8 tmr;             // e00      00 confirmed
5216
  u8 unknown1;        // e01
5217
  u8 sql;             // e02      02 confirmed
5218
  u8 unknown2;        // e03
5219
  u8 autolock;        // e04      04 confirmed
5220
  u8 tot;             // e05      05 confirmed
5221
  u8 apo;             // e06      06 confirmed
5222
  u8 unknown3;        // e07
5223
  u8 abr;             // e08      08 confirmed
5224
  u8 beep;            // e09      09 confirmed
5225
  u8 unknown4[4];     // e0a-e0d
5226
  u8 dtmfst;          // e0e      14 confirmed
5227
  u8 unknown5[2];     // e0f-e10
5228
  u8 screv;           // e11      17 confirmed
5229
  u8 unknown6[2];     // e12-e13
5230
  u8 pttid;           // e14      20 confirmed
5231
  u8 pttlt;           // e15      21 confirmed
5232
  u8 unknown7;        // e16
5233
  u8 emctp;           // e17      23 confirmed
5234
  u8 emcch;           // e18      24 confirmed
5235
  u8 sigbp;           // e19      25confirmed
5236
  u8 unknown8;        // e1a
5237
  u8 camdf;           // e1b      27 confirmed
5238
  u8 cbmdf;           // e1c      28 confirmed
5239
  u8 ccmdf;           // e1d      29 confirmed
5240
  u8 vox;             // e1e      30 confirmed
5241
  u8 voxt;            // e1f      31 confirmed
5242
  u8 sync;            // e20      32 confirmed
5243
  u8 asfc;            // e21      33 confirmed  above stat fore color
5244
  u8 mainfc;          // e22      34 confirmed  main fore color
5245
  u8 a_fc;            // e23      35 confirmed  a - fore color
5246
  u8 b_fc;            // e24      36 confirmed  b - fore color
5247
  u8 c_fc;            // e25      37 confirmed  c - fore color
5248
  u8 subfc;           // e26      38 confirmed  sub fore color
5249
  u8 battfc;          // e27      39 confirmed  batt fore color
5250
  u8 sigfc;           // e28      40 confirmed  signale fore color
5251
  u8 menufc;          // e29      41 confirmed  menu fore color
5252
  u8 txfc;            // e2a      42 confirmed  tx fore color
5253
  u8 rxfc;            // e2b      43 confirmed  rx fore color
5254
  u8 repsw;           // e2c      44 confirmed
5255
  u8 dsub;            // e2d      45 confirmed
5256
  u8 unknown9[5];     // e2e-e32
5257
  u8 anil;            // e33      51 confirmed
5258
  u8 reps;            // e34      52 confirmed
5259
  u8 repm;            // e35      53 confirmed
5260
  u8 tmrmr;           // e36      54 confirmed
5261
  u8 ste;             // e37      55 confirmed
5262
  u8 rpste;           // e38      56 confirmed
5263
  u8 rptdl;           // e39      57 confirmed
5264
  u8 dtmfg;           // e3a      58 confirmed
5265
  u8 mgain;           // e3b      59 confirmed
5266
  u8 skiptx;          // e3c      60 confirmed
5267
  u8 scmode;          // e3d      61 confirmed
5268
  u8 tmrtx;           // e3e      62 confirmed
5269
  u8 volume;          // e3f      63 confirmed
5270
  u8 unknown_10;      // e40
5271
  u8 save;            // e41      65 confirmed
5272
} settings;
5273

    
5274
#seekto 0x0E80;
5275
struct {
5276
  u8 unknown1;
5277
  u8 vfomr;
5278
  u8 keylock;
5279
  u8 unknown2;
5280
  u8 unknown3:4,
5281
     vfomren:1,
5282
     unknown4:1,
5283
     reseten:1,
5284
     menuen:1;
5285
  u8 unknown5[11];
5286
  u8 dispab;
5287
  u8 unknown6[2];
5288
  u8 menu;
5289
  u8 unknown7[7];
5290
  u8 vfomra;
5291
  u8 vfomrb;
5292
  u8 vfomrc;
5293
  u8 vfomrd;
5294
  u8 mrcha;
5295
  u8 mrchb;
5296
  u8 mrchc;
5297
  u8 mrchd;
5298
} settings2;
5299

    
5300
struct settings_vfo {
5301
  u8 freq[8];
5302
  u8 offset[6];
5303
  u8 unknown2[2];
5304
  ul16 rxtone;
5305
  ul16 txtone;
5306
  u8 scode;
5307
  u8 spmute;
5308
  u8 optsig;
5309
  u8 scramble;
5310
  u8 wide;
5311
  u8 power;
5312
  u8 shiftd;
5313
  u8 step;
5314
  u8 unknown3[4];
5315
};
5316

    
5317
#seekto 0x0F00;
5318
struct {
5319
  struct settings_vfo a;
5320
  struct settings_vfo b;
5321
  struct settings_vfo c;
5322
  struct settings_vfo d;
5323
} vfo;
5324

    
5325
#seekto 0x0F80;
5326
struct {
5327
  char line1[8];
5328
  char line2[8];
5329
  char line3[8];
5330
  char line4[8];
5331
  char line5[8];
5332
  char line6[8];
5333
  char line7[8];
5334
  char line8[8];
5335
} poweron_msg;
5336

    
5337
#seekto 0x0FE0;
5338
struct {
5339
  char line[16];
5340
} static_msg;
5341

    
5342
#seekto 0x1000;
5343
struct {
5344
  char name[7];
5345
  u8 unknown1[9];
5346
} names[200];
5347

    
5348
#seekto 0x2400;
5349
struct {
5350
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5351
  u8 group_tone;
5352
  u8 repeat_tone;
5353
  u8 unused[13];
5354
} _5tone_std_settings[15];
5355

    
5356
#seekto 0x2500;
5357
struct {
5358
  u8 frame1[5];
5359
  u8 frame2[5];
5360
  u8 frame3[5];
5361
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5362
} _5tone_codes[15];
5363

    
5364
#seekto 0x25F0;
5365
struct {
5366
  u8 _5tone_delay1; // * 10ms
5367
  u8 _5tone_delay2; // * 10ms
5368
  u8 _5tone_delay3; // * 10ms
5369
  u8 _5tone_first_digit_ext_length;
5370
  u8 unknown1;
5371
  u8 unknown2;
5372
  u8 unknown3;
5373
  u8 unknown4;
5374
  u8 decode_standard;
5375
  u8 unknown5:5,
5376
     _5tone_decode_call_frame3:1,
5377
     _5tone_decode_call_frame2:1,
5378
     _5tone_decode_call_frame1:1;
5379
  u8 unknown6:5,
5380
     _5tone_decode_disp_frame3:1,
5381
     _5tone_decode_disp_frame2:1,
5382
     _5tone_decode_disp_frame1:1;
5383
  u8 decode_reset_time; // * 100 + 100ms
5384
} _5tone_settings;
5385

    
5386
#seekto 0x2900;
5387
struct {
5388
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5389
} dtmf_codes[15];
5390

    
5391
#seekto 0x29F0;
5392
struct {
5393
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
5394
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
5395
  u8 unknown0[14];
5396
  u8 inspection[16];
5397
  u8 monitor[16];
5398
  u8 alarmcode[16];
5399
  u8 stun[16];
5400
  u8 kill[16];
5401
  u8 revive[16];
5402
  u8 unknown1[16];
5403
  u8 unknown2[16];
5404
  u8 unknown3[16];
5405
  u8 unknown4[16];
5406
  u8 unknown5[16];
5407
  u8 unknown6[16];
5408
  u8 unknown7[16];
5409
  u8 masterid[16];
5410
  u8 viceid[16];
5411
  u8 unused01:7,
5412
     mastervice:1;
5413
  u8 unused02:3,
5414
     mrevive:1,
5415
     mkill:1,
5416
     mstun:1,
5417
     mmonitor:1,
5418
     minspection:1;
5419
  u8 unused03:3,
5420
     vrevive:1,
5421
     vkill:1,
5422
     vstun:1,
5423
     vmonitor:1,
5424
     vinspection:1;
5425
  u8 unused04:6,
5426
     txdisable:1,
5427
     rxdisable:1;
5428
  u8 groupcode;
5429
  u8 spacecode;
5430
  u8 delayproctime; // * 100 + 100ms
5431
  u8 resettime;     // * 100 + 100ms
5432
} dtmf_settings;
5433

    
5434
#seekto 0x2D00;
5435
struct {
5436
  struct {
5437
    ul16 freq1;
5438
    u8 unused01[6];
5439
    ul16 freq2;
5440
    u8 unused02[6];
5441
  } _2tone_encode[15];
5442
  u8 duration_1st_tone; // *10ms
5443
  u8 duration_2nd_tone; // *10ms
5444
  u8 duration_gap;      // *10ms
5445
  u8 unused03[13];
5446
  struct {
5447
    struct {
5448
      u8 dec;      // one out of LIST_2TONE_DEC
5449
      u8 response; // one out of LIST_2TONE_RESPONSE
5450
      u8 alert;    // 1-16
5451
    } decs[4];
5452
    u8 unused04[4];
5453
  } _2tone_decode[15];
5454
  u8 unused05[16];
5455

    
5456
  struct {
5457
    ul16 freqA;
5458
    ul16 freqB;
5459
    ul16 freqC;
5460
    ul16 freqD;
5461
    // unknown what those values mean, but they are
5462
    // derived from configured frequencies
5463
    ul16 derived_from_freqA; // 2304000/freqA
5464
    ul16 derived_from_freqB; // 2304000/freqB
5465
    ul16 derived_from_freqC; // 2304000/freqC
5466
    ul16 derived_from_freqD; // 2304000/freqD
5467
  }freqs[15];
5468
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
5469
} _2tone;
5470

    
5471
#seekto 0x3D80;
5472
struct {
5473
  u8 vhf_low[3];
5474
  u8 vhf_high[3];
5475
  u8 unknown1[4];
5476
  u8 unknown2[6];
5477
  u8 vhf2_low[3];
5478
  u8 vhf2_high[3];
5479
  u8 unknown3[4];
5480
  u8 unknown4[6];
5481
  u8 uhf_low[3];
5482
  u8 uhf_high[3];
5483
  u8 unknown5[4];
5484
  u8 unknown6[6];
5485
  u8 uhf2_low[3];
5486
  u8 uhf2_high[3];
5487
} ranges;
5488

    
5489
#seekto 0x3F70;
5490
struct {
5491
  char fp[6];
5492
} fingerprint;
5493

    
5494
"""
5495

    
5496

    
5497
class BTechColorWP(BTechMobileCommon):
5498
    """BTECH's Color WP Mobile and alike radios"""
5499
    COLOR_LCD = True
5500
    COLOR_LCD4 = True
5501
    NAME_LENGTH = 7
5502
    LIST_TMR = LIST_TMR7
5503

    
5504
    def set_options(self):
5505
        """This is to read the options from the image and set it in the
5506
        environment, for now just the limits of the freqs in the VHF/UHF
5507
        ranges"""
5508

    
5509
        # setting the correct ranges for each radio type
5510
        ranges = self._memobj.ranges
5511

    
5512
        # the normal dual bands
5513
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
5514
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
5515

    
5516
        # DEBUG
5517
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
5518
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
5519

    
5520
        # set the class with the real data
5521
        self._vhf_range = vhf
5522
        self._uhf_range = uhf
5523

    
5524

    
5525
# real radios
5526
@directory.register
5527
class WP9900(BTechColorWP):
5528
    """Anysecu WP-9900"""
5529
    VENDOR = "Anysecu"
5530
    MODEL = "WP-9900"
5531
    BANDS = 2
5532
    # <delete me> LIST_TMR = LIST_TMR7
5533
    UPLOAD_MEM_SIZE = 0X3100
5534
    # <delete me> UPLOAD_MEM_SIZE = 0X4000
5535
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
5536
                     chirp_common.PowerLevel("Low", watts=5)]
5537
    _upper = 199
5538
    _magic = MSTRING_WP9900
5539
    _fileid = [WP9900_fp, ]
5540
    _gmrs = False
5541

    
5542
    def process_mmap(self):
5543
        """Process the mem map into the mem object"""
5544

    
5545
        # Get it
5546
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
5547

    
5548
        # load specific parameters from the radio image
5549
        self.set_options()
(4-4/13)