Project

General

Profile

New Model #9827 » btech_wp-9900_kt-wp12_draft_#2.py

Jim Unroe, 05/21/2022 04:57 PM

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

    
18
import time
19
import struct
20
import logging
21

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

    
31
LOG = logging.getLogger(__name__)
32

    
33
# A note about the 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
# QYT KT-WP12 and KT-9900
280
KTWP12_fp = "WP3094"
281

    
282
# Anysecu WP-9900
283
WP9900_fp = "WP3094"
284

    
285

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

    
309

    
310
def _clean_buffer(radio):
311
    """Cleaning the read serial buffer, hard timeout to survive an infinite
312
    data stream"""
313

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

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

    
330
        # restore the default serial timeout
331
        radio.pipe.timeout = STIMEOUT
332

    
333
    except Exception:
334
        raise errors.RadioError("Unknown error cleaning the serial buffer")
335

    
336

    
337
def _rawrecv(radio, amount):
338
    """Raw read from the radio device, less intensive way"""
339

    
340
    data = ""
341

    
342
    try:
343
        data = radio.pipe.read(amount)
344

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

    
350
        # fail if no data is received
351
        if len(data) == 0:
352
            raise errors.RadioError("No data received from radio")
353

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

    
359
    except:
360
        raise errors.RadioError("Error reading data from radio")
361

    
362
    return data
363

    
364

    
365
def _send(radio, data):
366
    """Send data to the radio device"""
367

    
368
    try:
369
        radio.pipe.write(data)
370

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

    
378

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

    
386
    return frame
387

    
388

    
389
def _recv(radio, addr):
390
    """Get data from the radio all at once to lower syscalls load"""
391

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

    
395
    # get the whole block
396
    block = _rawrecv(radio, BLOCK_SIZE + 5)
397

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

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

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

    
413
    # return the data
414
    return block[5:]
415

    
416

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

    
423
    # lengthen the timeout here as these radios are reseting due to timeout
424
    radio.pipe.timeout = 0.75
425

    
426
    # send the magic word
427
    _send(radio, radio._magic)
428

    
429
    # Now you get a 50 byte reply if all goes well
430
    ident = _rawrecv(radio, 50)
431

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

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

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

    
450
            break
451

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

    
456
    # pause here for the radio to catch up
457
    sleep(0.1)
458

    
459
    # the OEM software reads this additional block, so we will, too
460

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

    
467
    # restore the default serial timeout
468
    radio.pipe.timeout = STIMEOUT
469

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

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

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

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

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

    
503
    # DEBUG
504
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
505

    
506
    return True
507

    
508

    
509
def _download(radio):
510
    """Get the memory map"""
511

    
512
    # UI progress
513
    status = chirp_common.Status()
514

    
515
    # put radio in program mode and identify it
516
    _do_ident(radio, status)
517

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

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

    
529
        # read
530
        d = _recv(radio, addr)
531

    
532
        # aggregate the data
533
        data += d
534

    
535
        # UI Update
536
        status.cur = addr / BLOCK_SIZE
537
        status.msg = "Cloning from radio..."
538
        radio.status_fn(status)
539

    
540
    return data
541

    
542

    
543
def _upload(radio):
544
    """Upload procedure"""
545

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

    
550
    # UI progress
551
    status = chirp_common.Status()
552

    
553
    # put radio in program mode and identify it
554
    _do_ident(radio, status, True)
555

    
556
    # get the data to upload to radio
557
    data = radio.get_mmap()
558

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

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

    
570
        # build the frame to send
571
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
572

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

    
578
        # send the frame
579
        _send(radio, frame)
580

    
581
        # receiving the response
582
        ack = _rawrecv(radio, 1)
583

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

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

    
591
        # UI Update
592
        status.cur = addr / TX_BLOCK_SIZE
593
        status.msg = "Cloning to radio..."
594
        radio.status_fn(status)
595

    
596

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

    
601
    if rid in cls._fileid:
602
        return True
603

    
604
    return False
605

    
606

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

    
615
    return (ilow, ihigh)
616

    
617

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

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

    
629
    # if you get here is because the freq pairs are split
630
    return True
631

    
632

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

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

    
673
            1 - Turn off your radio
674
            2 - Connect your interface cable
675
            3 - Turn on your radio
676
            4 - Do the download of your radio data
677

    
678
            """))
679
        rp.pre_upload = _(dedent("""\
680
            Follow these instructions to upload your info:
681

    
682
            1 - Turn off your radio
683
            2 - Connect your interface cable
684
            3 - Turn on your radio
685
            4 - Do the upload of your radio data
686

    
687
            """))
688
        return rp
689

    
690
    def get_features(self):
691
        """Get the radio's features"""
692

    
693
        # we will use the following var as global
694
        global POWER_LEVELS
695

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

    
727
        # power levels
728
        POWER_LEVELS = self._power_levels
729
        rf.valid_power_levels = POWER_LEVELS
730

    
731
        # normal dual bands
732
        rf.valid_bands = [self._vhf_range, self._uhf_range]
733

    
734
        # 220 band
735
        if self.BANDS == 3 or self.BANDS == 4:
736
            rf.valid_bands.append(self._220_range)
737

    
738
        # 350 band
739
        if self.BANDS == 4:
740
            rf.valid_bands.append(self._350_range)
741

    
742
        return rf
743

    
744
    def validate_memory(self, mem):
745
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
746

    
747
        _msg_duplex1 = 'Memory location only supports "Low"'
748
        _msg_duplex2 = 'Memory location only supports "off"'
749
        _msg_duplex3 = 'Memory location only supports "(None)", "+" or "off"'
750

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

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

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

    
774
        return msgs
775

    
776
    def sync_in(self):
777
        """Download from radio"""
778
        data = _download(self)
779
        self._mmap = memmap.MemoryMap(data)
780
        self.process_mmap()
781

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

    
791
    def get_raw_memory(self, number):
792
        return repr(self._memobj.memory[number])
793

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

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

    
812
            tone = DTCS[index]
813
            return 'DTCS', tone, pol
814

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

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

    
844
        # Create a high-level memory object to return to the UI
845
        mem = chirp_common.Memory()
846

    
847
        # Memory number
848
        mem.number = number
849

    
850
        if _mem.get_raw()[0] == "\xFF":
851
            mem.empty = True
852
            return mem
853

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

    
878
        # name TAG of the channel
879
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
880

    
881
        # power
882
        mem.power = POWER_LEVELS[int(_mem.power)]
883

    
884
        # wide/narrow
885
        mem.mode = MODES[int(_mem.wide)]
886

    
887
        # skip
888
        mem.skip = SKIP_VALUES[_mem.add]
889

    
890
        # tone data
891
        rxtone = txtone = None
892
        txtone = self._decode_tone(_mem.txtone)
893
        rxtone = self._decode_tone(_mem.rxtone)
894
        chirp_common.split_tone_decode(mem, txtone, rxtone)
895

    
896
        # Extra
897
        mem.extra = RadioSettingGroup("extra", "Extra")
898

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

    
906
        bcl = RadioSetting("bcl", "Busy channel lockout",
907
                           RadioSettingValueBoolean(bool(_mem.bcl)))
908
        mem.extra.append(bcl)
909

    
910
        pttid = RadioSetting("pttid", "PTT ID",
911
                             RadioSettingValueList(PTTID_LIST,
912
                                                   PTTID_LIST[_mem.pttid]))
913
        mem.extra.append(pttid)
914

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

    
923
        optsig = RadioSetting("optsig", "Optional signaling",
924
                              RadioSettingValueList(
925
                                  OPTSIG_LIST,
926
                                  OPTSIG_LIST[_mem.optsig]))
927
        mem.extra.append(optsig)
928

    
929
        spmute = RadioSetting("spmute", "Speaker mute",
930
                              RadioSettingValueList(
931
                                  SPMUTE_LIST,
932
                                  SPMUTE_LIST[_mem.spmute]))
933
        mem.extra.append(spmute)
934

    
935
        return mem
936

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

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

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

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

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

    
1005
        # frequency
1006
        _mem.rxfreq = mem.freq / 10
1007

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

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

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

    
1033
        # power, # default power level is high
1034
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
1035

    
1036
        # wide/narrow
1037
        _mem.wide = MODES.index(mem.mode)
1038

    
1039
        # scan add property
1040
        _mem.add = SKIP_VALUES.index(mem.skip)
1041

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

    
1051
        def _zero_settings():
1052
            _mem.spmute = 0
1053
            _mem.optsig = 0
1054
            _mem.scramble = 0
1055
            _mem.bcl = 0
1056
            _mem.pttid = 0
1057
            _mem.scode = 0
1058

    
1059
        if self.COLOR_LCD and _mem.scramble:
1060
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1061
            _mem.scramble = 0
1062

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

    
1083
        return mem
1084

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

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

    
1106
        sql = RadioSetting("settings.sql", "Squelch level",
1107
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1108
        basic.append(sql)
1109

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

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

    
1121
        tot = RadioSetting("settings.tot", "Time out timer",
1122
                           RadioSettingValueList(
1123
                               LIST_TOT,
1124
                               LIST_TOT[_mem.settings.tot]))
1125
        basic.append(tot)
1126

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

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

    
1148
        abr = RadioSetting("settings.abr", "Backlight timer",
1149
                           RadioSettingValueList(
1150
                               LIST_OFF1TO50,
1151
                               LIST_OFF1TO50[_mem.settings.abr]))
1152
        basic.append(abr)
1153

    
1154
        beep = RadioSetting("settings.beep", "Key beep",
1155
                            RadioSettingValueBoolean(_mem.settings.beep))
1156
        basic.append(beep)
1157

    
1158
        if self.MODEL == "KT-WP12" or self.MODEL == "WP-9900":
1159
            rs = RadioSettingValueInteger(1, 51, _mem.settings.volume + 1)
1160
            volume = RadioSetting("settings.volume", "Volume", rs)
1161
            basic.append(volume)
1162

    
1163
        if self.MODEL == "KT-8R":
1164
            dsub = RadioSetting("settings.dsub", "CTCSS/DCS code display",
1165
                                RadioSettingValueBoolean(
1166
                                    _mem.settings.dsub))
1167
            basic.append(dsub)
1168

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

    
1181
        if not self.COLOR_LCD:
1182
            prisc = RadioSetting("settings.prisc", "Priority scan",
1183
                                 RadioSettingValueBoolean(
1184
                                     _mem.settings.prisc))
1185
            basic.append(prisc)
1186

    
1187
            prich = RadioSetting("settings.prich", "Priority channel",
1188
                                 RadioSettingValueInteger(0, self._upper,
1189
                                                          _mem.settings.prich))
1190
            basic.append(prich)
1191

    
1192
        screv = RadioSetting("settings.screv", "Scan resume method",
1193
                             RadioSettingValueList(
1194
                                 LIST_SCREV,
1195
                                 LIST_SCREV[_mem.settings.screv]))
1196
        basic.append(screv)
1197

    
1198
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1199
                             RadioSettingValueInteger(0, 30,
1200
                                                      _mem.settings.pttlt))
1201
        basic.append(pttlt)
1202

    
1203
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1204
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1205
                                 RadioSettingValueList(
1206
                                     LIST_EMCTPX,
1207
                                     LIST_EMCTPX[_mem.settings.emctp]))
1208
            basic.append(emctp)
1209
        else:
1210
            emctp = RadioSetting("settings.emctp", "Alarm mode",
1211
                                 RadioSettingValueList(
1212
                                     LIST_EMCTP,
1213
                                     LIST_EMCTP[_mem.settings.emctp]))
1214
            basic.append(emctp)
1215

    
1216
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1217
                             RadioSettingValueInteger(0, self._upper,
1218
                                                      _mem.settings.emcch))
1219
        basic.append(emcch)
1220

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

    
1236
        camdf = RadioSetting("settings.camdf", "Display mode A",
1237
                             RadioSettingValueList(
1238
                                 LIST_MDF,
1239
                                 LIST_MDF[_mem.settings.camdf]))
1240
        basic.append(camdf)
1241

    
1242
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1243
                             RadioSettingValueList(
1244
                                 LIST_MDF,
1245
                                 LIST_MDF[_mem.settings.cbmdf]))
1246
        basic.append(cbmdf)
1247

    
1248
        if self.COLOR_LCD:
1249
            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
1250
                                 RadioSettingValueList(
1251
                                     LIST_MDF,
1252
                                     LIST_MDF[_mem.settings.ccmdf]))
1253
            basic.append(ccmdf)
1254

    
1255
            if not self.COLOR_LCD4:
1256
                cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
1257
                                     RadioSettingValueList(
1258
                                         LIST_MDF,
1259
                                         LIST_MDF[_mem.settings.cdmdf]))
1260
                basic.append(cdmdf)
1261

    
1262
                langua = RadioSetting("settings.langua", "Language",
1263
                                      RadioSettingValueList(
1264
                                          LIST_LANGUA,
1265
                                          LIST_LANGUA[_mem.settings.langua]))
1266
                basic.append(langua)
1267

    
1268
        if self.MODEL == "KT-8R":
1269
            voice = RadioSetting("settings.voice", "Voice prompt",
1270
                                 RadioSettingValueList(
1271
                                     LIST_VOICE,
1272
                                     LIST_VOICE[_mem.settings.voice]))
1273
            basic.append(voice)
1274

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

    
1282
            voxt = RadioSetting("settings.voxt", "VOX delay time",
1283
                                RadioSettingValueList(
1284
                                    LIST_VOXT,
1285
                                    LIST_VOXT[_mem.settings.voxt]))
1286
            basic.append(voxt)
1287

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

    
1306
        if not self.COLOR_LCD:
1307
            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1308
                                  RadioSettingValueList(
1309
                                      LIST_PONMSG,
1310
                                      LIST_PONMSG[_mem.settings.ponmsg]))
1311
            basic.append(ponmsg)
1312

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

    
1322
            if not self.COLOR_LCD4:
1323
                mainbc = RadioSetting("settings.mainbc",
1324
                                      "Main LCD background color",
1325
                                      RadioSettingValueList(
1326
                                          LIST_COLOR9,
1327
                                          LIST_COLOR9[_mem.settings.mainbc]))
1328
                basic.append(mainbc)
1329

    
1330
            menufc = RadioSetting("settings.menufc", "Menu foreground color",
1331
                                  RadioSettingValueList(
1332
                                      LIST_COLOR9,
1333
                                      LIST_COLOR9[_mem.settings.menufc]))
1334
            basic.append(menufc)
1335

    
1336
            if not self.COLOR_LCD4:
1337
                val = RadioSettingValueList(LIST_COLOR9,
1338
                                            LIST_COLOR9[_mem.settings.menubc])
1339
                menubc = RadioSetting("settings.menubc",
1340
                                      "Menu background color", val)
1341
                basic.append(menubc)
1342

    
1343
            stafc = RadioSetting("settings.stafc",
1344
                                 "Top status foreground color",
1345
                                 RadioSettingValueList(
1346
                                     LIST_COLOR9,
1347
                                     LIST_COLOR9[_mem.settings.stafc]))
1348
            basic.append(stafc)
1349

    
1350
            if not self.COLOR_LCD4:
1351
                stabc = RadioSetting("settings.stabc",
1352
                                     "Top status background color",
1353
                                     RadioSettingValueList(
1354
                                         LIST_COLOR9,
1355
                                         LIST_COLOR9[_mem.settings.stabc]))
1356
                basic.append(stabc)
1357

    
1358
            sigfc = RadioSetting("settings.sigfc",
1359
                                 "Bottom status foreground color",
1360
                                 RadioSettingValueList(
1361
                                     LIST_COLOR9,
1362
                                     LIST_COLOR9[_mem.settings.sigfc]))
1363
            basic.append(sigfc)
1364

    
1365
            if not self.COLOR_LCD4:
1366
                sigbc = RadioSetting("settings.sigbc",
1367
                                     "Bottom status background color",
1368
                                     RadioSettingValueList(
1369
                                         LIST_COLOR9,
1370
                                         LIST_COLOR9[_mem.settings.sigbc]))
1371
                basic.append(sigbc)
1372

    
1373
            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
1374
                                RadioSettingValueList(
1375
                                    LIST_COLOR9,
1376
                                    LIST_COLOR9[_mem.settings.rxfc]))
1377
            basic.append(rxfc)
1378

    
1379
            txfc = RadioSetting("settings.txfc",
1380
                                "Transmitting character color",
1381
                                RadioSettingValueList(
1382
                                    LIST_COLOR9,
1383
                                    LIST_COLOR9[_mem.settings.txfc]))
1384
            basic.append(txfc)
1385

    
1386
            if not self.COLOR_LCD4:
1387
                txdisp = RadioSetting("settings.txdisp",
1388
                                      "Transmitting status display",
1389
                                      RadioSettingValueList(
1390
                                          LIST_TXDISP,
1391
                                          LIST_TXDISP[_mem.settings.txdisp]))
1392
                basic.append(txdisp)
1393

    
1394
        elif self.COLOR_LCD2 or self.COLOR_LCD3:
1395
            stfc = RadioSetting("settings.stfc",
1396
                                "ST-FC",
1397
                                RadioSettingValueList(
1398
                                    LIST_COLOR8,
1399
                                    LIST_COLOR8[_mem.settings.stfc]))
1400
            basic.append(stfc)
1401

    
1402
            mffc = RadioSetting("settings.mffc",
1403
                                "MF-FC",
1404
                                RadioSettingValueList(
1405
                                    LIST_COLOR8,
1406
                                    LIST_COLOR8[_mem.settings.mffc]))
1407
            basic.append(mffc)
1408

    
1409
            sfafc = RadioSetting("settings.sfafc",
1410
                                 "SFA-FC",
1411
                                 RadioSettingValueList(
1412
                                     LIST_COLOR8,
1413
                                     LIST_COLOR8[_mem.settings.sfafc]))
1414
            basic.append(sfafc)
1415

    
1416
            sfbfc = RadioSetting("settings.sfbfc",
1417
                                 "SFB-FC",
1418
                                 RadioSettingValueList(
1419
                                     LIST_COLOR8,
1420
                                     LIST_COLOR8[_mem.settings.sfbfc]))
1421
            basic.append(sfbfc)
1422

    
1423
            sfcfc = RadioSetting("settings.sfcfc",
1424
                                 "SFC-FC",
1425
                                 RadioSettingValueList(
1426
                                     LIST_COLOR8,
1427
                                     LIST_COLOR8[_mem.settings.sfcfc]))
1428
            basic.append(sfcfc)
1429

    
1430
            sfdfc = RadioSetting("settings.sfdfc",
1431
                                 "SFD-FC",
1432
                                 RadioSettingValueList(
1433
                                     LIST_COLOR8,
1434
                                     LIST_COLOR8[_mem.settings.sfdfc]))
1435
            basic.append(sfdfc)
1436

    
1437
            subfc = RadioSetting("settings.subfc",
1438
                                 "SUB-FC",
1439
                                 RadioSettingValueList(
1440
                                     LIST_COLOR8,
1441
                                     LIST_COLOR8[_mem.settings.subfc]))
1442
            basic.append(subfc)
1443

    
1444
            fmfc = RadioSetting("settings.fmfc",
1445
                                "FM-FC",
1446
                                RadioSettingValueList(
1447
                                    LIST_COLOR8,
1448
                                    LIST_COLOR8[_mem.settings.fmfc]))
1449
            basic.append(fmfc)
1450

    
1451
            sigfc = RadioSetting("settings.sigfc",
1452
                                 "SIG-FC",
1453
                                 RadioSettingValueList(
1454
                                     LIST_COLOR8,
1455
                                     LIST_COLOR8[_mem.settings.sigfc]))
1456
            basic.append(sigfc)
1457

    
1458
            if not self.MODEL == "KT-8R":
1459
                modfc = RadioSetting("settings.modfc",
1460
                                     "MOD-FC",
1461
                                     RadioSettingValueList(
1462
                                         LIST_COLOR8,
1463
                                         LIST_COLOR8[_mem.settings.modfc]))
1464
                basic.append(modfc)
1465

    
1466
            menufc = RadioSetting("settings.menufc",
1467
                                  "MENUFC",
1468
                                  RadioSettingValueList(
1469
                                      LIST_COLOR8,
1470
                                      LIST_COLOR8[_mem.settings.menufc]))
1471
            basic.append(menufc)
1472

    
1473
            txfc = RadioSetting("settings.txfc",
1474
                                "TX-FC",
1475
                                RadioSettingValueList(
1476
                                    LIST_COLOR8,
1477
                                    LIST_COLOR8[_mem.settings.txfc]))
1478
            basic.append(txfc)
1479

    
1480
            if self.MODEL == "KT-8R":
1481
                rxfc = RadioSetting("settings.rxfc",
1482
                                    "RX-FC",
1483
                                    RadioSettingValueList(
1484
                                        LIST_COLOR8,
1485
                                        LIST_COLOR8[_mem.settings.rxfc]))
1486
                basic.append(rxfc)
1487

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

    
1503
            mainfc = RadioSetting("settings.mainfc",
1504
                                  "Main fore color",
1505
                                  RadioSettingValueList(
1506
                                     LIST_COLOR8,
1507
                                     LIST_COLOR8[_mem.settings.mainfc]))
1508
            basic.append(mainfc)
1509

    
1510
            a_fc = RadioSetting("settings.a_fc",
1511
                                "A - fore color",
1512
                                RadioSettingValueList(
1513
                                    LIST_COLOR8,
1514
                                    LIST_COLOR8[_mem.settings.a_fc]))
1515
            basic.append(a_fc)
1516

    
1517
            b_fc = RadioSetting("settings.b_fc",
1518
                                "B - fore color",
1519
                                RadioSettingValueList(
1520
                                    LIST_COLOR8,
1521
                                    LIST_COLOR8[_mem.settings.b_fc]))
1522
            basic.append(b_fc)
1523

    
1524
            c_fc = RadioSetting("settings.c_fc",
1525
                                "C - fore color",
1526
                                RadioSettingValueList(
1527
                                    LIST_COLOR8,
1528
                                    LIST_COLOR8[_mem.settings.c_fc]))
1529
            basic.append(c_fc)
1530

    
1531
            subfc = RadioSetting("settings.subfc",
1532
                                 "Sub fore color",
1533
                                 RadioSettingValueList(
1534
                                     LIST_COLOR8,
1535
                                     LIST_COLOR8[_mem.settings.subfc]))
1536
            basic.append(subfc)
1537

    
1538
            battfc = RadioSetting("settings.battfc",
1539
                                  "Battery fore color",
1540
                                  RadioSettingValueList(
1541
                                      LIST_COLOR8,
1542
                                      LIST_COLOR8[_mem.settings.battfc]))
1543
            basic.append(battfc)
1544

    
1545
            sigfc = RadioSetting("settings.sigfc",
1546
                                 "Signal fore color",
1547
                                 RadioSettingValueList(
1548
                                     LIST_COLOR8,
1549
                                     LIST_COLOR8[_mem.settings.sigfc]))
1550
            basic.append(sigfc)
1551

    
1552
            menufc = RadioSetting("settings.menufc",
1553
                                  "Menu fore color",
1554
                                  RadioSettingValueList(
1555
                                      LIST_COLOR8,
1556
                                      LIST_COLOR8[_mem.settings.menufc]))
1557
            basic.append(menufc)
1558

    
1559
            txfc = RadioSetting("settings.txfc",
1560
                                "TX fore color",
1561
                                RadioSettingValueList(
1562
                                    LIST_COLOR8,
1563
                                    LIST_COLOR8[_mem.settings.txfc]))
1564
            basic.append(txfc)
1565

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

    
1579
            rxled = RadioSetting("settings.rxled", "RX backlight Color",
1580
                                 RadioSettingValueList(
1581
                                     LIST_COLOR4,
1582
                                     LIST_COLOR4[_mem.settings.rxled]))
1583
            basic.append(rxled)
1584

    
1585
            txled = RadioSetting("settings.txled", "TX backlight Color",
1586
                                 RadioSettingValueList(
1587
                                     LIST_COLOR4,
1588
                                     LIST_COLOR4[_mem.settings.txled]))
1589
            basic.append(txled)
1590

    
1591
        anil = RadioSetting("settings.anil", "ANI length",
1592
                            RadioSettingValueList(
1593
                                LIST_ANIL,
1594
                                LIST_ANIL[_mem.settings.anil]))
1595
        basic.append(anil)
1596

    
1597
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1598
                            RadioSettingValueList(
1599
                                LIST_REPS,
1600
                                LIST_REPS[_mem.settings.reps]))
1601
        basic.append(reps)
1602

    
1603
        if self.COLOR_LCD4:
1604
            dsub = RadioSetting("settings.dsub", "Subtone display",
1605
                                RadioSettingValueBoolean(
1606
                                    _mem.settings.dsub))
1607
            basic.append(dsub)
1608

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

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

    
1633
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1634
                               RadioSettingValueBoolean(_mem.settings.ste))
1635
            basic.append(ste)
1636

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

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

    
1663
        if self.MODEL == "DB25-G":
1664
            mgain = RadioSetting("settings.mgain", "Auto power-on",
1665
                                 RadioSettingValueBoolean(_mem.settings.mgain))
1666
            basic.append(mgain)
1667

    
1668
        if str(_mem.fingerprint.fp) in BTECH3:
1669
            mgain = RadioSetting("settings.mgain", "Mic gain",
1670
                                 RadioSettingValueInteger(0, 120,
1671
                                                          _mem.settings.mgain))
1672
            basic.append(mgain)
1673

    
1674
        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
1675
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1676
                                 RadioSettingValueInteger(0, 60,
1677
                                                          _mem.settings.dtmfg))
1678
            basic.append(dtmfg)
1679

    
1680
        if self.VENDOR == "BTECH" and self.COLOR_LCD:
1681
            mgain = RadioSetting("settings.mgain", "Mic gain",
1682
                                 RadioSettingValueInteger(0, 127,
1683
                                                          _mem.settings.mgain))
1684
            basic.append(mgain)
1685

    
1686
            skiptx = RadioSetting("settings.skiptx", "Skip TX",
1687
                                  RadioSettingValueList(
1688
                                      LIST_SKIPTX,
1689
                                      LIST_SKIPTX[_mem.settings.skiptx]))
1690
            basic.append(skiptx)
1691

    
1692
            scmode = RadioSetting("settings.scmode", "Scan mode",
1693
                                  RadioSettingValueList(
1694
                                      LIST_SCMODE,
1695
                                      LIST_SCMODE[_mem.settings.scmode]))
1696
            basic.append(scmode)
1697

    
1698
        if self.MODEL == "KT-8R":
1699
            tmrtx = RadioSetting("settings.tmrtx", "TX in multi-standby",
1700
                                 RadioSettingValueList(
1701
                                     LIST_TMRTX,
1702
                                     LIST_TMRTX[_mem.settings.tmrtx]))
1703
            basic.append(tmrtx)
1704

    
1705
        # Advanced
1706
        def _filter(name):
1707
            filtered = ""
1708
            for char in str(name):
1709
                if char in VALID_CHARS:
1710
                    filtered += char
1711
                else:
1712
                    filtered += " "
1713
            return filtered
1714

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

    
1776
        if self.MODEL in ("UV-2501", "UV-5001"):
1777
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1778
                                   RadioSettingValueBoolean(
1779
                                       _mem.settings2.vfomren))
1780
            advanced.append(vfomren)
1781

    
1782
            reseten = RadioSetting("settings2.reseten", "RESET",
1783
                                   RadioSettingValueBoolean(
1784
                                       _mem.settings2.reseten))
1785
            advanced.append(reseten)
1786

    
1787
            menuen = RadioSetting("settings2.menuen", "Menu",
1788
                                  RadioSettingValueBoolean(
1789
                                      _mem.settings2.menuen))
1790
            advanced.append(menuen)
1791

    
1792
        # Other
1793
        def convert_bytes_to_limit(bytes):
1794
            limit = ""
1795
            for byte in bytes:
1796
                if byte < 10:
1797
                    limit += chr(byte + 0x30)
1798
                else:
1799
                    break
1800
            return limit
1801

    
1802
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1803
            _ranges = self._memobj.ranges220
1804
            ranges = "ranges220"
1805
        else:
1806
            _ranges = self._memobj.ranges
1807
            ranges = "ranges"
1808

    
1809
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1810
        val = RadioSettingValueString(0, 3, _limit)
1811
        val.set_mutable(False)
1812
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1813
        other.append(vhf_low)
1814

    
1815
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1816
        val = RadioSettingValueString(0, 3, _limit)
1817
        val.set_mutable(False)
1818
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1819
        other.append(vhf_high)
1820

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

    
1828
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1829
            val = RadioSettingValueString(0, 3, _limit)
1830
            val.set_mutable(False)
1831
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1832
            other.append(vhf2_high)
1833

    
1834
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1835
        val = RadioSettingValueString(0, 3, _limit)
1836
        val.set_mutable(False)
1837
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1838
        other.append(uhf_low)
1839

    
1840
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1841
        val = RadioSettingValueString(0, 3, _limit)
1842
        val.set_mutable(False)
1843
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1844
        other.append(uhf_high)
1845

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

    
1853
            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
1854
            val = RadioSettingValueString(0, 3, _limit)
1855
            val.set_mutable(False)
1856
            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
1857
            other.append(uhf2_high)
1858

    
1859
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1860
        val.set_mutable(False)
1861
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1862
        other.append(fp)
1863

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

    
1878
        if self.COLOR_LCD:
1879
            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
1880
                                  RadioSettingValueList(
1881
                                      LIST_VFOMR,
1882
                                      LIST_VFOMR[_mem.settings2.vfomra]))
1883
            work.append(vfomra)
1884

    
1885
            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
1886
                                  RadioSettingValueList(
1887
                                      LIST_VFOMR,
1888
                                      LIST_VFOMR[_mem.settings2.vfomrb]))
1889
            work.append(vfomrb)
1890

    
1891
            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
1892
                                  RadioSettingValueList(
1893
                                      LIST_VFOMR,
1894
                                      LIST_VFOMR[_mem.settings2.vfomrc]))
1895
            work.append(vfomrc)
1896

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

    
1910
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1911
                               RadioSettingValueBoolean(
1912
                                   _mem.settings2.keylock))
1913
        work.append(keylock)
1914

    
1915
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1916
                             RadioSettingValueInteger(0, self._upper,
1917
                                                      _mem.settings2.mrcha))
1918
        work.append(mrcha)
1919

    
1920
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1921
                             RadioSettingValueInteger(0, self._upper,
1922
                                                      _mem.settings2.mrchb))
1923
        work.append(mrchb)
1924

    
1925
        if self.COLOR_LCD:
1926
            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
1927
                                 RadioSettingValueInteger(
1928
                                     0, self._upper, _mem.settings2.mrchc))
1929
            work.append(mrchc)
1930

    
1931
            if not self.COLOR_LCD4:
1932
                mrchd = RadioSetting("settings2.mrchd", "MR D channel",
1933
                                     RadioSettingValueInteger(
1934
                                         0, self._upper, _mem.settings2.mrchd))
1935
                work.append(mrchd)
1936

    
1937
        def convert_bytes_to_freq(bytes):
1938
            real_freq = 0
1939
            for byte in bytes:
1940
                real_freq = (real_freq * 10) + byte
1941
            return chirp_common.format_freq(real_freq * 10)
1942

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

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

    
1986
        def apply_freq(setting, obj):
1987
            value = chirp_common.parse_freq(str(setting.value)) / 10
1988
            for i in range(7, -1, -1):
1989
                obj.freq[i] = value % 10
1990
                value /= 10
1991

    
1992
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1993
                                        _mem.vfo.a.freq))
1994
        val1a.set_validate_callback(my_validate)
1995
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1996
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1997
        work.append(vfoafreq)
1998

    
1999
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
2000
                                        _mem.vfo.b.freq))
2001
        val1b.set_validate_callback(my_validate)
2002
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
2003
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
2004
        work.append(vfobfreq)
2005

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

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

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

    
2029
            vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
2030
                                      RadioSettingValueList(
2031
                                          LIST_SHIFT,
2032
                                          LIST_SHIFT[_mem.vfo.b.shiftd]))
2033
            work.append(vfobshiftd)
2034

    
2035
            if self.COLOR_LCD:
2036
                vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
2037
                                          RadioSettingValueList(
2038
                                              LIST_SHIFT,
2039
                                              LIST_SHIFT[_mem.vfo.c.shiftd]))
2040
                work.append(vfocshiftd)
2041

    
2042
                if not self.COLOR_LCD4:
2043
                    val = RadioSettingValueList(LIST_SHIFT,
2044
                                                LIST_SHIFT[_mem.vfo.d.shiftd])
2045
                    vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
2046
                                              val)
2047
                    work.append(vfodshiftd)
2048

    
2049
        def convert_bytes_to_offset(bytes):
2050
            real_offset = 0
2051
            for byte in bytes:
2052
                real_offset = (real_offset * 10) + byte
2053
            return chirp_common.format_freq(real_offset * 1000)
2054

    
2055
        def apply_offset(setting, obj):
2056
            value = chirp_common.parse_freq(str(setting.value)) / 1000
2057
            for i in range(5, -1, -1):
2058
                obj.offset[i] = value % 10
2059
                value /= 10
2060

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

    
2071
                val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2072
                                                _mem.vfo.b.offset))
2073
                vfoboffset = RadioSetting("vfo.b.offset",
2074
                                          "VFO B offset (0.000-999.999)",
2075
                                          val1b)
2076
                vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
2077
                work.append(vfoboffset)
2078

    
2079
                val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
2080
                                                _mem.vfo.c.offset))
2081
                vfocoffset = RadioSetting("vfo.c.offset",
2082
                                          "VFO C offset (0.000-999.999)",
2083
                                          val1c)
2084
                vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
2085
                work.append(vfocoffset)
2086

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

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

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

    
2118
            vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
2119
                                   RadioSettingValueList(
2120
                                       LIST_TXP,
2121
                                       LIST_TXP[_mem.vfo.b.power]))
2122
            work.append(vfobtxp)
2123

    
2124
            if self.COLOR_LCD:
2125
                vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
2126
                                       RadioSettingValueList(
2127
                                           LIST_TXP,
2128
                                           LIST_TXP[_mem.vfo.c.power]))
2129
                work.append(vfoctxp)
2130

    
2131
                if not self.COLOR_LCD4:
2132
                    vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
2133
                                           RadioSettingValueList(
2134
                                               LIST_TXP,
2135
                                               LIST_TXP[_mem.vfo.d.power]))
2136
                    work.append(vfodtxp)
2137

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

    
2145
            vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
2146
                                    RadioSettingValueList(
2147
                                        LIST_WIDE,
2148
                                        LIST_WIDE[_mem.vfo.b.wide]))
2149
            work.append(vfobwide)
2150

    
2151
            if self.COLOR_LCD:
2152
                vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
2153
                                        RadioSettingValueList(
2154
                                            LIST_WIDE,
2155
                                            LIST_WIDE[_mem.vfo.c.wide]))
2156
                work.append(vfocwide)
2157

    
2158
                if not self.COLOR_LCD4:
2159
                    vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
2160
                                            RadioSettingValueList(
2161
                                                LIST_WIDE,
2162
                                                LIST_WIDE[_mem.vfo.d.wide]))
2163
                    work.append(vfodwide)
2164

    
2165
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
2166
                                RadioSettingValueList(
2167
                                    LIST_STEP,
2168
                                    LIST_STEP[_mem.vfo.a.step]))
2169
        work.append(vfoastep)
2170

    
2171
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
2172
                                RadioSettingValueList(
2173
                                    LIST_STEP,
2174
                                    LIST_STEP[_mem.vfo.b.step]))
2175
        work.append(vfobstep)
2176

    
2177
        if self.COLOR_LCD:
2178
            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
2179
                                    RadioSettingValueList(
2180
                                        LIST_STEP,
2181
                                        LIST_STEP[_mem.vfo.c.step]))
2182
            work.append(vfocstep)
2183

    
2184
            if not self.COLOR_LCD4:
2185
                vfodstep = RadioSetting("vfo.d.step", "VFO D step",
2186
                                        RadioSettingValueList(
2187
                                            LIST_STEP,
2188
                                            LIST_STEP[_mem.vfo.d.step]))
2189
                work.append(vfodstep)
2190

    
2191
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
2192
                                  RadioSettingValueList(
2193
                                      OPTSIG_LIST,
2194
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
2195
        work.append(vfoaoptsig)
2196

    
2197
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
2198
                                  RadioSettingValueList(
2199
                                      OPTSIG_LIST,
2200
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
2201
        work.append(vfoboptsig)
2202

    
2203
        if self.COLOR_LCD:
2204
            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
2205
                                      RadioSettingValueList(
2206
                                          OPTSIG_LIST,
2207
                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
2208
            work.append(vfocoptsig)
2209

    
2210
            if not self.COLOR_LCD4:
2211
                val = RadioSettingValueList(OPTSIG_LIST,
2212
                                            OPTSIG_LIST[_mem.vfo.d.optsig])
2213
                vfodoptsig = RadioSetting("vfo.d.optsig",
2214
                                          "VFO D optional signal", val)
2215
                work.append(vfodoptsig)
2216

    
2217
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
2218
                                  RadioSettingValueList(
2219
                                      SPMUTE_LIST,
2220
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
2221
        work.append(vfoaspmute)
2222

    
2223
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
2224
                                  RadioSettingValueList(
2225
                                      SPMUTE_LIST,
2226
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
2227
        work.append(vfobspmute)
2228

    
2229
        if self.COLOR_LCD:
2230
            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
2231
                                      RadioSettingValueList(
2232
                                          SPMUTE_LIST,
2233
                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
2234
            work.append(vfocspmute)
2235

    
2236
            if not self.COLOR_LCD4:
2237
                vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
2238
                                          RadioSettingValueList(
2239
                                              SPMUTE_LIST,
2240
                                              SPMUTE_LIST[_mem.vfo.d.spmute]))
2241
                work.append(vfodspmute)
2242

    
2243
        if not self.COLOR_LCD or \
2244
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
2245
            vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
2246
                                   RadioSettingValueBoolean(
2247
                                       _mem.vfo.a.scramble))
2248
            work.append(vfoascr)
2249

    
2250
            vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
2251
                                   RadioSettingValueBoolean(
2252
                                       _mem.vfo.b.scramble))
2253
            work.append(vfobscr)
2254

    
2255
        if self.COLOR_LCD and not self.VENDOR == "BTECH":
2256
            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
2257
                                   RadioSettingValueBoolean(
2258
                                       _mem.vfo.c.scramble))
2259
            work.append(vfocscr)
2260

    
2261
            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
2262
                                   RadioSettingValueBoolean(
2263
                                       _mem.vfo.d.scramble))
2264
            work.append(vfodscr)
2265

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

    
2273
            vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
2274
                                     RadioSettingValueList(
2275
                                         PTTIDCODE_LIST,
2276
                                         PTTIDCODE_LIST[_mem.vfo.b.scode]))
2277
            work.append(vfobscode)
2278

    
2279
            if self.COLOR_LCD:
2280
                vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
2281
                                         RadioSettingValueList(
2282
                                             PTTIDCODE_LIST,
2283
                                             PTTIDCODE_LIST[_mem.vfo.c.scode]))
2284
                work.append(vfocscode)
2285

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

    
2294
        if not self.MODEL == "GMRS-50X1":
2295
            pttid = RadioSetting("settings.pttid", "PTT ID",
2296
                                 RadioSettingValueList(
2297
                                     PTTID_LIST,
2298
                                     PTTID_LIST[_mem.settings.pttid]))
2299
            work.append(pttid)
2300

    
2301
        if not self.COLOR_LCD:
2302
            # FM presets
2303
            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
2304
            top.append(fm_presets)
2305

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

    
2315
            def apply_fm_preset_name(setting, obj):
2316
                valstring = str(setting.value)
2317
                for i in range(0, 6):
2318
                    if valstring[i] in VALID_CHARS:
2319
                        obj[i] = valstring[i]
2320
                    else:
2321
                        obj[i] = '0xff'
2322

    
2323
            def apply_fm_freq(setting, obj):
2324
                value = chirp_common.parse_freq(str(setting.value)) / 10
2325
                for i in range(7, -1, -1):
2326
                    obj.freq[i] = value % 10
2327
                    value /= 10
2328

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

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

    
2349
                i = i + 1
2350

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

    
2364
        rxdisable = RadioSetting("dtmf_settings.rxdisable",
2365
                                 "RX-Disable",
2366
                                 RadioSettingValueBoolean(
2367
                                     _mem.dtmf_settings.rxdisable))
2368
        dtmf_enc_settings.append(rxdisable)
2369

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

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

    
2394
        def memory2string(dmtf_mem):
2395
            dtmf_string = ""
2396
            for digit in dmtf_mem:
2397
                if digit != 255:
2398
                    index = LIST_DTMF_VALUES.index(digit)
2399
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
2400
            return dtmf_string
2401

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

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

    
2426
        line = RadioSetting("dtmf_settings.mastervice",
2427
                            "Master and Vice ID",
2428
                            RadioSettingValueBoolean(
2429
                                _mem.dtmf_settings.mastervice))
2430
        dtmf_dec_settings.append(line)
2431

    
2432
        val = RadioSettingValueString(0, 16, memory2string(
2433
                                          _mem.dtmf_settings.masterid),
2434
                                      False, CHARSET_DTMF_DIGITS)
2435
        line = RadioSetting("dtmf_settings.masterid",
2436
                            "Master Control ID ", val)
2437
        line.set_apply_callback(apply_dmtf_frame,
2438
                                _mem.dtmf_settings.masterid)
2439
        dtmf_dec_settings.append(line)
2440

    
2441
        line = RadioSetting("dtmf_settings.minspection",
2442
                            "Master Inspection",
2443
                            RadioSettingValueBoolean(
2444
                                _mem.dtmf_settings.minspection))
2445
        dtmf_dec_settings.append(line)
2446

    
2447
        line = RadioSetting("dtmf_settings.mmonitor",
2448
                            "Master Monitor",
2449
                            RadioSettingValueBoolean(
2450
                                _mem.dtmf_settings.mmonitor))
2451
        dtmf_dec_settings.append(line)
2452

    
2453
        line = RadioSetting("dtmf_settings.mstun",
2454
                            "Master Stun",
2455
                            RadioSettingValueBoolean(
2456
                                _mem.dtmf_settings.mstun))
2457
        dtmf_dec_settings.append(line)
2458

    
2459
        line = RadioSetting("dtmf_settings.mkill",
2460
                            "Master Kill",
2461
                            RadioSettingValueBoolean(
2462
                                _mem.dtmf_settings.mkill))
2463
        dtmf_dec_settings.append(line)
2464

    
2465
        line = RadioSetting("dtmf_settings.mrevive",
2466
                            "Master Revive",
2467
                            RadioSettingValueBoolean(
2468
                                _mem.dtmf_settings.mrevive))
2469
        dtmf_dec_settings.append(line)
2470

    
2471
        val = RadioSettingValueString(0, 16, memory2string(
2472
                                          _mem.dtmf_settings.viceid),
2473
                                      False, CHARSET_DTMF_DIGITS)
2474
        line = RadioSetting("dtmf_settings.viceid",
2475
                            "Vice Control ID ", val)
2476
        line.set_apply_callback(apply_dmtf_frame,
2477
                                _mem.dtmf_settings.viceid)
2478
        dtmf_dec_settings.append(line)
2479

    
2480
        line = RadioSetting("dtmf_settings.vinspection",
2481
                            "Vice Inspection",
2482
                            RadioSettingValueBoolean(
2483
                                _mem.dtmf_settings.vinspection))
2484
        dtmf_dec_settings.append(line)
2485

    
2486
        line = RadioSetting("dtmf_settings.vmonitor",
2487
                            "Vice Monitor",
2488
                            RadioSettingValueBoolean(
2489
                                _mem.dtmf_settings.vmonitor))
2490
        dtmf_dec_settings.append(line)
2491

    
2492
        line = RadioSetting("dtmf_settings.vstun",
2493
                            "Vice Stun",
2494
                            RadioSettingValueBoolean(
2495
                                _mem.dtmf_settings.vstun))
2496
        dtmf_dec_settings.append(line)
2497

    
2498
        line = RadioSetting("dtmf_settings.vkill",
2499
                            "Vice Kill",
2500
                            RadioSettingValueBoolean(
2501
                                _mem.dtmf_settings.vkill))
2502
        dtmf_dec_settings.append(line)
2503

    
2504
        line = RadioSetting("dtmf_settings.vrevive",
2505
                            "Vice Revive",
2506
                            RadioSettingValueBoolean(
2507
                                _mem.dtmf_settings.vrevive))
2508
        dtmf_dec_settings.append(line)
2509

    
2510
        val = RadioSettingValueString(0, 16, memory2string(
2511
                                          _mem.dtmf_settings.inspection),
2512
                                      False, CHARSET_DTMF_DIGITS)
2513
        line = RadioSetting("dtmf_settings.inspection",
2514
                            "Inspection", val)
2515
        line.set_apply_callback(apply_dmtf_frame,
2516
                                _mem.dtmf_settings.inspection)
2517
        dtmf_dec_settings.append(line)
2518

    
2519
        val = RadioSettingValueString(0, 16, memory2string(
2520
                                          _mem.dtmf_settings.alarmcode),
2521
                                      False, CHARSET_DTMF_DIGITS)
2522
        line = RadioSetting("dtmf_settings.alarmcode",
2523
                            "Alarm", val)
2524
        line.set_apply_callback(apply_dmtf_frame,
2525
                                _mem.dtmf_settings.alarmcode)
2526
        dtmf_dec_settings.append(line)
2527

    
2528
        val = RadioSettingValueString(0, 16, memory2string(
2529
                                          _mem.dtmf_settings.kill),
2530
                                      False, CHARSET_DTMF_DIGITS)
2531
        line = RadioSetting("dtmf_settings.kill",
2532
                            "Kill", val)
2533
        line.set_apply_callback(apply_dmtf_frame,
2534
                                _mem.dtmf_settings.kill)
2535
        dtmf_dec_settings.append(line)
2536

    
2537
        val = RadioSettingValueString(0, 16, memory2string(
2538
                                          _mem.dtmf_settings.monitor),
2539
                                      False, CHARSET_DTMF_DIGITS)
2540
        line = RadioSetting("dtmf_settings.monitor",
2541
                            "Monitor", val)
2542
        line.set_apply_callback(apply_dmtf_frame,
2543
                                _mem.dtmf_settings.monitor)
2544
        dtmf_dec_settings.append(line)
2545

    
2546
        val = RadioSettingValueString(0, 16, memory2string(
2547
                                          _mem.dtmf_settings.stun),
2548
                                      False, CHARSET_DTMF_DIGITS)
2549
        line = RadioSetting("dtmf_settings.stun",
2550
                            "Stun", val)
2551
        line.set_apply_callback(apply_dmtf_frame,
2552
                                _mem.dtmf_settings.stun)
2553
        dtmf_dec_settings.append(line)
2554

    
2555
        val = RadioSettingValueString(0, 16, memory2string(
2556
                                          _mem.dtmf_settings.revive),
2557
                                      False, CHARSET_DTMF_DIGITS)
2558
        line = RadioSetting("dtmf_settings.revive",
2559
                            "Revive", val)
2560
        line.set_apply_callback(apply_dmtf_frame,
2561
                                _mem.dtmf_settings.revive)
2562
        dtmf_dec_settings.append(line)
2563

    
2564
        def apply_dmtf_listvalue(setting, obj):
2565
            LOG.debug("Setting value: " + str(setting.value) + " from list")
2566
            val = str(setting.value)
2567
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
2568
            val = LIST_DTMF_SPECIAL_VALUES[index]
2569
            obj.set_value(val)
2570

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

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

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

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

    
2632
        # 5 Tone Settings
2633
        stds_5tone = RadioSettingGroup("stds_5tone", "Standards")
2634
        codes_5tone = RadioSettingGroup("codes_5tone", "Codes")
2635

    
2636
        group_5tone = RadioSettingGroup("group_5tone", "5 Tone Settings")
2637
        group_5tone.append(stds_5tone)
2638
        group_5tone.append(codes_5tone)
2639

    
2640
        top.append(group_5tone)
2641

    
2642
        def apply_list_value(setting, obj):
2643
            options = setting.value.get_options()
2644
            obj.set_value(options.index(str(setting.value)))
2645

    
2646
        _5tone_standards = self._memobj._5tone_std_settings
2647
        i = 0
2648
        for standard in _5tone_standards:
2649
            std_5tone = RadioSettingGroup("std_5tone_" + str(i),
2650
                                          LIST_5TONE_STANDARDS[i])
2651
            stds_5tone.append(std_5tone)
2652

    
2653
            period = standard.period
2654
            if period == 255:
2655
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] +
2656
                          " is not yet configured. Setting to 70ms.")
2657
                period = 5
2658

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

    
2670
            group_tone = standard.group_tone
2671
            if group_tone == 255:
2672
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2673
                          " is not yet configured. Setting to A.")
2674
                group_tone = 10
2675

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

    
2689
            repeat_tone = standard.repeat_tone
2690
            if repeat_tone == 255:
2691
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] +
2692
                          " is not yet configured. Setting to E.")
2693
                repeat_tone = 14
2694

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

    
2709
        def my_apply_5tonestdlist_value(setting, obj):
2710
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2711
                obj.set_value(0xFF)
2712
            else:
2713
                obj.set_value(LIST_5TONE_STANDARDS.
2714
                              index(str(setting.value)))
2715

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

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

    
2746
        def frame2string(frame):
2747
            frameString = ""
2748
            for digit in frame:
2749
                if digit != 255:
2750
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2751
            return frameString
2752

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

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

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

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

    
2797
        _5_tone_decode1 = RadioSetting(
2798
            "_5tone_settings._5tone_decode_call_frame1",
2799
            "5 Tone decode call Frame 1",
2800
            RadioSettingValueBoolean(
2801
                _mem._5tone_settings._5tone_decode_call_frame1))
2802
        group_5tone.append(_5_tone_decode1)
2803

    
2804
        _5_tone_decode2 = RadioSetting(
2805
            "_5tone_settings._5tone_decode_call_frame2",
2806
            "5 Tone decode call Frame 2",
2807
            RadioSettingValueBoolean(
2808
                _mem._5tone_settings._5tone_decode_call_frame2))
2809
        group_5tone.append(_5_tone_decode2)
2810

    
2811
        _5_tone_decode3 = RadioSetting(
2812
            "_5tone_settings._5tone_decode_call_frame3",
2813
            "5 Tone decode call Frame 3",
2814
            RadioSettingValueBoolean(
2815
                _mem._5tone_settings._5tone_decode_call_frame3))
2816
        group_5tone.append(_5_tone_decode3)
2817

    
2818
        _5_tone_decode_disp1 = RadioSetting(
2819
            "_5tone_settings._5tone_decode_disp_frame1",
2820
            "5 Tone decode disp Frame 1",
2821
            RadioSettingValueBoolean(
2822
                _mem._5tone_settings._5tone_decode_disp_frame1))
2823
        group_5tone.append(_5_tone_decode_disp1)
2824

    
2825
        _5_tone_decode_disp2 = RadioSetting(
2826
            "_5tone_settings._5tone_decode_disp_frame2",
2827
            "5 Tone decode disp Frame 2",
2828
            RadioSettingValueBoolean(
2829
                _mem._5tone_settings._5tone_decode_disp_frame2))
2830
        group_5tone.append(_5_tone_decode_disp2)
2831

    
2832
        _5_tone_decode_disp3 = RadioSetting(
2833
            "_5tone_settings._5tone_decode_disp_frame3",
2834
            "5 Tone decode disp Frame 3",
2835
            RadioSettingValueBoolean(
2836
                _mem._5tone_settings._5tone_decode_disp_frame3))
2837
        group_5tone.append(_5_tone_decode_disp3)
2838

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

    
2853
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2854
        if _5tone_delay1 == 255:
2855
            _5tone_delay1 = 20
2856

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

    
2868
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2869
        if _5tone_delay2 == 255:
2870
            _5tone_delay2 = 20
2871
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2872

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

    
2883
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2884
        if _5tone_delay3 == 255:
2885
            _5tone_delay3 = 20
2886
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2887

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

    
2898
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2899
        if ext_length == 255:
2900
            ext_length = 0
2901
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2902

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

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

    
2930
        # 2 Tone
2931
        encode_2tone = RadioSettingGroup("encode_2tone", "2 Tone Encode")
2932
        decode_2tone = RadioSettingGroup("decode_2tone", "2 Code Decode")
2933

    
2934
        top.append(encode_2tone)
2935
        top.append(decode_2tone)
2936

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

    
2943
        if duration_1st_tone <= len(LIST_5TONE_DELAY):
2944
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2945
                                        LIST_5TONE_DELAY[
2946
                                            duration_1st_tone])
2947
            line = RadioSetting("_2tone.duration_1st_tone",
2948
                                "Duration 1st Tone", val)
2949
            encode_2tone.append(line)
2950

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

    
2957
        if duration_2nd_tone <= len(LIST_5TONE_DELAY):
2958
            val = RadioSettingValueList(LIST_5TONE_DELAY,
2959
                                        LIST_5TONE_DELAY[
2960
                                            duration_2nd_tone])
2961
            line = RadioSetting("_2tone.duration_2nd_tone",
2962
                                "Duration 2nd Tone", val)
2963
            encode_2tone.append(line)
2964

    
2965
        duration_gap = self._memobj._2tone.duration_gap
2966
        if duration_gap == 255:
2967
            LOG.debug("Duration of gap is not yet " +
2968
                      "configured. Setting to 300ms")
2969
            duration_gap = 30
2970

    
2971
        if duration_gap <= len(LIST_5TONE_DELAY):
2972
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2973
                                RadioSettingValueList(LIST_5TONE_DELAY,
2974
                                                      LIST_5TONE_DELAY[
2975
                                                          duration_gap]))
2976
            encode_2tone.append(line)
2977

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

    
2988
        def apply_2tone_freq(setting, obj):
2989
            val = int(setting.value)
2990
            if (val == 0) or (val == 65535):
2991
                obj.set_value(65535)
2992
            else:
2993
                obj.set_value(val)
2994

    
2995
        i = 1
2996
        for code in self._memobj._2tone._2tone_encode:
2997
            code_2tone = RadioSettingGroup("code_2tone_" + str(i),
2998
                                           "Encode Code " + str(i))
2999
            encode_2tone.append(code_2tone)
3000

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

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

    
3021
            i = i + 1
3022

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

    
3038
        def apply_2tone_freq_pair(setting, obj):
3039
            val = int(setting.value)
3040
            derived_val = 65535
3041
            frqname = str(setting._name[-5:])
3042
            derivedname = "derived_from_" + frqname
3043

    
3044
            if (val == 0):
3045
                val = 65535
3046
                derived_val = 65535
3047
            else:
3048
                derived_val = int(round(2304000.0/val))
3049

    
3050
            obj[frqname].set_value(val)
3051
            obj[derivedname].set_value(derived_val)
3052

    
3053
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " +
3054
                      derivedname + ": " + str(derived_val))
3055

    
3056
        i = 1
3057
        for decode_code in self._memobj._2tone._2tone_decode:
3058
            _2tone_dec_code = RadioSettingGroup("code_2tone_" + str(i),
3059
                                                "Decode Code " + str(i))
3060
            decode_2tone.append(_2tone_dec_code)
3061

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

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

    
3081
                val = dec.response
3082
                if val == 255:
3083
                    LOG.debug("Response for Code " +
3084
                              str(i) + " Dec " + str(j) +
3085
                              " is not yet configured. Setting to 0.")
3086
                    val = 0
3087

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

    
3101
                val = dec.alert
3102
                if val == 255:
3103
                    LOG.debug("Alert for Code " +
3104
                              str(i) + " Dec " + str(j) +
3105
                              " is not yet configured. Setting to 0.")
3106
                    val = 0
3107

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

    
3121
            freq = self._memobj._2tone.freqs[i-1]
3122
            for char in ['A', 'B', 'C', 'D']:
3123
                setting_name = "freq" + str(char)
3124

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

    
3144
            i = i + 1
3145

    
3146
        return top
3147

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

    
3175
                    if element.has_apply_callback():
3176
                        LOG.debug("Using apply callback")
3177
                        element.run_apply_callback()
3178
                    elif setting == "volume" and self.MODEL == "KT-WP12":
3179
                        setattr(obj, setting, int(element.value) - 1)
3180
                    elif setting == "volume" and self.MODEL == "WP-9900":
3181
                        setattr(obj, setting, int(element.value) - 1)
3182
                    elif element.value.get_mutable():
3183
                        LOG.debug("Setting %s = %s" % (setting, element.value))
3184
                        setattr(obj, setting, element.value)
3185
                except Exception, e:
3186
                    LOG.debug(element.get_name())
3187
                    raise
3188

    
3189
    @classmethod
3190
    def match_model(cls, filedata, filename):
3191
        match_size = False
3192
        match_model = False
3193

    
3194
        # testing the file data size
3195
        if len(filedata) == MEM_SIZE:
3196
            match_size = True
3197

    
3198
        # testing the firmware model fingerprint
3199
        match_model = model_match(cls, filedata)
3200

    
3201
        if match_size and match_model:
3202
            return True
3203
        else:
3204
            return False
3205

    
3206

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

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

    
3278
#seekto 0x0E80;
3279
struct {
3280
  u8 unknown1;
3281
  u8 vfomr;
3282
  u8 keylock;
3283
  u8 unknown2;
3284
  u8 unknown3:4,
3285
     vfomren:1,
3286
     unknown4:1,
3287
     reseten:1,
3288
     menuen:1;
3289
  u8 unknown5[11];
3290
  u8 dispab;
3291
  u8 mrcha;
3292
  u8 mrchb;
3293
  u8 menu;
3294
} settings2;
3295

    
3296
#seekto 0x0EC0;
3297
struct {
3298
  char line1[6];
3299
  char line2[6];
3300
} poweron_msg;
3301

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

    
3319
#seekto 0x0F00;
3320
struct {
3321
  struct settings_vfo a;
3322
  struct settings_vfo b;
3323
} vfo;
3324

    
3325
#seekto 0x1000;
3326
struct {
3327
  char name[6];
3328
  u8 unknown1[10];
3329
} names[200];
3330

    
3331
#seekto 0x2400;
3332
struct {
3333
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3334
  u8 group_tone;
3335
  u8 repeat_tone;
3336
  u8 unused[13];
3337
} _5tone_std_settings[15];
3338

    
3339
#seekto 0x2500;
3340
struct {
3341
  u8 frame1[5];
3342
  u8 frame2[5];
3343
  u8 frame3[5];
3344
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3345
} _5tone_codes[15];
3346

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

    
3369
#seekto 0x2900;
3370
struct {
3371
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3372
} dtmf_codes[15];
3373

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

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

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

    
3454
#seekto 0x3000;
3455
struct {
3456
  u8 freq[8];
3457
  char broadcast_station_name[6];
3458
  u8 unknown[2];
3459
} fm_radio_preset[16];
3460

    
3461
#seekto 0x3C90;
3462
struct {
3463
  u8 vhf_low[3];
3464
  u8 vhf_high[3];
3465
  u8 uhf_low[3];
3466
  u8 uhf_high[3];
3467
} ranges;
3468

    
3469
// the UV-2501+220 & KT8900R has different zones for storing ranges
3470

    
3471
#seekto 0x3CD0;
3472
struct {
3473
  u8 vhf_low[3];
3474
  u8 vhf_high[3];
3475
  u8 unknown1[4];
3476
  u8 unknown2[6];
3477
  u8 vhf2_low[3];
3478
  u8 vhf2_high[3];
3479
  u8 unknown3[4];
3480
  u8 unknown4[6];
3481
  u8 uhf_low[3];
3482
  u8 uhf_high[3];
3483
} ranges220;
3484

    
3485
#seekto 0x3F70;
3486
struct {
3487
  char fp[6];
3488
} fingerprint;
3489

    
3490
"""
3491

    
3492

    
3493
class BTech(BTechMobileCommon):
3494
    """BTECH's UV-5001 and alike radios"""
3495
    BANDS = 2
3496
    COLOR_LCD = False
3497
    NAME_LENGTH = 6
3498

    
3499
    def set_options(self):
3500
        """This is to read the options from the image and set it in the
3501
        environment, for now just the limits of the freqs in the VHF/UHF
3502
        ranges"""
3503

    
3504
        # setting the correct ranges for each radio type
3505
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3506
            # the model 2501+220 has a segment in 220
3507
            # and a different position in the memmap
3508
            # also the QYT KT8900R
3509
            ranges = self._memobj.ranges220
3510
        else:
3511
            ranges = self._memobj.ranges
3512

    
3513
        # the normal dual bands
3514
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3515
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3516

    
3517
        # DEBUG
3518
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3519
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3520

    
3521
        # 220Mhz radios case
3522
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
3523
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
3524
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
3525
            self._220_range = vhf2
3526

    
3527
        # set the class with the real data
3528
        self._vhf_range = vhf
3529
        self._uhf_range = uhf
3530

    
3531
    def process_mmap(self):
3532
        """Process the mem map into the mem object"""
3533

    
3534
        # Get it
3535
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
3536

    
3537
        # load specific parameters from the radio image
3538
        self.set_options()
3539

    
3540

    
3541
# Declaring Aliases (Clones of the real radios)
3542
class JT2705M(chirp_common.Alias):
3543
    VENDOR = "Jetstream"
3544
    MODEL = "JT2705M"
3545

    
3546

    
3547
class JT6188Mini(chirp_common.Alias):
3548
    VENDOR = "Juentai"
3549
    MODEL = "JT-6188 Mini"
3550

    
3551

    
3552
class JT6188Plus(chirp_common.Alias):
3553
    VENDOR = "Juentai"
3554
    MODEL = "JT-6188 Plus"
3555

    
3556

    
3557
class SSGT890(chirp_common.Alias):
3558
    VENDOR = "Sainsonic"
3559
    MODEL = "GT-890"
3560

    
3561

    
3562
class ZastoneMP300(chirp_common.Alias):
3563
    VENDOR = "Zastone"
3564
    MODEL = "MP-300"
3565

    
3566

    
3567
# real radios
3568
@directory.register
3569
class UV2501(BTech):
3570
    """Baofeng Tech UV2501"""
3571
    MODEL = "UV-2501"
3572
    _fileid = [UV2501G3_fp,
3573
               UV2501G2_fp,
3574
               UV2501pp2_fp,
3575
               UV2501pp_fp]
3576

    
3577

    
3578
@directory.register
3579
class UV2501_220(BTech):
3580
    """Baofeng Tech UV2501+220"""
3581
    MODEL = "UV-2501+220"
3582
    BANDS = 3
3583
    _magic = MSTRING_220
3584
    _fileid = [UV2501_220G3_fp,
3585
               UV2501_220G2_fp,
3586
               UV2501_220_fp,
3587
               UV2501_220pp_fp]
3588

    
3589

    
3590
@directory.register
3591
class UV5001(BTech):
3592
    """Baofeng Tech UV5001"""
3593
    MODEL = "UV-5001"
3594
    _fileid = [UV5001G3_fp,
3595
               UV5001G22_fp,
3596
               UV5001G2_fp,
3597
               UV5001alpha_fp,
3598
               UV5001pp_fp]
3599
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
3600
                     chirp_common.PowerLevel("Low", watts=10)]
3601

    
3602

    
3603
@directory.register
3604
class MINI8900(BTech):
3605
    """WACCOM MINI-8900"""
3606
    VENDOR = "WACCOM"
3607
    MODEL = "MINI-8900"
3608
    _magic = MSTRING_MINI8900
3609
    _fileid = [MINI8900_fp, ]
3610
    # Clones
3611
    ALIASES = [JT6188Plus, ]
3612

    
3613

    
3614
@directory.register
3615
class KTUV980(BTech):
3616
    """QYT KT-UV980"""
3617
    VENDOR = "QYT"
3618
    MODEL = "KT-UV980"
3619
    _vhf_range = (136000000, 175000000)
3620
    _uhf_range = (400000000, 481000000)
3621
    _magic = MSTRING_MINI8900
3622
    _fileid = [KTUV980_fp, ]
3623
    # Clones
3624
    ALIASES = [JT2705M, ]
3625

    
3626
# Please note that there is a version of this radios that is a clone of the
3627
# Waccom Mini8900, maybe an early version?
3628

    
3629

    
3630
class OTGRadioV1(chirp_common.Alias):
3631
    VENDOR = 'OTGSTUFF'
3632
    MODEL = 'OTG Radio v1'
3633

    
3634

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

    
3654

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

    
3672

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

    
3686

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

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

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

    
3795
struct settings_vfo {
3796
  u8 freq[8];
3797
  u8 offset[6];
3798
  u8 unknown2[2];
3799
  ul16 rxtone;
3800
  ul16 txtone;
3801
  u8 scode;
3802
  u8 spmute;
3803
  u8 optsig;
3804
  u8 scramble;
3805
  u8 wide;
3806
  u8 power;
3807
  u8 shiftd;
3808
  u8 step;
3809
  u8 unknown3[4];
3810
};
3811

    
3812
#seekto 0x0F00;
3813
struct {
3814
  struct settings_vfo a;
3815
  struct settings_vfo b;
3816
  struct settings_vfo c;
3817
  struct settings_vfo d;
3818
} vfo;
3819

    
3820
#seekto 0x0F80;
3821
struct {
3822
  char line1[8];
3823
  char line2[8];
3824
  char line3[8];
3825
  char line4[8];
3826
  char line5[8];
3827
  char line6[8];
3828
  char line7[8];
3829
  char line8[8];
3830
} poweron_msg;
3831

    
3832
#seekto 0x1000;
3833
struct {
3834
  char name[8];
3835
  u8 unknown1[8];
3836
} names[200];
3837

    
3838
#seekto 0x2400;
3839
struct {
3840
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3841
  u8 group_tone;
3842
  u8 repeat_tone;
3843
  u8 unused[13];
3844
} _5tone_std_settings[15];
3845

    
3846
#seekto 0x2500;
3847
struct {
3848
  u8 frame1[5];
3849
  u8 frame2[5];
3850
  u8 frame3[5];
3851
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3852
} _5tone_codes[15];
3853

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

    
3876
#seekto 0x2900;
3877
struct {
3878
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3879
} dtmf_codes[15];
3880

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

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

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

    
3961
#seekto 0x3D80;
3962
struct {
3963
  u8 vhf_low[3];
3964
  u8 vhf_high[3];
3965
  u8 unknown1[4];
3966
  u8 unknown2[6];
3967
  u8 vhf2_low[3];
3968
  u8 vhf2_high[3];
3969
  u8 unknown3[4];
3970
  u8 unknown4[6];
3971
  u8 uhf_low[3];
3972
  u8 uhf_high[3];
3973
  u8 unknown5[4];
3974
  u8 unknown6[6];
3975
  u8 uhf2_low[3];
3976
  u8 uhf2_high[3];
3977
} ranges;
3978

    
3979
#seekto 0x3F70;
3980
struct {
3981
  char fp[6];
3982
} fingerprint;
3983

    
3984
"""
3985

    
3986

    
3987
class BTechColor(BTechMobileCommon):
3988
    """BTECH's Color LCD Mobile and alike radios"""
3989
    COLOR_LCD = True
3990
    NAME_LENGTH = 8
3991
    LIST_TMR = LIST_TMR16
3992

    
3993
    def process_mmap(self):
3994
        """Process the mem map into the mem object"""
3995

    
3996
        # Get it
3997
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
3998

    
3999
        # load specific parameters from the radio image
4000
        self.set_options()
4001

    
4002
    def set_options(self):
4003
        """This is to read the options from the image and set it in the
4004
        environment, for now just the limits of the freqs in the VHF/UHF
4005
        ranges"""
4006

    
4007
        # setting the correct ranges for each radio type
4008
        ranges = self._memobj.ranges
4009

    
4010
        # the normal dual bands
4011
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4012
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4013

    
4014
        # DEBUG
4015
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4016
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4017

    
4018
        # the additional bands
4019
        if self.MODEL in ["UV-25X4", "KT7900D"]:
4020
            # 200Mhz band
4021
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4022
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4023
            self._220_range = vhf2
4024

    
4025
            # 350Mhz band
4026
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4027
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4028
            self._350_range = uhf2
4029

    
4030
        # set the class with the real data
4031
        self._vhf_range = vhf
4032
        self._uhf_range = uhf
4033

    
4034

    
4035
# Declaring Aliases (Clones of the real radios)
4036
class SKT8900D(chirp_common.Alias):
4037
    VENDOR = "Surecom"
4038
    MODEL = "S-KT8900D"
4039

    
4040

    
4041
class QB25(chirp_common.Alias):
4042
    VENDOR = "Radioddity"
4043
    MODEL = "QB25"
4044

    
4045

    
4046
# real radios
4047
@directory.register
4048
class UV25X2(BTechColor):
4049
    """Baofeng Tech UV25X2"""
4050
    MODEL = "UV-25X2"
4051
    BANDS = 2
4052
    _vhf_range = (130000000, 180000000)
4053
    _uhf_range = (400000000, 521000000)
4054
    _magic = MSTRING_UV25X2
4055
    _fileid = [UV25X2_fp, ]
4056

    
4057

    
4058
@directory.register
4059
class UV25X4(BTechColor):
4060
    """Baofeng Tech UV25X4"""
4061
    MODEL = "UV-25X4"
4062
    BANDS = 4
4063
    _vhf_range = (130000000, 180000000)
4064
    _220_range = (200000000, 271000000)
4065
    _uhf_range = (400000000, 521000000)
4066
    _350_range = (350000000, 391000000)
4067
    _magic = MSTRING_UV25X4
4068
    _fileid = [UV25X4_fp, ]
4069

    
4070

    
4071
@directory.register
4072
class UV50X2(BTechColor):
4073
    """Baofeng Tech UV50X2"""
4074
    MODEL = "UV-50X2"
4075
    BANDS = 2
4076
    _vhf_range = (130000000, 180000000)
4077
    _uhf_range = (400000000, 521000000)
4078
    _magic = MSTRING_UV25X2
4079
    _fileid = [UV50X2_fp, ]
4080
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
4081
                     chirp_common.PowerLevel("Low", watts=10)]
4082

    
4083

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

    
4101

    
4102
@directory.register
4103
class KT8900D(BTechColor):
4104
    """QYT KT8900D"""
4105
    VENDOR = "QYT"
4106
    MODEL = "KT8900D"
4107
    BANDS = 2
4108
    LIST_TMR = LIST_TMR15
4109
    _vhf_range = (136000000, 175000000)
4110
    _uhf_range = (400000000, 481000000)
4111
    _magic = MSTRING_KT8900D
4112
    _fileid = [KT8900D_fp3, KT8900D_fp2, KT8900D_fp1, KT8900D_fp]
4113

    
4114
    # Clones
4115
    ALIASES = [OTGRadioV1]
4116

    
4117

    
4118
@directory.register
4119
class KT5800(BTechColor):
4120
    """QYT KT5800"""
4121
    VENDOR = "QYT"
4122
    MODEL = "KT5800"
4123
    BANDS = 2
4124
    LIST_TMR = LIST_TMR15
4125
    _vhf_range = (136000000, 175000000)
4126
    _uhf_range = (400000000, 481000000)
4127
    _magic = MSTRING_KT8900D
4128
    _fileid = [KT5800_fp, ]
4129

    
4130

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

    
4145
    @classmethod
4146
    def match_model(cls, filedata, filename):
4147
        # This model is only ever matched via metadata
4148
        return False
4149

    
4150

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

    
4167
    @classmethod
4168
    def match_model(cls, filedata, filename):
4169
        # This model is only ever matched via metadata
4170
        return False
4171

    
4172

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

    
4198
#seekto 0x1000;
4199
struct {
4200
  char name[7];
4201
  u8 unknown1[9];
4202
} names[256];
4203

    
4204
#seekto 0x2400;
4205
struct {
4206
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4207
  u8 group_tone;
4208
  u8 repeat_tone;
4209
  u8 unused[13];
4210
} _5tone_std_settings[15];
4211

    
4212
#seekto 0x2500;
4213
struct {
4214
  u8 frame1[5];
4215
  u8 frame2[5];
4216
  u8 frame3[5];
4217
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4218
} _5tone_codes[15];
4219

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

    
4242
#seekto 0x2900;
4243
struct {
4244
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4245
} dtmf_codes[15];
4246

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

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

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

    
4327
#seekto 0x3000;
4328
struct {
4329
  u8 freq[8];
4330
  char broadcast_station_name[6];
4331
  u8 unknown[2];
4332
} fm_radio_preset[16];
4333

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

    
4365

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

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

    
4419
struct settings_vfo {
4420
  u8 freq[8];
4421
  u8 offset[6];
4422
  u8 unknown2[2];
4423
  ul16 rxtone;
4424
  ul16 txtone;
4425
  u8 scode;
4426
  u8 spmute;
4427
  u8 optsig;
4428
  u8 scramble;
4429
  u8 wide;
4430
  u8 power;
4431
  u8 shiftd;
4432
  u8 step;
4433
  u8 unknown3[4];
4434
};
4435

    
4436
#seekto 0x3300;
4437
struct {
4438
  struct settings_vfo a;
4439
  struct settings_vfo b;
4440
  struct settings_vfo c;
4441
  struct settings_vfo d;
4442
} vfo;
4443

    
4444
#seekto 0x3D80;
4445
struct {
4446
  u8 vhf_low[3];
4447
  u8 vhf_high[3];
4448
  u8 unknown1[4];
4449
  u8 unknown2[6];
4450
  u8 vhf2_low[3];
4451
  u8 vhf2_high[3];
4452
  u8 unknown3[4];
4453
  u8 unknown4[6];
4454
  u8 uhf_low[3];
4455
  u8 uhf_high[3];
4456
  u8 unknown5[4];
4457
  u8 unknown6[6];
4458
  u8 uhf2_low[3];
4459
  u8 uhf2_high[3];
4460
} ranges;
4461

    
4462
#seekto 0x33B0;
4463
struct {
4464
  char line[16];
4465
} static_msg;
4466

    
4467
#seekto 0x3F70;
4468
struct {
4469
  char fp[6];
4470
} fingerprint;
4471

    
4472
"""
4473

    
4474

    
4475
class BTechGMRS(BTechMobileCommon):
4476
    """BTECH's GMRS Mobile"""
4477
    COLOR_LCD = True
4478
    COLOR_LCD2 = True
4479
    NAME_LENGTH = 7
4480
    UPLOAD_MEM_SIZE = 0X3400
4481

    
4482
    def process_mmap(self):
4483
        """Process the mem map into the mem object"""
4484

    
4485
        # Get it
4486
        self._memobj = bitwise.parse(GMRS_MEM_FORMAT, self._mmap)
4487

    
4488
        # load specific parameters from the radio image
4489
        self.set_options()
4490

    
4491
    def set_options(self):
4492
        """This is to read the options from the image and set it in the
4493
        environment, for now just the limits of the freqs in the VHF/UHF
4494
        ranges"""
4495

    
4496
        # setting the correct ranges for each radio type
4497
        ranges = self._memobj.ranges
4498

    
4499
        # the normal dual bands
4500
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4501
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4502

    
4503
        # DEBUG
4504
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4505
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4506

    
4507
        # set the class with the real data
4508
        self._vhf_range = vhf
4509
        self._uhf_range = uhf
4510

    
4511

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

    
4528

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

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

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

    
4637
struct settings_vfo {
4638
  u8 freq[8];
4639
  u8 offset[6];
4640
  u8 unknown2[2];
4641
  ul16 rxtone;
4642
  ul16 txtone;
4643
  u8 scode;
4644
  u8 spmute;
4645
  u8 optsig;
4646
  u8 scramble;
4647
  u8 wide;
4648
  u8 power;
4649
  u8 shiftd;
4650
  u8 step;
4651
  u8 unknown3[4];
4652
};
4653

    
4654
#seekto 0x0F00;
4655
struct {
4656
  struct settings_vfo a;
4657
  struct settings_vfo b;
4658
  struct settings_vfo c;
4659
  struct settings_vfo d;
4660
} vfo;
4661

    
4662
#seekto 0x0FE0;
4663
struct {
4664
  char line[16];
4665
} static_msg;
4666

    
4667
#seekto 0x1000;
4668
struct {
4669
  char name[8];
4670
  u8 unknown1[8];
4671
} names[200];
4672

    
4673
#seekto 0x2400;
4674
struct {
4675
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
4676
  u8 group_tone;
4677
  u8 repeat_tone;
4678
  u8 unused[13];
4679
} _5tone_std_settings[15];
4680

    
4681
#seekto 0x2500;
4682
struct {
4683
  u8 frame1[5];
4684
  u8 frame2[5];
4685
  u8 frame3[5];
4686
  u8 standard;   // one out of LIST_5TONE_STANDARDS
4687
} _5tone_codes[15];
4688

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

    
4711
#seekto 0x2900;
4712
struct {
4713
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
4714
} dtmf_codes[15];
4715

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

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

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

    
4796
#seekto 0x3D80;
4797
struct {
4798
  u8 vhf_low[3];
4799
  u8 vhf_high[3];
4800
  u8 unknown1[4];
4801
  u8 unknown2[6];
4802
  u8 vhf2_low[3];
4803
  u8 vhf2_high[3];
4804
  u8 unknown3[4];
4805
  u8 unknown4[6];
4806
  u8 uhf_low[3];
4807
  u8 uhf_high[3];
4808
  u8 unknown5[4];
4809
  u8 unknown6[6];
4810
  u8 uhf2_low[3];
4811
  u8 uhf2_high[3];
4812
} ranges;
4813

    
4814
#seekto 0x3F70;
4815
struct {
4816
  char fp[6];
4817
} fingerprint;
4818

    
4819
"""
4820

    
4821

    
4822
class QYTColorHT(BTechMobileCommon):
4823
    """QTY's Color LCD Handheld and alike radios"""
4824
    COLOR_LCD = True
4825
    COLOR_LCD3 = True
4826
    NAME_LENGTH = 8
4827
    LIST_TMR = LIST_TMR15
4828

    
4829
    def process_mmap(self):
4830
        """Process the mem map into the mem object"""
4831

    
4832
        # Get it
4833
        self._memobj = bitwise.parse(COLORHT_MEM_FORMAT, self._mmap)
4834

    
4835
        # load specific parameters from the radio image
4836
        self.set_options()
4837

    
4838
    def set_options(self):
4839
        """This is to read the options from the image and set it in the
4840
        environment, for now just the limits of the freqs in the VHF/UHF
4841
        ranges"""
4842

    
4843
        # setting the correct ranges for each radio type
4844
        ranges = self._memobj.ranges
4845

    
4846
        # the normal dual bands
4847
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
4848
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
4849

    
4850
        # DEBUG
4851
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
4852
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
4853

    
4854
        # the additional bands
4855
        if self.MODEL in ["KT-8R"]:
4856
            # 200Mhz band
4857
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
4858
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
4859
            self._220_range = vhf2
4860

    
4861
            # 350Mhz band
4862
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
4863
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
4864
            self._350_range = uhf2
4865

    
4866
        # set the class with the real data
4867
        self._vhf_range = vhf
4868
        self._uhf_range = uhf
4869

    
4870

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

    
4888

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

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

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

    
4997
struct settings_vfo {
4998
  u8 freq[8];
4999
  u8 offset[6];
5000
  u8 unknown2[2];
5001
  ul16 rxtone;
5002
  ul16 txtone;
5003
  u8 scode;
5004
  u8 spmute;
5005
  u8 optsig;
5006
  u8 scramble;
5007
  u8 wide;
5008
  u8 power;
5009
  u8 shiftd;
5010
  u8 step;
5011
  u8 unknown3[4];
5012
};
5013

    
5014
#seekto 0x0F00;
5015
struct {
5016
  struct settings_vfo a;
5017
  struct settings_vfo b;
5018
  struct settings_vfo c;
5019
  struct settings_vfo d;
5020
} vfo;
5021

    
5022
#seekto 0x0F80;
5023
struct {
5024
  char line1[8];
5025
  char line2[8];
5026
  char line3[8];
5027
  char line4[8];
5028
  char line5[8];
5029
  char line6[8];
5030
  char line7[8];
5031
  char line8[8];
5032
} poweron_msg;
5033

    
5034
#seekto 0x0FE0;
5035
struct {
5036
  char line[16];
5037
} static_msg;
5038

    
5039
#seekto 0x1000;
5040
struct {
5041
  char name[7];
5042
  u8 unknown1[9];
5043
} names[200];
5044

    
5045
#seekto 0x2400;
5046
struct {
5047
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5048
  u8 group_tone;
5049
  u8 repeat_tone;
5050
  u8 unused[13];
5051
} _5tone_std_settings[15];
5052

    
5053
#seekto 0x2500;
5054
struct {
5055
  u8 frame1[5];
5056
  u8 frame2[5];
5057
  u8 frame3[5];
5058
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5059
} _5tone_codes[15];
5060

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

    
5083
#seekto 0x2900;
5084
struct {
5085
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5086
} dtmf_codes[15];
5087

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

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

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

    
5168
#seekto 0x3D80;
5169
struct {
5170
  u8 vhf_low[3];
5171
  u8 vhf_high[3];
5172
  u8 unknown1[4];
5173
  u8 unknown2[6];
5174
  u8 vhf2_low[3];
5175
  u8 vhf2_high[3];
5176
  u8 unknown3[4];
5177
  u8 unknown4[6];
5178
  u8 uhf_low[3];
5179
  u8 uhf_high[3];
5180
  u8 unknown5[4];
5181
  u8 unknown6[6];
5182
  u8 uhf2_low[3];
5183
  u8 uhf2_high[3];
5184
} ranges;
5185

    
5186
#seekto 0x3F70;
5187
struct {
5188
  char fp[6];
5189
} fingerprint;
5190

    
5191
"""
5192

    
5193

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

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

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

    
5306
struct settings_vfo {
5307
  u8 freq[8];
5308
  u8 offset[6];
5309
  u8 unknown2[2];
5310
  ul16 rxtone;
5311
  ul16 txtone;
5312
  u8 scode;
5313
  u8 spmute;
5314
  u8 optsig;
5315
  u8 scramble;
5316
  u8 wide;
5317
  u8 power;
5318
  u8 shiftd;
5319
  u8 step;
5320
  u8 unknown3[4];
5321
};
5322

    
5323
#seekto 0x0F00;
5324
struct {
5325
  struct settings_vfo a;
5326
  struct settings_vfo b;
5327
  struct settings_vfo c;
5328
  struct settings_vfo d;
5329
} vfo;
5330

    
5331
#seekto 0x0F80;
5332
struct {
5333
  char line1[8];
5334
  char line2[8];
5335
  char line3[8];
5336
  char line4[8];
5337
  char line5[8];
5338
  char line6[8];
5339
  char line7[8];
5340
  char line8[8];
5341
} poweron_msg;
5342

    
5343
#seekto 0x0FE0;
5344
struct {
5345
  char line[16];
5346
} static_msg;
5347

    
5348
#seekto 0x1000;
5349
struct {
5350
  char name[7];
5351
  u8 unknown1[9];
5352
} names[200];
5353

    
5354
#seekto 0x2400;
5355
struct {
5356
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
5357
  u8 group_tone;
5358
  u8 repeat_tone;
5359
  u8 unused[13];
5360
} _5tone_std_settings[15];
5361

    
5362
#seekto 0x2500;
5363
struct {
5364
  u8 frame1[5];
5365
  u8 frame2[5];
5366
  u8 frame3[5];
5367
  u8 standard;   // one out of LIST_5TONE_STANDARDS
5368
} _5tone_codes[15];
5369

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

    
5392
#seekto 0x2900;
5393
struct {
5394
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
5395
} dtmf_codes[15];
5396

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

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

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

    
5477
#seekto 0x3D80;
5478
struct {
5479
  u8 vhf_low[3];
5480
  u8 vhf_high[3];
5481
  u8 unknown1[4];
5482
  u8 unknown2[6];
5483
  u8 vhf2_low[3];
5484
  u8 vhf2_high[3];
5485
  u8 unknown3[4];
5486
  u8 unknown4[6];
5487
  u8 uhf_low[3];
5488
  u8 uhf_high[3];
5489
  u8 unknown5[4];
5490
  u8 unknown6[6];
5491
  u8 uhf2_low[3];
5492
  u8 uhf2_high[3];
5493
} ranges;
5494

    
5495
#seekto 0x3F70;
5496
struct {
5497
  char fp[6];
5498
} fingerprint;
5499

    
5500
"""
5501

    
5502

    
5503
class BTechColorWP(BTechMobileCommon):
5504
    """BTECH's Color WP Mobile and alike radios"""
5505
    COLOR_LCD = True
5506
    COLOR_LCD4 = True
5507
    NAME_LENGTH = 7
5508
    LIST_TMR = LIST_TMR7
5509

    
5510
    def set_options(self):
5511
        """This is to read the options from the image and set it in the
5512
        environment, for now just the limits of the freqs in the VHF/UHF
5513
        ranges"""
5514

    
5515
        # setting the correct ranges for each radio type
5516
        ranges = self._memobj.ranges
5517

    
5518
        # the normal dual bands
5519
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
5520
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
5521

    
5522
        # DEBUG
5523
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
5524
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
5525

    
5526
        # set the class with the real data
5527
        self._vhf_range = vhf
5528
        self._uhf_range = uhf
5529

    
5530

    
5531
# real radios
5532
@directory.register
5533
class KTWP12(BTechColorWP):
5534
    """QYT KT-WP12"""
5535
    VENDOR = "QYT"
5536
    MODEL = "KT-WP12"
5537
    BANDS = 2
5538
    # <delete me> LIST_TMR = LIST_TMR7
5539
    UPLOAD_MEM_SIZE = 0X3100
5540
    # <delete me> UPLOAD_MEM_SIZE = 0X4000
5541
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
5542
                     chirp_common.PowerLevel("Low", watts=5)]
5543
    _upper = 199
5544
    _magic = MSTRING_KTWP12
5545
    _fileid = [KTWP12_fp, ]
5546
    _gmrs = False
5547

    
5548
    def process_mmap(self):
5549
        """Process the mem map into the mem object"""
5550

    
5551
        # Get it
5552
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
5553

    
5554
        # load specific parameters from the radio image
5555
        self.set_options()
5556

    
5557

    
5558
@directory.register
5559
class WP9900(BTechColorWP):
5560
    """Anysecu WP-9900"""
5561
    VENDOR = "Anysecu"
5562
    MODEL = "WP-9900"
5563
    BANDS = 2
5564
    # <delete me> LIST_TMR = LIST_TMR7
5565
    UPLOAD_MEM_SIZE = 0X3100
5566
    # <delete me> UPLOAD_MEM_SIZE = 0X4000
5567
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
5568
                     chirp_common.PowerLevel("Low", watts=5)]
5569
    _upper = 199
5570
    _magic = MSTRING_KTWP12
5571
    _fileid = [WP9900_fp, ]
5572
    _gmrs = False
5573

    
5574
    def process_mmap(self):
5575
        """Process the mem map into the mem object"""
5576

    
5577
        # Get it
5578
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
5579

    
5580
        # load specific parameters from the radio image
5581
        self.set_options()
(10-10/13)