Project

General

Profile

New Model #9827 » WP-9900 MCU WP3094 - read test.py

Jim Unroe, 05/09/2022 07:38 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  ##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
# B-TECH GMRS-20V2
205
GMRS20V2_fp = "WP4204"
206

    
207
# special var to know when we found a BTECH Gen 3
208
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
209

    
210

    
211
# WACCOM Mini-8900
212
MINI8900_fp = "M28854"
213

    
214

    
215
# QYT KT-UV980
216
KTUV980_fp = "H28854"
217

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

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

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

    
252
# QB25 (quad band) - a clone of KT7900D
253
QB25_fp = "QB-25"
254

    
255
# KT8900D (dual band)
256
KT8900D_fp = "VC2002"
257
KT8900D_fp1 = "VC8632"
258
KT8900D_fp2 = "VC3402"
259
KT8900D_fp3 = "VC7062"
260

    
261
# LUITON LT-588UV
262
LT588UV_fp = "V2G1F4"
263
# Added by rstrickoff gen 2 id
264
LT588UV_fp1 = "V2G214"
265

    
266
# QYT KT-8R (quad band ht)
267
KT8R_fp = "MCB264"
268
KT8R_fp1 = "MCB284"
269
KT8R_fp2 = "MC5264"
270

    
271
# QYT KT5800 (dual band)
272
KT5800_fp = "VCB222"
273

    
274
# QYT KT980Plus (dual band)
275
KT980PLUS_fp = "VC2002"
276
KT980PLUS_fp1 = "VC6042"
277

    
278
# Radioddity DB25-G (gmrs)
279
DB25G_fp = "VC6182"
280
DB25G_fp1 = "VC7062"
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 BTECH GMRS-20V2
305
MSTRING_GMRS20V2 = "\x55\x20\x21\x03\x27\xFF\xDC\x02"
306
# for the QYT KT-8R
307
MSTRING_KT8R = "\x55\x20\x17\x07\x03\xFF\xDC\x02"
308
# for the Anysecu WP-9900
309
MSTRING_WP9900 = "\x55\x20\x18\x11\x02\xFF\xDC\x02"
310

    
311

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

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

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

    
332
        # restore the default serial timeout
333
        radio.pipe.timeout = STIMEOUT
334

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

    
338

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

    
342
    data = ""
343

    
344
    try:
345
        data = radio.pipe.read(amount)
346

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

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

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

    
361
    except:
362
        raise errors.RadioError("Error reading data from radio")
363

    
364
    return data
365

    
366

    
367
def _send(radio, data):
368
    """Send data to the radio device"""
369

    
370
    try:
371
        radio.pipe.write(data)
372

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

    
380

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

    
388
    return frame
389

    
390

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

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

    
397
    # get the whole block
398
    block = _rawrecv(radio, BLOCK_SIZE + 5)
399

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

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

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

    
415
    # return the data
416
    return block[5:]
417

    
418

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

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

    
428
    # send the magic word
429
    _send(radio, radio._magic)
430

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

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

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

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

    
452
            break
453

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

    
458
    # pause here for the radio to catch up
459
    sleep(0.1)
460

    
461
    # the OEM software reads this additional block, so we will, too
462

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

    
469
    # restore the default serial timeout
470
    radio.pipe.timeout = STIMEOUT
471

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

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

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

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

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

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

    
508
    return True
509

    
510

    
511
def _download(radio):
512
    """Get the memory map"""
513

    
514
    # UI progress
515
    status = chirp_common.Status()
516

    
517
    # put radio in program mode and identify it
518
    _do_ident(radio, status)
519

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

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

    
531
        # read
532
        d = _recv(radio, addr)
533

    
534
        # aggregate the data
535
        data += d
536

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

    
542
    return data
543

    
544

    
545
def _upload(radio):
546
    """Upload procedure"""
547

    
548
    ## uploads not implemented
549

    
550

    
551
def model_match(cls, data):
552
    """Match the opened/downloaded image to the correct version"""
553
    rid = data[0x3f70:0x3f76]
554

    
555
    if rid in cls._fileid:
556
        return True
557

    
558
    return False
559

    
560

    
561
def _decode_ranges(low, high):
562
    """Unpack the data in the ranges zones in the memmap and return
563
    a tuple with the integer corresponding to the Mhz it means"""
564
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
565
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
566
    ilow *= 1000000
567
    ihigh *= 1000000
568

    
569
    return (ilow, ihigh)
570

    
571

    
572
def _split(rf, f1, f2):
573
    """Returns False if the two freqs are in the same band (no split)
574
    or True otherwise"""
575

    
576
    # determine if the two freqs are in the same band
577
    for low, high in rf.valid_bands:
578
        if f1 >= low and f1 <= high and \
579
                f2 >= low and f2 <= high:
580
            # if the two freqs are on the same Band this is not a split
581
            return False
582

    
583
    # if you get here is because the freq pairs are split
584
    return True
585

    
586

    
587
class BTechMobileCommon(chirp_common.CloneModeRadio,
588
                        chirp_common.ExperimentalRadio):
589
    """BTECH's UV-5001 and alike radios"""
590
    VENDOR = "BTECH"
591
    MODEL = ""
592
    IDENT = ""
593
    BANDS = 2
594
    COLOR_LCD = False
595
    COLOR_LCD2 = False  # BTech Original GMRS Radios
596
    COLOR_LCD3 = False  # Color HT Radios
597
    COLOR_LCD4 = False  # Waterproof Mobile Radios
598
    NAME_LENGTH = 6
599
    UPLOAD_MEM_SIZE = 0X3100
600
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
601
                     chirp_common.PowerLevel("Low", watts=10)]
602
    _vhf_range = (130000000, 180000000)
603
    _220_range = (200000000, 271000000)
604
    _uhf_range = (400000000, 521000000)
605
    _350_range = (350000000, 391000000)
606
    _upper = 199
607
    _magic = MSTRING
608
    _fileid = None
609
    _id2 = False
610
    btech3 = False
611
    _gmrs = False
612

    
613
    @classmethod
614
    def get_prompts(cls):
615
        rp = chirp_common.RadioPrompts()
616
        rp.experimental = \
617
            ('This driver is experimental.\n'
618
             '\n'
619
             'Please keep a copy of your memories with the original software '
620
             'if you treasure them, this driver is new and may contain'
621
             ' bugs.\n'
622
             '\n'
623
             )
624
        rp.pre_download = _(dedent("""\
625
            Follow these instructions to download your info:
626

    
627
            1 - Turn off your radio
628
            2 - Connect your interface cable
629
            3 - Turn on your radio
630
            4 - Do the download of your radio data
631

    
632
            """))
633
        rp.pre_upload = _(dedent("""\
634
            Follow these instructions to upload your info:
635

    
636
            1 - Turn off your radio
637
            2 - Connect your interface cable
638
            3 - Turn on your radio
639
            4 - Do the upload of your radio data
640

    
641
            """))
642
        return rp
643

    
644
    def get_features(self):
645
        """Get the radio's features"""
646

    
647
        # we will use the following var as global
648
        global POWER_LEVELS
649

    
650
        rf = chirp_common.RadioFeatures()
651
        rf.has_settings = False ##
652
        rf.has_bank = False
653
        rf.has_tuning_step = False
654
        rf.can_odd_split = True
655
        rf.has_name = True
656
        rf.has_offset = True
657
        rf.has_mode = True
658
        rf.has_dtcs = True
659
        rf.has_rx_dtcs = True
660
        rf.has_dtcs_polarity = True
661
        rf.has_ctone = True
662
        rf.has_cross = True
663
        rf.valid_modes = MODES
664
        rf.valid_characters = VALID_CHARS
665
        rf.valid_name_length = self.NAME_LENGTH
666
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
667
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
668
        rf.valid_cross_modes = [
669
            "Tone->Tone",
670
            "DTCS->",
671
            "->DTCS",
672
            "Tone->DTCS",
673
            "DTCS->Tone",
674
            "->Tone",
675
            "DTCS->DTCS"]
676
        rf.valid_skips = SKIP_VALUES
677
        rf.valid_dtcs_codes = DTCS
678
        rf.valid_tuning_steps = STEPS
679
        rf.memory_bounds = (0, self._upper)
680

    
681
        # power levels
682
        POWER_LEVELS = self._power_levels
683
        rf.valid_power_levels = POWER_LEVELS
684

    
685
        # normal dual bands
686
        rf.valid_bands = [self._vhf_range, self._uhf_range]
687

    
688
        # 220 band
689
        if self.BANDS == 3 or self.BANDS == 4:
690
            rf.valid_bands.append(self._220_range)
691

    
692
        # 350 band
693
        if self.BANDS == 4:
694
            rf.valid_bands.append(self._350_range)
695

    
696
        return rf
697

    
698
    def validate_memory(self, mem):
699
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
700

    
701
        _msg_duplex1 = 'Memory location only supports "Low"'
702
        _msg_duplex2 = 'Memory location only supports "off"'
703
        _msg_duplex3 = 'Memory location only supports "(None)", "+" or "off"'
704

    
705
        ## need to evaluate this code
706
        """
707
        if self._gmrs:
708
            if mem.number < 1 or mem.number > 30:
709
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
710
                    if mem.duplex not in ['', 'off']:
711
                        # warn user wrong Duplex
712
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
713
                    if mem.power != self._power_levels[2]:
714
                        # warn user wrong Duplex
715
                        msgs.append(chirp_common.ValidationError(_msg_duplex1))
716

    
717
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
718
                    if mem.duplex not in ['off', ]:
719
                        # warn user wrong Duplex
720
                        msgs.append(chirp_common.ValidationError(_msg_duplex2))
721

    
722
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
723
                    if mem.duplex not in ['', '+', 'off']:
724
                        # warn user wrong Duplex
725
                        msgs.append(chirp_common.ValidationError(_msg_duplex3))
726
        """
727

    
728
        return msgs
729

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

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

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

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

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

    
766
            tone = DTCS[index]
767
            return 'DTCS', tone, pol
768

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

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

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

    
801
        # Memory number
802
        mem.number = number
803

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

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

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

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

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

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

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

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

    
853
        if not self.COLOR_LCD or \
854
                (self.COLOR_LCD and not self.VENDOR == "BTECH"):
855
            scramble = RadioSetting("scramble", "Scramble",
856
                                    RadioSettingValueBoolean(bool(
857
                                        _mem.scramble)))
858
            mem.extra.append(scramble)
859

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

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

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

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

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

    
889
        return mem
890

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

    
897
        mem_was_empty = False
898
        # same method as used in get_memory for determining if mem is empty
899
        # doing this BEFORE overwriting it with new values ...
900
        if _mem.get_raw()[0] == "\xFF":
901
            LOG.debug("This mem was empty before")
902
            mem_was_empty = True
903

    
904
        # if empty memmory
905
        if mem.empty:
906
            # the channel itself
907
            _mem.set_raw("\xFF" * 16)
908
            # the name tag
909
            _names.set_raw("\xFF" * 16)
910
            return
911

    
912
        if mem_was_empty:
913
            # Zero the whole memory if we're making it unempty for
914
            # the first time
915
            LOG.debug('Zeroing new memory')
916
            _mem.set_raw('\x00' * 16)
917

    
918
        if self._gmrs:
919
            if mem.number >= 1 and mem.number <= 30:
920
                GMRS_FREQ = int(GMRS_FREQS[mem.number - 1] * 1000000)
921
                mem.freq = GMRS_FREQ
922
                if mem.number <= 22:
923
                    mem.duplex = ''
924
                    mem.offset = 0
925
                    mem.mode = "FM"
926
                    mem.power = POWER_LEVELS[0]
927
                    if mem.number >= 8 and mem.number <= 14:
928
                        mem.duplex = 'off'
929
                        mem.offset = 0
930
                        mem.mode = "NFM"
931
                        mem.power = POWER_LEVELS[2]
932
                if mem.number > 22:
933
                    mem.duplex = '+'
934
                    mem.offset = 5000000
935
                    mem.mode = "FM"
936
                    mem.power = POWER_LEVELS[0]
937
            elif float(mem.freq) / 1000000 in GMRS_FREQS:
938
                if float(mem.freq) / 1000000 in GMRS_FREQS1:
939
                    mem.duplex = ''
940
                    mem.offset = 0
941
                    mem.mode = "FM"
942
                    mem.power = POWER_LEVELS[0]
943
                if float(mem.freq) / 1000000 in GMRS_FREQS2:
944
                    mem.duplex = 'off'
945
                    mem.offset = 0
946
                    mem.mode = "NFM"
947
                    mem.power = POWER_LEVELS[2]
948
                if float(mem.freq) / 1000000 in GMRS_FREQS3:
949
                    if mem.duplex == '+':
950
                        mem.offset = 5000000
951
                    else:
952
                        mem.offset = 0
953
                    mem.mode = "FM"
954
                    mem.power = POWER_LEVELS[0]
955
            else:
956
                mem.duplex = 'off'
957
                mem.offset = 0
958

    
959
        # frequency
960
        _mem.rxfreq = mem.freq / 10
961

    
962
        # duplex
963
        if mem.duplex == "+":
964
            _mem.txfreq = (mem.freq + mem.offset) / 10
965
        elif mem.duplex == "-":
966
            _mem.txfreq = (mem.freq - mem.offset) / 10
967
        elif mem.duplex == "off":
968
            for i in _mem.txfreq:
969
                i.set_raw("\xFF")
970
        elif mem.duplex == "split":
971
            _mem.txfreq = mem.offset / 10
972
        else:
973
            _mem.txfreq = mem.freq / 10
974

    
975
        # tone data
976
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
977
            chirp_common.split_tone_encode(mem)
978
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
979
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
980

    
981
        # name TAG of the channel
982
        if len(mem.name) < self.NAME_LENGTH:
983
            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
984
            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
985
        _names.name = str(mem.name).replace(" ", "\xFF")
986

    
987
        # power, # default power level is high
988
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
989

    
990
        # wide/narrow
991
        _mem.wide = MODES.index(mem.mode)
992

    
993
        # scan add property
994
        _mem.add = SKIP_VALUES.index(mem.skip)
995

    
996
        # reseting unknowns, this have to be set by hand
997
        _mem.unknown0 = 0
998
        _mem.unknown1 = 0
999
        _mem.unknown2 = 0
1000
        _mem.unknown3 = 0
1001
        _mem.unknown4 = 0
1002
        _mem.unknown5 = 0
1003
        _mem.unknown6 = 0
1004

    
1005
        def _zero_settings():
1006
            _mem.spmute = 0
1007
            _mem.optsig = 0
1008
            _mem.scramble = 0
1009
            _mem.bcl = 0
1010
            _mem.pttid = 0
1011
            _mem.scode = 0
1012

    
1013
        if self.COLOR_LCD and _mem.scramble:
1014
            LOG.info('Resetting scramble bit for BTECH COLOR_LCD variant')
1015
            _mem.scramble = 0
1016

    
1017
        # extra settings
1018
        if len(mem.extra) > 0:
1019
            # there are setting, parse
1020
            LOG.debug("Extra-Setting supplied. Setting them.")
1021
            # Zero them all first so any not provided by model don't
1022
            # stay set
1023
            _zero_settings()
1024
            for setting in mem.extra:
1025
                setattr(_mem, setting.get_name(), setting.value)
1026
        else:
1027
            if mem.empty:
1028
                LOG.debug("New mem is empty.")
1029
            else:
1030
                LOG.debug("New mem is NOT empty")
1031
                # set extra-settings to default ONLY when apreviously empty or
1032
                # deleted memory was edited to prevent errors such as #4121
1033
                if mem_was_empty:
1034
                    LOG.debug("old mem was empty. Setting default for extras.")
1035
                    _zero_settings()
1036

    
1037
        return mem
1038

    
1039
    def set_settings(self, settings):
1040
        _settings = self._memobj.settings
1041
        for element in settings:
1042
            if not isinstance(element, RadioSetting):
1043
                if element.get_name() == "fm_preset":
1044
                    self._set_fm_preset(element)
1045
                else:
1046
                    self.set_settings(element)
1047
                    continue
1048
            else:
1049
                try:
1050
                    name = element.get_name()
1051
                    if "." in name:
1052
                        bits = name.split(".")
1053
                        obj = self._memobj
1054
                        for bit in bits[:-1]:
1055
                            if "/" in bit:
1056
                                bit, index = bit.split("/", 1)
1057
                                index = int(index)
1058
                                obj = getattr(obj, bit)[index]
1059
                            else:
1060
                                obj = getattr(obj, bit)
1061
                        setting = bits[-1]
1062
                    else:
1063
                        obj = _settings
1064
                        setting = element.get_name()
1065

    
1066
                    if element.has_apply_callback():
1067
                        LOG.debug("Using apply callback")
1068
                        element.run_apply_callback()
1069
                    elif setting == "volume" and self.MODEL == "WP-9900":
1070
                        setattr(obj, setting, int(element.value) - 1)
1071
                    elif element.value.get_mutable():
1072
                        LOG.debug("Setting %s = %s" % (setting, element.value))
1073
                        setattr(obj, setting, element.value)
1074
                except Exception, e:
1075
                    LOG.debug(element.get_name())
1076
                    raise
1077

    
1078
    @classmethod
1079
    def match_model(cls, filedata, filename):
1080
        match_size = False
1081
        match_model = False
1082

    
1083
        # testing the file data size
1084
        if len(filedata) == MEM_SIZE:
1085
            match_size = True
1086

    
1087
        # testing the firmware model fingerprint
1088
        match_model = model_match(cls, filedata)
1089

    
1090
        if match_size and match_model:
1091
            return True
1092
        else:
1093
            return False
1094

    
1095

    
1096
MEM_FORMAT = """
1097
#seekto 0x0000;
1098
struct {
1099
  lbcd rxfreq[4];
1100
  lbcd txfreq[4];
1101
  ul16 rxtone;
1102
  ul16 txtone;
1103
  u8 unknown0:4,
1104
     scode:4;
1105
  u8 unknown1:2,
1106
     spmute:2,
1107
     unknown2:2,
1108
     optsig:2;
1109
  u8 unknown3:3,
1110
     scramble:1,
1111
     unknown4:3,
1112
     power:1;
1113
  u8 unknown5:1,
1114
     wide:1,
1115
     unknown6:2,
1116
     bcl:1,
1117
     add:1,
1118
     pttid:2;
1119
} memory[200];
1120

    
1121
#seekto 0x0E00;
1122
struct {
1123
  u8 tdr;
1124
  u8 unknown1;
1125
  u8 sql;
1126
  u8 unknown2[2];
1127
  u8 tot;
1128
  u8 apo;           // BTech radios use this as the Auto Power Off time
1129
                    // other radios use this as pre-Time Out Alert
1130
  u8 unknown3;
1131
  u8 abr;
1132
  u8 beep;
1133
  u8 unknown4[4];
1134
  u8 dtmfst;
1135
  u8 unknown5[2];
1136
  u8 prisc;
1137
  u8 prich;
1138
  u8 screv;
1139
  u8 unknown6[2];
1140
  u8 pttid;
1141
  u8 pttlt;
1142
  u8 unknown7;
1143
  u8 emctp;
1144
  u8 emcch;
1145
  u8 ringt;
1146
  u8 unknown8;
1147
  u8 camdf;
1148
  u8 cbmdf;
1149
  u8 sync;          // BTech radios use this as the display sync setting
1150
                    // other radios use this as the auto keypad lock setting
1151
  u8 ponmsg;
1152
  u8 wtled;
1153
  u8 rxled;
1154
  u8 txled;
1155
  u8 unknown9[5];
1156
  u8 anil;
1157
  u8 reps;
1158
  u8 repm;
1159
  u8 tdrab;
1160
  u8 ste;
1161
  u8 rpste;
1162
  u8 rptdl;
1163
  u8 mgain;
1164
  u8 dtmfg;
1165
} settings;
1166

    
1167
#seekto 0x0E80;
1168
struct {
1169
  u8 unknown1;
1170
  u8 vfomr;
1171
  u8 keylock;
1172
  u8 unknown2;
1173
  u8 unknown3:4,
1174
     vfomren:1,
1175
     unknown4:1,
1176
     reseten:1,
1177
     menuen:1;
1178
  u8 unknown5[11];
1179
  u8 dispab;
1180
  u8 mrcha;
1181
  u8 mrchb;
1182
  u8 menu;
1183
} settings2;
1184

    
1185
#seekto 0x0EC0;
1186
struct {
1187
  char line1[6];
1188
  char line2[6];
1189
} poweron_msg;
1190

    
1191
struct settings_vfo {
1192
  u8 freq[8];
1193
  u8 offset[6];
1194
  u8 unknown2[2];
1195
  ul16 rxtone;
1196
  ul16 txtone;
1197
  u8 scode;
1198
  u8 spmute;
1199
  u8 optsig;
1200
  u8 scramble;
1201
  u8 wide;
1202
  u8 power;
1203
  u8 shiftd;
1204
  u8 step;
1205
  u8 unknown3[4];
1206
};
1207

    
1208
#seekto 0x0F00;
1209
struct {
1210
  struct settings_vfo a;
1211
  struct settings_vfo b;
1212
} vfo;
1213

    
1214
#seekto 0x1000;
1215
struct {
1216
  char name[6];
1217
  u8 unknown1[10];
1218
} names[200];
1219

    
1220
#seekto 0x2400;
1221
struct {
1222
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
1223
  u8 group_tone;
1224
  u8 repeat_tone;
1225
  u8 unused[13];
1226
} _5tone_std_settings[15];
1227

    
1228
#seekto 0x2500;
1229
struct {
1230
  u8 frame1[5];
1231
  u8 frame2[5];
1232
  u8 frame3[5];
1233
  u8 standard;   // one out of LIST_5TONE_STANDARDS
1234
} _5tone_codes[15];
1235

    
1236
#seekto 0x25F0;
1237
struct {
1238
  u8 _5tone_delay1; // * 10ms
1239
  u8 _5tone_delay2; // * 10ms
1240
  u8 _5tone_delay3; // * 10ms
1241
  u8 _5tone_first_digit_ext_length;
1242
  u8 unknown1;
1243
  u8 unknown2;
1244
  u8 unknown3;
1245
  u8 unknown4;
1246
  u8 decode_standard;
1247
  u8 unknown5:5,
1248
     _5tone_decode_call_frame3:1,
1249
     _5tone_decode_call_frame2:1,
1250
     _5tone_decode_call_frame1:1;
1251
  u8 unknown6:5,
1252
     _5tone_decode_disp_frame3:1,
1253
     _5tone_decode_disp_frame2:1,
1254
     _5tone_decode_disp_frame1:1;
1255
  u8 decode_reset_time; // * 100 + 100ms
1256
} _5tone_settings;
1257

    
1258
#seekto 0x2900;
1259
struct {
1260
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
1261
} dtmf_codes[15];
1262

    
1263
#seekto 0x29F0;
1264
struct {
1265
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
1266
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
1267
  u8 unknown0[14];
1268
  u8 inspection[16];
1269
  u8 monitor[16];
1270
  u8 alarmcode[16];
1271
  u8 stun[16];
1272
  u8 kill[16];
1273
  u8 revive[16];
1274
  u8 unknown1[16];
1275
  u8 unknown2[16];
1276
  u8 unknown3[16];
1277
  u8 unknown4[16];
1278
  u8 unknown5[16];
1279
  u8 unknown6[16];
1280
  u8 unknown7[16];
1281
  u8 masterid[16];
1282
  u8 viceid[16];
1283
  u8 unused01:7,
1284
     mastervice:1;
1285
  u8 unused02:3,
1286
     mrevive:1,
1287
     mkill:1,
1288
     mstun:1,
1289
     mmonitor:1,
1290
     minspection:1;
1291
  u8 unused03:3,
1292
     vrevive:1,
1293
     vkill:1,
1294
     vstun:1,
1295
     vmonitor:1,
1296
     vinspection:1;
1297
  u8 unused04:6,
1298
     txdisable:1,
1299
     rxdisable:1;
1300
  u8 groupcode;
1301
  u8 spacecode;
1302
  u8 delayproctime; // * 100 + 100ms
1303
  u8 resettime;     // * 100 + 100ms
1304
} dtmf_settings;
1305

    
1306
#seekto 0x2D00;
1307
struct {
1308
  struct {
1309
    ul16 freq1;
1310
    u8 unused01[6];
1311
    ul16 freq2;
1312
    u8 unused02[6];
1313
  } _2tone_encode[15];
1314
  u8 duration_1st_tone; // *10ms
1315
  u8 duration_2nd_tone; // *10ms
1316
  u8 duration_gap;      // *10ms
1317
  u8 unused03[13];
1318
  struct {
1319
    struct {
1320
      u8 dec;      // one out of LIST_2TONE_DEC
1321
      u8 response; // one out of LIST_2TONE_RESPONSE
1322
      u8 alert;    // 1-16
1323
    } decs[4];
1324
    u8 unused04[4];
1325
  } _2tone_decode[15];
1326
  u8 unused05[16];
1327

    
1328
  struct {
1329
    ul16 freqA;
1330
    ul16 freqB;
1331
    ul16 freqC;
1332
    ul16 freqD;
1333
    // unknown what those values mean, but they are
1334
    // derived from configured frequencies
1335
    ul16 derived_from_freqA; // 2304000/freqA
1336
    ul16 derived_from_freqB; // 2304000/freqB
1337
    ul16 derived_from_freqC; // 2304000/freqC
1338
    ul16 derived_from_freqD; // 2304000/freqD
1339
  }freqs[15];
1340
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
1341
} _2tone;
1342

    
1343
#seekto 0x3000;
1344
struct {
1345
  u8 freq[8];
1346
  char broadcast_station_name[6];
1347
  u8 unknown[2];
1348
} fm_radio_preset[16];
1349

    
1350
#seekto 0x3C90;
1351
struct {
1352
  u8 vhf_low[3];
1353
  u8 vhf_high[3];
1354
  u8 uhf_low[3];
1355
  u8 uhf_high[3];
1356
} ranges;
1357

    
1358
// the UV-2501+220 & KT8900R has different zones for storing ranges
1359

    
1360
#seekto 0x3CD0;
1361
struct {
1362
  u8 vhf_low[3];
1363
  u8 vhf_high[3];
1364
  u8 unknown1[4];
1365
  u8 unknown2[6];
1366
  u8 vhf2_low[3];
1367
  u8 vhf2_high[3];
1368
  u8 unknown3[4];
1369
  u8 unknown4[6];
1370
  u8 uhf_low[3];
1371
  u8 uhf_high[3];
1372
} ranges220;
1373

    
1374
#seekto 0x3F70;
1375
struct {
1376
  char fp[6];
1377
} fingerprint;
1378

    
1379
"""
1380

    
1381

    
1382
class BTech(BTechMobileCommon):
1383
    """BTECH's UV-5001 and alike radios"""
1384
    BANDS = 2
1385
    COLOR_LCD = False
1386
    NAME_LENGTH = 6
1387

    
1388
    def set_options(self):
1389
        """This is to read the options from the image and set it in the
1390
        environment, for now just the limits of the freqs in the VHF/UHF
1391
        ranges"""
1392

    
1393
        # setting the correct ranges for each radio type
1394
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1395
            # the model 2501+220 has a segment in 220
1396
            # and a different position in the memmap
1397
            # also the QYT KT8900R
1398
            ranges = self._memobj.ranges220
1399
        else:
1400
            ranges = self._memobj.ranges
1401

    
1402
        # the normal dual bands
1403
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
1404
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
1405

    
1406
        # DEBUG
1407
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
1408
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
1409

    
1410
        # 220Mhz radios case
1411
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1412
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
1413
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
1414
            self._220_range = vhf2
1415

    
1416
        # set the class with the real data
1417
        self._vhf_range = vhf
1418
        self._uhf_range = uhf
1419

    
1420
    def process_mmap(self):
1421
        """Process the mem map into the mem object"""
1422

    
1423
        # Get it
1424
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
1425

    
1426
        # load specific parameters from the radio image
1427
        self.set_options()
1428

    
1429

    
1430
# Declaring Aliases (Clones of the real radios)
1431
class JT2705M(chirp_common.Alias):
1432
    VENDOR = "Jetstream"
1433
    MODEL = "JT2705M"
1434

    
1435

    
1436
class JT6188Mini(chirp_common.Alias):
1437
    VENDOR = "Juentai"
1438
    MODEL = "JT-6188 Mini"
1439

    
1440

    
1441
class JT6188Plus(chirp_common.Alias):
1442
    VENDOR = "Juentai"
1443
    MODEL = "JT-6188 Plus"
1444

    
1445

    
1446
class SSGT890(chirp_common.Alias):
1447
    VENDOR = "Sainsonic"
1448
    MODEL = "GT-890"
1449

    
1450

    
1451
class ZastoneMP300(chirp_common.Alias):
1452
    VENDOR = "Zastone"
1453
    MODEL = "MP-300"
1454

    
1455

    
1456
# real radios
1457
@directory.register
1458
class UV2501(BTech):
1459
    """Baofeng Tech UV2501"""
1460
    MODEL = "UV-2501"
1461
    _fileid = [UV2501G3_fp,
1462
               UV2501G2_fp,
1463
               UV2501pp2_fp,
1464
               UV2501pp_fp]
1465

    
1466

    
1467
@directory.register
1468
class UV2501_220(BTech):
1469
    """Baofeng Tech UV2501+220"""
1470
    MODEL = "UV-2501+220"
1471
    BANDS = 3
1472
    _magic = MSTRING_220
1473
    _fileid = [UV2501_220G3_fp,
1474
               UV2501_220G2_fp,
1475
               UV2501_220_fp,
1476
               UV2501_220pp_fp]
1477

    
1478

    
1479
@directory.register
1480
class UV5001(BTech):
1481
    """Baofeng Tech UV5001"""
1482
    MODEL = "UV-5001"
1483
    _fileid = [UV5001G3_fp,
1484
               UV5001G22_fp,
1485
               UV5001G2_fp,
1486
               UV5001alpha_fp,
1487
               UV5001pp_fp]
1488
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
1489
                     chirp_common.PowerLevel("Low", watts=10)]
1490

    
1491

    
1492
@directory.register
1493
class MINI8900(BTech):
1494
    """WACCOM MINI-8900"""
1495
    VENDOR = "WACCOM"
1496
    MODEL = "MINI-8900"
1497
    _magic = MSTRING_MINI8900
1498
    _fileid = [MINI8900_fp, ]
1499
    # Clones
1500
    ALIASES = [JT6188Plus, ]
1501

    
1502

    
1503
@directory.register
1504
class KTUV980(BTech):
1505
    """QYT KT-UV980"""
1506
    VENDOR = "QYT"
1507
    MODEL = "KT-UV980"
1508
    _vhf_range = (136000000, 175000000)
1509
    _uhf_range = (400000000, 481000000)
1510
    _magic = MSTRING_MINI8900
1511
    _fileid = [KTUV980_fp, ]
1512
    # Clones
1513
    ALIASES = [JT2705M, ]
1514

    
1515
# Please note that there is a version of this radios that is a clone of the
1516
# Waccom Mini8900, maybe an early version?
1517

    
1518

    
1519
class OTGRadioV1(chirp_common.Alias):
1520
    VENDOR = 'OTGSTUFF'
1521
    MODEL = 'OTG Radio v1'
1522

    
1523

    
1524
@directory.register
1525
class KT9800(BTech):
1526
    """QYT KT8900"""
1527
    VENDOR = "QYT"
1528
    MODEL = "KT8900"
1529
    _vhf_range = (136000000, 175000000)
1530
    _uhf_range = (400000000, 481000000)
1531
    _magic = MSTRING_KT8900
1532
    _fileid = [KT8900_fp,
1533
               KT8900_fp1,
1534
               KT8900_fp2,
1535
               KT8900_fp3,
1536
               KT8900_fp4,
1537
               KT8900_fp5,
1538
               KT8900_fp6,
1539
               KT8900_fp7]
1540
    # Clones
1541
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
1542

    
1543

    
1544
@directory.register
1545
class KT9800R(BTech):
1546
    """QYT KT8900R"""
1547
    VENDOR = "QYT"
1548
    MODEL = "KT8900R"
1549
    BANDS = 3
1550
    _vhf_range = (136000000, 175000000)
1551
    _220_range = (240000000, 271000000)
1552
    _uhf_range = (400000000, 481000000)
1553
    _magic = MSTRING_KT8900R
1554
    _fileid = [KT8900R_fp,
1555
               KT8900R_fp1,
1556
               KT8900R_fp2,
1557
               KT8900R_fp3,
1558
               KT8900R_fp4,
1559
               KT8900R_fp5]
1560

    
1561

    
1562
@directory.register
1563
class LT588UV(BTech):
1564
    """LUITON LT-588UV"""
1565
    VENDOR = "LUITON"
1566
    MODEL = "LT-588UV"
1567
    _vhf_range = (136000000, 175000000)
1568
    _uhf_range = (400000000, 481000000)
1569
    _magic = MSTRING_KT8900
1570
    _fileid = [LT588UV_fp,
1571
               LT588UV_fp1]
1572
    _power_levels = [chirp_common.PowerLevel("High", watts=60),
1573
                     chirp_common.PowerLevel("Low", watts=10)]
1574

    
1575

    
1576
COLOR_MEM_FORMAT = """
1577
#seekto 0x0000;
1578
struct {
1579
  lbcd rxfreq[4];
1580
  lbcd txfreq[4];
1581
  ul16 rxtone;
1582
  ul16 txtone;
1583
  u8 unknown0:4,
1584
     scode:4;
1585
  u8 unknown1:2,
1586
     spmute:2,
1587
     unknown2:2,
1588
     optsig:2;
1589
  u8 unknown3:3,
1590
     scramble:1,
1591
     unknown4:2,
1592
     power:2;
1593
  u8 unknown5:1,
1594
     wide:1,
1595
     unknown6:2,
1596
     bcl:1,
1597
     add:1,
1598
     pttid:2;
1599
} memory[200];
1600

    
1601
#seekto 0x0E00;
1602
struct {
1603
  u8 tmr;
1604
  u8 unknown1;
1605
  u8 sql;
1606
  u8 unknown2;
1607
  u8 mgain2;
1608
  u8 tot;
1609
  u8 apo;
1610
  u8 unknown3;
1611
  u8 abr;
1612
  u8 beep;
1613
  u8 unknown4[4];
1614
  u8 dtmfst;
1615
  u8 unknown5[2];
1616
  u8 screv;
1617
  u8 unknown6[2];
1618
  u8 pttid;
1619
  u8 pttlt;
1620
  u8 unknown7;
1621
  u8 emctp;
1622
  u8 emcch;
1623
  u8 sigbp;
1624
  u8 unknown8;
1625
  u8 camdf;
1626
  u8 cbmdf;
1627
  u8 ccmdf;
1628
  u8 cdmdf;
1629
  u8 langua;
1630
  u8 sync;          // BTech radios use this as the display sync
1631
                    // setting, other radios use this as the auto
1632
                    // keypad lock setting
1633
  u8 mainfc;
1634
  u8 mainbc;
1635
  u8 menufc;
1636
  u8 menubc;
1637
  u8 stafc;
1638
  u8 stabc;
1639
  u8 sigfc;
1640
  u8 sigbc;
1641
  u8 rxfc;
1642
  u8 txfc;
1643
  u8 txdisp;
1644
  u8 unknown9[5];
1645
  u8 anil;
1646
  u8 reps;
1647
  u8 repm;
1648
  u8 tmrmr;
1649
  u8 ste;
1650
  u8 rpste;
1651
  u8 rptdl;
1652
  u8 dtmfg;
1653
  u8 mgain;         // used by db25-g for ponyey
1654
  u8 skiptx;
1655
  u8 scmode;
1656
} settings;
1657

    
1658
#seekto 0x0E80;
1659
struct {
1660
  u8 unknown1;
1661
  u8 vfomr;
1662
  u8 keylock;
1663
  u8 unknown2;
1664
  u8 unknown3:4,
1665
     vfomren:1,
1666
     unknown4:1,
1667
     reseten:1,
1668
     menuen:1;
1669
  u8 unknown5[11];
1670
  u8 dispab;
1671
  u8 unknown6[2];
1672
  u8 menu;
1673
  u8 unknown7[7];
1674
  u8 vfomra;
1675
  u8 vfomrb;
1676
  u8 vfomrc;
1677
  u8 vfomrd;
1678
  u8 mrcha;
1679
  u8 mrchb;
1680
  u8 mrchc;
1681
  u8 mrchd;
1682
} settings2;
1683

    
1684
struct settings_vfo {
1685
  u8 freq[8];
1686
  u8 offset[6];
1687
  u8 unknown2[2];
1688
  ul16 rxtone;
1689
  ul16 txtone;
1690
  u8 scode;
1691
  u8 spmute;
1692
  u8 optsig;
1693
  u8 scramble;
1694
  u8 wide;
1695
  u8 power;
1696
  u8 shiftd;
1697
  u8 step;
1698
  u8 unknown3[4];
1699
};
1700

    
1701
#seekto 0x0F00;
1702
struct {
1703
  struct settings_vfo a;
1704
  struct settings_vfo b;
1705
  struct settings_vfo c;
1706
  struct settings_vfo d;
1707
} vfo;
1708

    
1709
#seekto 0x0F80;
1710
struct {
1711
  char line1[8];
1712
  char line2[8];
1713
  char line3[8];
1714
  char line4[8];
1715
  char line5[8];
1716
  char line6[8];
1717
  char line7[8];
1718
  char line8[8];
1719
} poweron_msg;
1720

    
1721
#seekto 0x1000;
1722
struct {
1723
  char name[8];
1724
  u8 unknown1[8];
1725
} names[200];
1726

    
1727
#seekto 0x2400;
1728
struct {
1729
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
1730
  u8 group_tone;
1731
  u8 repeat_tone;
1732
  u8 unused[13];
1733
} _5tone_std_settings[15];
1734

    
1735
#seekto 0x2500;
1736
struct {
1737
  u8 frame1[5];
1738
  u8 frame2[5];
1739
  u8 frame3[5];
1740
  u8 standard;   // one out of LIST_5TONE_STANDARDS
1741
} _5tone_codes[15];
1742

    
1743
#seekto 0x25F0;
1744
struct {
1745
  u8 _5tone_delay1; // * 10ms
1746
  u8 _5tone_delay2; // * 10ms
1747
  u8 _5tone_delay3; // * 10ms
1748
  u8 _5tone_first_digit_ext_length;
1749
  u8 unknown1;
1750
  u8 unknown2;
1751
  u8 unknown3;
1752
  u8 unknown4;
1753
  u8 decode_standard;
1754
  u8 unknown5:5,
1755
     _5tone_decode_call_frame3:1,
1756
     _5tone_decode_call_frame2:1,
1757
     _5tone_decode_call_frame1:1;
1758
  u8 unknown6:5,
1759
     _5tone_decode_disp_frame3:1,
1760
     _5tone_decode_disp_frame2:1,
1761
     _5tone_decode_disp_frame1:1;
1762
  u8 decode_reset_time; // * 100 + 100ms
1763
} _5tone_settings;
1764

    
1765
#seekto 0x2900;
1766
struct {
1767
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
1768
} dtmf_codes[15];
1769

    
1770
#seekto 0x29F0;
1771
struct {
1772
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
1773
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
1774
  u8 unknown0[14];
1775
  u8 inspection[16];
1776
  u8 monitor[16];
1777
  u8 alarmcode[16];
1778
  u8 stun[16];
1779
  u8 kill[16];
1780
  u8 revive[16];
1781
  u8 unknown1[16];
1782
  u8 unknown2[16];
1783
  u8 unknown3[16];
1784
  u8 unknown4[16];
1785
  u8 unknown5[16];
1786
  u8 unknown6[16];
1787
  u8 unknown7[16];
1788
  u8 masterid[16];
1789
  u8 viceid[16];
1790
  u8 unused01:7,
1791
     mastervice:1;
1792
  u8 unused02:3,
1793
     mrevive:1,
1794
     mkill:1,
1795
     mstun:1,
1796
     mmonitor:1,
1797
     minspection:1;
1798
  u8 unused03:3,
1799
     vrevive:1,
1800
     vkill:1,
1801
     vstun:1,
1802
     vmonitor:1,
1803
     vinspection:1;
1804
  u8 unused04:6,
1805
     txdisable:1,
1806
     rxdisable:1;
1807
  u8 groupcode;
1808
  u8 spacecode;
1809
  u8 delayproctime; // * 100 + 100ms
1810
  u8 resettime;     // * 100 + 100ms
1811
} dtmf_settings;
1812

    
1813
#seekto 0x2D00;
1814
struct {
1815
  struct {
1816
    ul16 freq1;
1817
    u8 unused01[6];
1818
    ul16 freq2;
1819
    u8 unused02[6];
1820
  } _2tone_encode[15];
1821
  u8 duration_1st_tone; // *10ms
1822
  u8 duration_2nd_tone; // *10ms
1823
  u8 duration_gap;      // *10ms
1824
  u8 unused03[13];
1825
  struct {
1826
    struct {
1827
      u8 dec;      // one out of LIST_2TONE_DEC
1828
      u8 response; // one out of LIST_2TONE_RESPONSE
1829
      u8 alert;    // 1-16
1830
    } decs[4];
1831
    u8 unused04[4];
1832
  } _2tone_decode[15];
1833
  u8 unused05[16];
1834

    
1835
  struct {
1836
    ul16 freqA;
1837
    ul16 freqB;
1838
    ul16 freqC;
1839
    ul16 freqD;
1840
    // unknown what those values mean, but they are
1841
    // derived from configured frequencies
1842
    ul16 derived_from_freqA; // 2304000/freqA
1843
    ul16 derived_from_freqB; // 2304000/freqB
1844
    ul16 derived_from_freqC; // 2304000/freqC
1845
    ul16 derived_from_freqD; // 2304000/freqD
1846
  }freqs[15];
1847
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
1848
} _2tone;
1849

    
1850
#seekto 0x3D80;
1851
struct {
1852
  u8 vhf_low[3];
1853
  u8 vhf_high[3];
1854
  u8 unknown1[4];
1855
  u8 unknown2[6];
1856
  u8 vhf2_low[3];
1857
  u8 vhf2_high[3];
1858
  u8 unknown3[4];
1859
  u8 unknown4[6];
1860
  u8 uhf_low[3];
1861
  u8 uhf_high[3];
1862
  u8 unknown5[4];
1863
  u8 unknown6[6];
1864
  u8 uhf2_low[3];
1865
  u8 uhf2_high[3];
1866
} ranges;
1867

    
1868
#seekto 0x3F70;
1869
struct {
1870
  char fp[6];
1871
} fingerprint;
1872

    
1873
"""
1874

    
1875

    
1876
class BTechColor(BTechMobileCommon):
1877
    """BTECH's Color LCD Mobile and alike radios"""
1878
    COLOR_LCD = True
1879
    NAME_LENGTH = 8
1880
    LIST_TMR = LIST_TMR16
1881

    
1882
    def process_mmap(self):
1883
        """Process the mem map into the mem object"""
1884

    
1885
        # Get it
1886
        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
1887

    
1888
        # load specific parameters from the radio image
1889
        self.set_options()
1890

    
1891
    def set_options(self):
1892
        """This is to read the options from the image and set it in the
1893
        environment, for now just the limits of the freqs in the VHF/UHF
1894
        ranges"""
1895

    
1896
        # setting the correct ranges for each radio type
1897
        ranges = self._memobj.ranges
1898

    
1899
        # the normal dual bands
1900
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
1901
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
1902

    
1903
        # DEBUG
1904
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
1905
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
1906

    
1907
        # the additional bands
1908
        if self.MODEL in ["UV-25X4", "KT7900D"]:
1909
            # 200Mhz band
1910
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
1911
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
1912
            self._220_range = vhf2
1913

    
1914
            # 350Mhz band
1915
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
1916
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
1917
            self._350_range = uhf2
1918

    
1919
        # set the class with the real data
1920
        self._vhf_range = vhf
1921
        self._uhf_range = uhf
1922

    
1923

    
1924
# Declaring Aliases (Clones of the real radios)
1925
class SKT8900D(chirp_common.Alias):
1926
    VENDOR = "Surecom"
1927
    MODEL = "S-KT8900D"
1928

    
1929

    
1930
class QB25(chirp_common.Alias):
1931
    VENDOR = "Radioddity"
1932
    MODEL = "QB25"
1933

    
1934

    
1935
# real radios
1936
@directory.register
1937
class UV25X2(BTechColor):
1938
    """Baofeng Tech UV25X2"""
1939
    MODEL = "UV-25X2"
1940
    BANDS = 2
1941
    _vhf_range = (130000000, 180000000)
1942
    _uhf_range = (400000000, 521000000)
1943
    _magic = MSTRING_UV25X2
1944
    _fileid = [UV25X2_fp, ]
1945

    
1946

    
1947
@directory.register
1948
class UV25X4(BTechColor):
1949
    """Baofeng Tech UV25X4"""
1950
    MODEL = "UV-25X4"
1951
    BANDS = 4
1952
    _vhf_range = (130000000, 180000000)
1953
    _220_range = (200000000, 271000000)
1954
    _uhf_range = (400000000, 521000000)
1955
    _350_range = (350000000, 391000000)
1956
    _magic = MSTRING_UV25X4
1957
    _fileid = [UV25X4_fp, ]
1958

    
1959

    
1960
@directory.register
1961
class UV50X2(BTechColor):
1962
    """Baofeng Tech UV50X2"""
1963
    MODEL = "UV-50X2"
1964
    BANDS = 2
1965
    _vhf_range = (130000000, 180000000)
1966
    _uhf_range = (400000000, 521000000)
1967
    _magic = MSTRING_UV25X2
1968
    _fileid = [UV50X2_fp, ]
1969
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
1970
                     chirp_common.PowerLevel("Low", watts=10)]
1971

    
1972

    
1973
@directory.register
1974
class KT7900D(BTechColor):
1975
    """QYT KT7900D"""
1976
    VENDOR = "QYT"
1977
    MODEL = "KT7900D"
1978
    BANDS = 4
1979
    LIST_TMR = LIST_TMR15
1980
    _vhf_range = (136000000, 175000000)
1981
    _220_range = (200000000, 271000000)
1982
    _uhf_range = (400000000, 481000000)
1983
    _350_range = (350000000, 371000000)
1984
    _magic = MSTRING_KT8900D
1985
    _fileid = [KT7900D_fp, KT7900D_fp1, KT7900D_fp2, KT7900D_fp3, KT7900D_fp4,
1986
               KT7900D_fp5, KT7900D_fp6, KT7900D_fp7, QB25_fp, ]
1987
    # Clones
1988
    ALIASES = [SKT8900D, QB25, ]
1989

    
1990

    
1991
@directory.register
1992
class KT8900D(BTechColor):
1993
    """QYT KT8900D"""
1994
    VENDOR = "QYT"
1995
    MODEL = "KT8900D"
1996
    BANDS = 2
1997
    LIST_TMR = LIST_TMR15
1998
    _vhf_range = (136000000, 175000000)
1999
    _uhf_range = (400000000, 481000000)
2000
    _magic = MSTRING_KT8900D
2001
    _fileid = [KT8900D_fp3, KT8900D_fp2, KT8900D_fp1, KT8900D_fp]
2002

    
2003
    # Clones
2004
    ALIASES = [OTGRadioV1]
2005

    
2006

    
2007
@directory.register
2008
class KT5800(BTechColor):
2009
    """QYT KT5800"""
2010
    VENDOR = "QYT"
2011
    MODEL = "KT5800"
2012
    BANDS = 2
2013
    LIST_TMR = LIST_TMR15
2014
    _vhf_range = (136000000, 175000000)
2015
    _uhf_range = (400000000, 481000000)
2016
    _magic = MSTRING_KT8900D
2017
    _fileid = [KT5800_fp, ]
2018

    
2019

    
2020
@directory.register
2021
class KT980PLUS(BTechColor):
2022
    """QYT KT980PLUS"""
2023
    VENDOR = "QYT"
2024
    MODEL = "KT980PLUS"
2025
    BANDS = 2
2026
    LIST_TMR = LIST_TMR15
2027
    _vhf_range = (136000000, 175000000)
2028
    _uhf_range = (400000000, 481000000)
2029
    _magic = MSTRING_KT8900D
2030
    _fileid = [KT980PLUS_fp1, KT980PLUS_fp]
2031
    _power_levels = [chirp_common.PowerLevel("High", watts=75),
2032
                     chirp_common.PowerLevel("Low", watts=55)]
2033

    
2034
    @classmethod
2035
    def match_model(cls, filedata, filename):
2036
        # This model is only ever matched via metadata
2037
        return False
2038

    
2039

    
2040
@directory.register
2041
class DB25G(BTechColor):
2042
    """Radioddity DB25-G"""
2043
    VENDOR = "Radioddity"
2044
    MODEL = "DB25-G"
2045
    BANDS = 2
2046
    LIST_TMR = LIST_TMR15
2047
    _vhf_range = (136000000, 175000000)
2048
    _uhf_range = (400000000, 481000000)
2049
    _magic = MSTRING_KT8900D
2050
    _fileid = [DB25G_fp1, DB25G_fp]
2051
    _gmrs = True
2052
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
2053
                     chirp_common.PowerLevel("Mid", watts=15),
2054
                     chirp_common.PowerLevel("Low", watts=5)]
2055

    
2056
    @classmethod
2057
    def match_model(cls, filedata, filename):
2058
        # This model is only ever matched via metadata
2059
        return False
2060

    
2061

    
2062
GMRS_MEM_FORMAT = """
2063
#seekto 0x0000;
2064
struct {
2065
  lbcd rxfreq[4];
2066
  lbcd txfreq[4];
2067
  ul16 rxtone;
2068
  ul16 txtone;
2069
  u8 unknown0:4,
2070
     scode:4;
2071
  u8 unknown1:2,
2072
     spmute:2,
2073
     unknown2:2,
2074
     optsig:2;
2075
  u8 unknown3:3,
2076
     scramble:1,
2077
     unknown4:2,
2078
     power:2;
2079
  u8 unknown5:1,
2080
     wide:1,
2081
     unknown6:2,
2082
     bcl:1,
2083
     add:1,
2084
     pttid:2;
2085
} memory[256];
2086

    
2087
#seekto 0x1000;
2088
struct {
2089
  char name[7];
2090
  u8 unknown1[9];
2091
} names[256];
2092

    
2093
#seekto 0x2400;
2094
struct {
2095
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2096
  u8 group_tone;
2097
  u8 repeat_tone;
2098
  u8 unused[13];
2099
} _5tone_std_settings[15];
2100

    
2101
#seekto 0x2500;
2102
struct {
2103
  u8 frame1[5];
2104
  u8 frame2[5];
2105
  u8 frame3[5];
2106
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2107
} _5tone_codes[15];
2108

    
2109
#seekto 0x25F0;
2110
struct {
2111
  u8 _5tone_delay1; // * 10ms
2112
  u8 _5tone_delay2; // * 10ms
2113
  u8 _5tone_delay3; // * 10ms
2114
  u8 _5tone_first_digit_ext_length;
2115
  u8 unknown1;
2116
  u8 unknown2;
2117
  u8 unknown3;
2118
  u8 unknown4;
2119
  u8 decode_standard;
2120
  u8 unknown5:5,
2121
     _5tone_decode_call_frame3:1,
2122
     _5tone_decode_call_frame2:1,
2123
     _5tone_decode_call_frame1:1;
2124
  u8 unknown6:5,
2125
     _5tone_decode_disp_frame3:1,
2126
     _5tone_decode_disp_frame2:1,
2127
     _5tone_decode_disp_frame1:1;
2128
  u8 decode_reset_time; // * 100 + 100ms
2129
} _5tone_settings;
2130

    
2131
#seekto 0x2900;
2132
struct {
2133
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2134
} dtmf_codes[15];
2135

    
2136
#seekto 0x29F0;
2137
struct {
2138
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
2139
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
2140
  u8 unknown0[14];
2141
  u8 inspection[16];
2142
  u8 monitor[16];
2143
  u8 alarmcode[16];
2144
  u8 stun[16];
2145
  u8 kill[16];
2146
  u8 revive[16];
2147
  u8 unknown1[16];
2148
  u8 unknown2[16];
2149
  u8 unknown3[16];
2150
  u8 unknown4[16];
2151
  u8 unknown5[16];
2152
  u8 unknown6[16];
2153
  u8 unknown7[16];
2154
  u8 masterid[16];
2155
  u8 viceid[16];
2156
  u8 unused01:7,
2157
     mastervice:1;
2158
  u8 unused02:3,
2159
     mrevive:1,
2160
     mkill:1,
2161
     mstun:1,
2162
     mmonitor:1,
2163
     minspection:1;
2164
  u8 unused03:3,
2165
     vrevive:1,
2166
     vkill:1,
2167
     vstun:1,
2168
     vmonitor:1,
2169
     vinspection:1;
2170
  u8 unused04:6,
2171
     txdisable:1,
2172
     rxdisable:1;
2173
  u8 groupcode;
2174
  u8 spacecode;
2175
  u8 delayproctime; // * 100 + 100ms
2176
  u8 resettime;     // * 100 + 100ms
2177
} dtmf_settings;
2178

    
2179
#seekto 0x2D00;
2180
struct {
2181
  struct {
2182
    ul16 freq1;
2183
    u8 unused01[6];
2184
    ul16 freq2;
2185
    u8 unused02[6];
2186
  } _2tone_encode[15];
2187
  u8 duration_1st_tone; // *10ms
2188
  u8 duration_2nd_tone; // *10ms
2189
  u8 duration_gap;      // *10ms
2190
  u8 unused03[13];
2191
  struct {
2192
    struct {
2193
      u8 dec;      // one out of LIST_2TONE_DEC
2194
      u8 response; // one out of LIST_2TONE_RESPONSE
2195
      u8 alert;    // 1-16
2196
    } decs[4];
2197
    u8 unused04[4];
2198
  } _2tone_decode[15];
2199
  u8 unused05[16];
2200

    
2201
  struct {
2202
    ul16 freqA;
2203
    ul16 freqB;
2204
    ul16 freqC;
2205
    ul16 freqD;
2206
    // unknown what those values mean, but they are
2207
    // derived from configured frequencies
2208
    ul16 derived_from_freqA; // 2304000/freqA
2209
    ul16 derived_from_freqB; // 2304000/freqB
2210
    ul16 derived_from_freqC; // 2304000/freqC
2211
    ul16 derived_from_freqD; // 2304000/freqD
2212
  }freqs[15];
2213
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
2214
} _2tone;
2215

    
2216
#seekto 0x3000;
2217
struct {
2218
  u8 freq[8];
2219
  char broadcast_station_name[6];
2220
  u8 unknown[2];
2221
} fm_radio_preset[16];
2222

    
2223
#seekto 0x3200;
2224
struct {
2225
  u8 tmr;
2226
  u8 unknown1;
2227
  u8 sql;
2228
  u8 unknown2;
2229
  u8 autolk;
2230
  u8 tot;
2231
  u8 apo;
2232
  u8 unknown3;
2233
  u8 abr;
2234
  u8 beep;
2235
  u8 unknown4[4];
2236
  u8 dtmfst;
2237
  u8 unknown5[2];
2238
  u8 screv;
2239
  u8 unknown6[2];
2240
  u8 pttid;
2241
  u8 pttlt;
2242
  u8 unknown7;
2243
  u8 emctp;
2244
  u8 emcch;
2245
  u8 sigbp;
2246
  u8 unknown8;
2247
  u8 camdf;
2248
  u8 cbmdf;
2249
  u8 ccmdf;
2250
  u8 cdmdf;
2251
  u8 langua;
2252
  u8 sync;
2253

    
2254

    
2255
  u8 stfc;
2256
  u8 mffc;
2257
  u8 sfafc;
2258
  u8 sfbfc;
2259
  u8 sfcfc;
2260
  u8 sfdfc;
2261
  u8 subfc;
2262
  u8 fmfc;
2263
  u8 sigfc;
2264
  u8 modfc;
2265
  u8 menufc;
2266
  u8 txfc;
2267
  u8 txdisp;
2268
  u8 unknown9[5];
2269
  u8 anil;
2270
  u8 reps;
2271
  u8 repm;
2272
  u8 tmrmr;
2273
  u8 ste;
2274
  u8 rpste;
2275
  u8 rptdl;
2276
  u8 dtmfg;
2277
  u8 mgain;
2278
  u8 skiptx;
2279
  u8 scmode;
2280
} settings;
2281

    
2282
#seekto 0x3280;
2283
struct {
2284
  u8 unknown1;
2285
  u8 vfomr;
2286
  u8 keylock;
2287
  u8 unknown2;
2288
  u8 unknown3:4,
2289
     vfomren:1,
2290
     unknown4:1,
2291
     reseten:1,
2292
     menuen:1;
2293
  u8 unknown5[11];
2294
  u8 dispab;
2295
  u8 unknown6[2];
2296
  u8 smenu;
2297
  u8 unknown7[7];
2298
  u8 vfomra;
2299
  u8 vfomrb;
2300
  u8 vfomrc;
2301
  u8 vfomrd;
2302
  u8 mrcha;
2303
  u8 mrchb;
2304
  u8 mrchc;
2305
  u8 mrchd;
2306
} settings2;
2307

    
2308
struct settings_vfo {
2309
  u8 freq[8];
2310
  u8 offset[6];
2311
  u8 unknown2[2];
2312
  ul16 rxtone;
2313
  ul16 txtone;
2314
  u8 scode;
2315
  u8 spmute;
2316
  u8 optsig;
2317
  u8 scramble;
2318
  u8 wide;
2319
  u8 power;
2320
  u8 shiftd;
2321
  u8 step;
2322
  u8 unknown3[4];
2323
};
2324

    
2325
#seekto 0x3300;
2326
struct {
2327
  struct settings_vfo a;
2328
  struct settings_vfo b;
2329
  struct settings_vfo c;
2330
  struct settings_vfo d;
2331
} vfo;
2332

    
2333
#seekto 0x3D80;
2334
struct {
2335
  u8 vhf_low[3];
2336
  u8 vhf_high[3];
2337
  u8 unknown1[4];
2338
  u8 unknown2[6];
2339
  u8 vhf2_low[3];
2340
  u8 vhf2_high[3];
2341
  u8 unknown3[4];
2342
  u8 unknown4[6];
2343
  u8 uhf_low[3];
2344
  u8 uhf_high[3];
2345
  u8 unknown5[4];
2346
  u8 unknown6[6];
2347
  u8 uhf2_low[3];
2348
  u8 uhf2_high[3];
2349
} ranges;
2350

    
2351
#seekto 0x33B0;
2352
struct {
2353
  char line[16];
2354
} static_msg;
2355

    
2356
#seekto 0x3F70;
2357
struct {
2358
  char fp[6];
2359
} fingerprint;
2360

    
2361
"""
2362

    
2363

    
2364
class BTechGMRS(BTechMobileCommon):
2365
    """BTECH's GMRS Mobile"""
2366
    COLOR_LCD = True
2367
    COLOR_LCD2 = True
2368
    NAME_LENGTH = 7
2369
    UPLOAD_MEM_SIZE = 0X3400
2370

    
2371
    def process_mmap(self):
2372
        """Process the mem map into the mem object"""
2373

    
2374
        # Get it
2375
        self._memobj = bitwise.parse(GMRS_MEM_FORMAT, self._mmap)
2376

    
2377
        # load specific parameters from the radio image
2378
        self.set_options()
2379

    
2380
    def set_options(self):
2381
        """This is to read the options from the image and set it in the
2382
        environment, for now just the limits of the freqs in the VHF/UHF
2383
        ranges"""
2384

    
2385
        # setting the correct ranges for each radio type
2386
        ranges = self._memobj.ranges
2387

    
2388
        # the normal dual bands
2389
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
2390
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
2391

    
2392
        # DEBUG
2393
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
2394
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
2395

    
2396
        # set the class with the real data
2397
        self._vhf_range = vhf
2398
        self._uhf_range = uhf
2399

    
2400

    
2401
# real radios
2402
@directory.register
2403
class GMRS50X1(BTechGMRS):
2404
    """Baofeng Tech GMRS50X1"""
2405
    MODEL = "GMRS-50X1"
2406
    BANDS = 2
2407
    LIST_TMR = LIST_TMR16
2408
    _power_levels = [chirp_common.PowerLevel("High", watts=50),
2409
                     chirp_common.PowerLevel("Mid", watts=10),
2410
                     chirp_common.PowerLevel("Low", watts=5)]
2411
    _vhf_range = (136000000, 175000000)
2412
    _uhf_range = (400000000, 521000000)
2413
    _upper = 255
2414
    _magic = MSTRING_GMRS50X1
2415
    _fileid = [GMRS50X1_fp1, GMRS50X1_fp, ]
2416

    
2417

    
2418
COLORHT_MEM_FORMAT = """
2419
#seekto 0x0000;
2420
struct {
2421
  lbcd rxfreq[4];
2422
  lbcd txfreq[4];
2423
  ul16 rxtone;
2424
  ul16 txtone;
2425
  u8 unknown0:4,
2426
     scode:4;
2427
  u8 unknown1:2,
2428
     spmute:2,
2429
     unknown2:2,
2430
     optsig:2;
2431
  u8 unknown3:3,
2432
     scramble:1,
2433
     unknown4:3,
2434
     power:1;
2435
  u8 unknown5:1,
2436
     wide:1,
2437
     unknown6:2,
2438
     bcl:1,
2439
     add:1,
2440
     pttid:2;
2441
} memory[200];
2442

    
2443
#seekto 0x0E00;
2444
struct {
2445
  u8 tmr;
2446
  u8 unknownE01;
2447
  u8 sql;
2448
  u8 unknownE03[2];
2449
  u8 tot;
2450
  u8 save;
2451
  u8 unknownE07;
2452
  u8 abr;
2453
  u8 beep;
2454
  u8 unknownE0A[4];
2455
  u8 dsub;
2456
  u8 dtmfst;
2457
  u8 screv;
2458
  u8 unknownE11[3];
2459
  u8 pttid;
2460
  u8 unknownE15;
2461
  u8 pttlt;
2462
  u8 unknownE17;
2463
  u8 emctp;
2464
  u8 emcch;
2465
  u8 sigbp;
2466
  u8 unknownE1B;
2467
  u8 camdf;
2468
  u8 cbmdf;
2469
  u8 ccmdf;
2470
  u8 cdmdf;
2471
  u8 langua;
2472
  u8 voice;
2473
  u8 vox;
2474
  u8 voxt;
2475
  u8 sync;          // BTech radios use this as the display sync setting
2476
                    // other radios use this as the auto keypad lock setting
2477
  u8 stfc;
2478
  u8 mffc;
2479
  u8 sfafc;
2480
  u8 sfbfc;
2481
  u8 sfcfc;
2482
  u8 sfdfc;
2483
  u8 subfc;
2484
  u8 fmfc;
2485
  u8 sigfc;
2486
  u8 menufc;
2487
  u8 txfc;
2488
  u8 rxfc;
2489
  u8 unknownE31[5];
2490
  u8 anil;
2491
  u8 reps;
2492
  u8 tmrmr;
2493
  u8 ste;
2494
  u8 rpste;
2495
  u8 rptdl;
2496
  u8 dtmfg;
2497
  u8 tmrtx;
2498
} settings;
2499

    
2500
#seekto 0x0E80;
2501
struct {
2502
  u8 unknown1;
2503
  u8 vfomr;
2504
  u8 keylock;
2505
  u8 unknown2;
2506
  u8 unknown3:4,
2507
     vfomren:1,
2508
     unknown4:1,
2509
     reseten:1,
2510
     menuen:1;
2511
  u8 unknown5[11];
2512
  u8 dispab;
2513
  u8 unknown6[2];
2514
  u8 menu;
2515
  u8 unknown7[7];
2516
  u8 vfomra;
2517
  u8 vfomrb;
2518
  u8 vfomrc;
2519
  u8 vfomrd;
2520
  u8 mrcha;
2521
  u8 mrchb;
2522
  u8 mrchc;
2523
  u8 mrchd;
2524
} settings2;
2525

    
2526
struct settings_vfo {
2527
  u8 freq[8];
2528
  u8 offset[6];
2529
  u8 unknown2[2];
2530
  ul16 rxtone;
2531
  ul16 txtone;
2532
  u8 scode;
2533
  u8 spmute;
2534
  u8 optsig;
2535
  u8 scramble;
2536
  u8 wide;
2537
  u8 power;
2538
  u8 shiftd;
2539
  u8 step;
2540
  u8 unknown3[4];
2541
};
2542

    
2543
#seekto 0x0F00;
2544
struct {
2545
  struct settings_vfo a;
2546
  struct settings_vfo b;
2547
  struct settings_vfo c;
2548
  struct settings_vfo d;
2549
} vfo;
2550

    
2551
#seekto 0x0FE0;
2552
struct {
2553
  char line[16];
2554
} static_msg;
2555

    
2556
#seekto 0x1000;
2557
struct {
2558
  char name[8];
2559
  u8 unknown1[8];
2560
} names[200];
2561

    
2562
#seekto 0x2400;
2563
struct {
2564
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2565
  u8 group_tone;
2566
  u8 repeat_tone;
2567
  u8 unused[13];
2568
} _5tone_std_settings[15];
2569

    
2570
#seekto 0x2500;
2571
struct {
2572
  u8 frame1[5];
2573
  u8 frame2[5];
2574
  u8 frame3[5];
2575
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2576
} _5tone_codes[15];
2577

    
2578
#seekto 0x25F0;
2579
struct {
2580
  u8 _5tone_delay1; // * 10ms
2581
  u8 _5tone_delay2; // * 10ms
2582
  u8 _5tone_delay3; // * 10ms
2583
  u8 _5tone_first_digit_ext_length;
2584
  u8 unknown1;
2585
  u8 unknown2;
2586
  u8 unknown3;
2587
  u8 unknown4;
2588
  u8 decode_standard;
2589
  u8 unknown5:5,
2590
     _5tone_decode_call_frame3:1,
2591
     _5tone_decode_call_frame2:1,
2592
     _5tone_decode_call_frame1:1;
2593
  u8 unknown6:5,
2594
     _5tone_decode_disp_frame3:1,
2595
     _5tone_decode_disp_frame2:1,
2596
     _5tone_decode_disp_frame1:1;
2597
  u8 decode_reset_time; // * 100 + 100ms
2598
} _5tone_settings;
2599

    
2600
#seekto 0x2900;
2601
struct {
2602
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2603
} dtmf_codes[15];
2604

    
2605
#seekto 0x29F0;
2606
struct {
2607
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
2608
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
2609
  u8 unknown0[14];
2610
  u8 inspection[16];
2611
  u8 monitor[16];
2612
  u8 alarmcode[16];
2613
  u8 stun[16];
2614
  u8 kill[16];
2615
  u8 revive[16];
2616
  u8 unknown1[16];
2617
  u8 unknown2[16];
2618
  u8 unknown3[16];
2619
  u8 unknown4[16];
2620
  u8 unknown5[16];
2621
  u8 unknown6[16];
2622
  u8 unknown7[16];
2623
  u8 masterid[16];
2624
  u8 viceid[16];
2625
  u8 unused01:7,
2626
     mastervice:1;
2627
  u8 unused02:3,
2628
     mrevive:1,
2629
     mkill:1,
2630
     mstun:1,
2631
     mmonitor:1,
2632
     minspection:1;
2633
  u8 unused03:3,
2634
     vrevive:1,
2635
     vkill:1,
2636
     vstun:1,
2637
     vmonitor:1,
2638
     vinspection:1;
2639
  u8 unused04:6,
2640
     txdisable:1,
2641
     rxdisable:1;
2642
  u8 groupcode;
2643
  u8 spacecode;
2644
  u8 delayproctime; // * 100 + 100ms
2645
  u8 resettime;     // * 100 + 100ms
2646
} dtmf_settings;
2647

    
2648
#seekto 0x2D00;
2649
struct {
2650
  struct {
2651
    ul16 freq1;
2652
    u8 unused01[6];
2653
    ul16 freq2;
2654
    u8 unused02[6];
2655
  } _2tone_encode[15];
2656
  u8 duration_1st_tone; // *10ms
2657
  u8 duration_2nd_tone; // *10ms
2658
  u8 duration_gap;      // *10ms
2659
  u8 unused03[13];
2660
  struct {
2661
    struct {
2662
      u8 dec;      // one out of LIST_2TONE_DEC
2663
      u8 response; // one out of LIST_2TONE_RESPONSE
2664
      u8 alert;    // 1-16
2665
    } decs[4];
2666
    u8 unused04[4];
2667
  } _2tone_decode[15];
2668
  u8 unused05[16];
2669

    
2670
  struct {
2671
    ul16 freqA;
2672
    ul16 freqB;
2673
    ul16 freqC;
2674
    ul16 freqD;
2675
    // unknown what those values mean, but they are
2676
    // derived from configured frequencies
2677
    ul16 derived_from_freqA; // 2304000/freqA
2678
    ul16 derived_from_freqB; // 2304000/freqB
2679
    ul16 derived_from_freqC; // 2304000/freqC
2680
    ul16 derived_from_freqD; // 2304000/freqD
2681
  }freqs[15];
2682
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
2683
} _2tone;
2684

    
2685
#seekto 0x3D80;
2686
struct {
2687
  u8 vhf_low[3];
2688
  u8 vhf_high[3];
2689
  u8 unknown1[4];
2690
  u8 unknown2[6];
2691
  u8 vhf2_low[3];
2692
  u8 vhf2_high[3];
2693
  u8 unknown3[4];
2694
  u8 unknown4[6];
2695
  u8 uhf_low[3];
2696
  u8 uhf_high[3];
2697
  u8 unknown5[4];
2698
  u8 unknown6[6];
2699
  u8 uhf2_low[3];
2700
  u8 uhf2_high[3];
2701
} ranges;
2702

    
2703
#seekto 0x3F70;
2704
struct {
2705
  char fp[6];
2706
} fingerprint;
2707

    
2708
"""
2709

    
2710

    
2711
class QYTColorHT(BTechMobileCommon):
2712
    """QTY's Color LCD Handheld and alike radios"""
2713
    COLOR_LCD = True
2714
    COLOR_LCD3 = True
2715
    NAME_LENGTH = 8
2716
    LIST_TMR = LIST_TMR15
2717

    
2718
    def process_mmap(self):
2719
        """Process the mem map into the mem object"""
2720

    
2721
        # Get it
2722
        self._memobj = bitwise.parse(COLORHT_MEM_FORMAT, self._mmap)
2723

    
2724
        # load specific parameters from the radio image
2725
        self.set_options()
2726

    
2727
    def set_options(self):
2728
        """This is to read the options from the image and set it in the
2729
        environment, for now just the limits of the freqs in the VHF/UHF
2730
        ranges"""
2731

    
2732
        # setting the correct ranges for each radio type
2733
        ranges = self._memobj.ranges
2734

    
2735
        # the normal dual bands
2736
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
2737
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
2738

    
2739
        # DEBUG
2740
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
2741
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
2742

    
2743
        # the additional bands
2744
        if self.MODEL in ["KT-8R"]:
2745
            # 200Mhz band
2746
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
2747
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
2748
            self._220_range = vhf2
2749

    
2750
            # 350Mhz band
2751
            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
2752
            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
2753
            self._350_range = uhf2
2754

    
2755
        # set the class with the real data
2756
        self._vhf_range = vhf
2757
        self._uhf_range = uhf
2758

    
2759

    
2760
# real radios
2761
@directory.register
2762
class KT8R(QYTColorHT):
2763
    """QYT KT8R"""
2764
    VENDOR = "QYT"
2765
    MODEL = "KT-8R"
2766
    BANDS = 4
2767
    LIST_TMR = LIST_TMR16
2768
    _vhf_range = (136000000, 175000000)
2769
    _220_range = (200000000, 261000000)
2770
    _uhf_range = (400000000, 481000000)
2771
    _350_range = (350000000, 391000000)
2772
    _magic = MSTRING_KT8R
2773
    _fileid = [KT8R_fp2, KT8R_fp1, KT8R_fp, ]
2774
    _power_levels = [chirp_common.PowerLevel("High", watts=5),
2775
                     chirp_common.PowerLevel("Low", watts=1)]
2776

    
2777

    
2778
COLOR9900_MEM_FORMAT = """
2779
#seekto 0x0000;
2780
struct {
2781
  lbcd rxfreq[4];
2782
  lbcd txfreq[4];
2783
  ul16 rxtone;
2784
  ul16 txtone;
2785
  u8 unknown0:4,
2786
     scode:4;
2787
  u8 unknown1:2,
2788
     spmute:2,
2789
     unknown2:2,
2790
     optsig:2;
2791
  u8 unknown3:3,
2792
     scramble:1,
2793
     unknown4:2,
2794
     power:2;
2795
  u8 unknown5:1,
2796
     wide:1,
2797
     unknown6:2,
2798
     bcl:1,
2799
     add:1,
2800
     pttid:2;
2801
} memory[200];
2802

    
2803
#seekto 0x0E00;
2804
struct {
2805
  u8 tmr;             // e00      00 confirmed
2806
  u8 unknown1;        // e01
2807
  u8 sql;             // e02      02 confirmed
2808
  u8 unknown2[2];     // e03-e04
2809
  u8 tot;             // e05      05 confirmed
2810
  u8 volume;          // e06      06 confirmed
2811
  u8 unknown3;        // e07
2812
  u8 abr;             // e08      08 confirmed
2813
  u8 beep;            // e09      09 confirmed
2814
  u8 unknown4[4];     // e0a-e0d
2815
  u8 dsub;            // e0e      14 confirmed
2816
  u8 dtmfst;          // e0f      15 confirmed
2817
  u8 unknown_e10;     // e10
2818
  u8 unknown_e11;     // e11
2819
  u8 screv;           // e12      18 confirmed
2820
  u8 unknown_e13;     // e13
2821
  u8 unknown_e14;     // e14
2822
  u8 pttid;           // e15      21 confirmed
2823
  u8 pttlt;           // e16      22 confirmed
2824
  u8 unknown7;        // e17
2825
  u8 emctp;           // e18      24 confirmed
2826
  u8 emcch;           // e19      25 confirmed
2827
  u8 sigbp;           // e1a      26 confirmed
2828
  u8 unknown8;        // e1b
2829
  u8 camdf;           // e1c      28 confirmed
2830
  u8 cbmdf;           // e1d      29 confirmed
2831
  u8 ccmdf;           // e1e      30 confirmed
2832
  u8 language;        // e1f      31 confirmed
2833
  u8 tmrtx;           // e20      32 confirmed
2834
  u8 vox;             // e21      33 confirmed
2835
  u8 voxt;            // e22      34 confirmed
2836
  u8 autolock;        // e23      35 confirmed
2837
  u8 asfc;            // e24      36 confirmed  above stat fore color
2838
  u8 mainfc;          // e25      37 confirmed  main fore color
2839
  u8 a_fc;            // e26      38 confirmed  a - fore color
2840
  u8 b_fc;            // e27      39 confirmed  b - fore color
2841
  u8 c_fc;            // e28      40 confirmed  c - fore color
2842
  u8 subfc;           // e29      41 confirmed  sub fore color
2843
  u8 battfc;          // e2a      42 confirmed  batt fore color
2844
  u8 sigfc;           // e2b      43 confirmed  signale fore color
2845
  u8 menufc;          // e2c      44 confirmed  menu fore color
2846
  u8 txfc;            // e2d      45 confirmed  tx fore color
2847
  u8 rxfc;            // e2e      46 confirmed  rx fore color
2848
  u8 unknown_e2f;     // e2f
2849
  u8 unknown_e30;     // e30
2850
  u8 unknown9[3];     // e31-e33
2851
  u8 anil;            // e34      52 confirmed
2852
  u8 reps;            // e35      53 confirmed
2853
  u8 tmrmr;           // e36      54 confirmed
2854
  u8 ste;             // e37      55 confirmed
2855
  u8 rpste;           // e38      56 confirmed
2856
  u8 rptdl;           // e39      57 confirmed
2857
  u8 dtmfg;           // e3a      58 confirmed
2858
} settings;
2859

    
2860
#seekto 0x0E80;
2861
struct {
2862
  u8 unknown1;
2863
  u8 vfomr;
2864
  u8 keylock;
2865
  u8 unknown2;
2866
  u8 unknown3:4,
2867
     vfomren:1,
2868
     unknown4:1,
2869
     reseten:1,
2870
     menuen:1;
2871
  u8 unknown5[11];
2872
  u8 dispab;
2873
  u8 unknown6[2];
2874
  u8 menu;
2875
  u8 unknown7[7];
2876
  u8 vfomra;
2877
  u8 vfomrb;
2878
  u8 vfomrc;
2879
  u8 vfomrd;
2880
  u8 mrcha;
2881
  u8 mrchb;
2882
  u8 mrchc;
2883
  u8 mrchd;
2884
} settings2;
2885

    
2886
struct settings_vfo {
2887
  u8 freq[8];
2888
  u8 offset[6];
2889
  u8 unknown2[2];
2890
  ul16 rxtone;
2891
  ul16 txtone;
2892
  u8 scode;
2893
  u8 spmute;
2894
  u8 optsig;
2895
  u8 scramble;
2896
  u8 wide;
2897
  u8 power;
2898
  u8 shiftd;
2899
  u8 step;
2900
  u8 unknown3[4];
2901
};
2902

    
2903
#seekto 0x0F00;
2904
struct {
2905
  struct settings_vfo a;
2906
  struct settings_vfo b;
2907
  struct settings_vfo c;
2908
  struct settings_vfo d;
2909
} vfo;
2910

    
2911
#seekto 0x0F80;
2912
struct {
2913
  char line1[8];
2914
  char line2[8];
2915
  char line3[8];
2916
  char line4[8];
2917
  char line5[8];
2918
  char line6[8];
2919
  char line7[8];
2920
  char line8[8];
2921
} poweron_msg;
2922

    
2923
#seekto 0x0FE0;
2924
struct {
2925
  char line[16];
2926
} static_msg;
2927

    
2928
#seekto 0x1000;
2929
struct {
2930
  char name[7];
2931
  u8 unknown1[9];
2932
} names[200];
2933

    
2934
#seekto 0x2400;
2935
struct {
2936
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
2937
  u8 group_tone;
2938
  u8 repeat_tone;
2939
  u8 unused[13];
2940
} _5tone_std_settings[15];
2941

    
2942
#seekto 0x2500;
2943
struct {
2944
  u8 frame1[5];
2945
  u8 frame2[5];
2946
  u8 frame3[5];
2947
  u8 standard;   // one out of LIST_5TONE_STANDARDS
2948
} _5tone_codes[15];
2949

    
2950
#seekto 0x25F0;
2951
struct {
2952
  u8 _5tone_delay1; // * 10ms
2953
  u8 _5tone_delay2; // * 10ms
2954
  u8 _5tone_delay3; // * 10ms
2955
  u8 _5tone_first_digit_ext_length;
2956
  u8 unknown1;
2957
  u8 unknown2;
2958
  u8 unknown3;
2959
  u8 unknown4;
2960
  u8 decode_standard;
2961
  u8 unknown5:5,
2962
     _5tone_decode_call_frame3:1,
2963
     _5tone_decode_call_frame2:1,
2964
     _5tone_decode_call_frame1:1;
2965
  u8 unknown6:5,
2966
     _5tone_decode_disp_frame3:1,
2967
     _5tone_decode_disp_frame2:1,
2968
     _5tone_decode_disp_frame1:1;
2969
  u8 decode_reset_time; // * 100 + 100ms
2970
} _5tone_settings;
2971

    
2972
#seekto 0x2900;
2973
struct {
2974
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
2975
} dtmf_codes[15];
2976

    
2977
#seekto 0x29F0;
2978
struct {
2979
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10      // 9f0
2980
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10      // 9f1
2981
  u8 unknown0[14];                                             // 9f2-9ff
2982
  u8 inspection[16];                                           // a00-a0f
2983
  u8 monitor[16];                                              // a10-a1f
2984
  u8 alarmcode[16];                                            // a20-a2f
2985
  u8 stun[16];                                                 // a30-a3f
2986
  u8 kill[16];                                                 // a40-a4f
2987
  u8 revive[16];                                               // a50-a5f
2988
  u8 unknown1[16];                                             // a60-a6f
2989
  u8 unknown2[16];                                             // a70-a7f
2990
  u8 unknown3[16];                                             // a80-a8f
2991
  u8 unknown4[16];                                             // a90-a9f
2992
  u8 unknown5[16];                                             // aa0-aaf
2993
  u8 unknown6[16];                                             // ab0-abf
2994
  u8 unknown7[16];                                             // ac0-acf
2995
  u8 masterid[16];                                             // ad0-adf
2996
  u8 viceid[16];                                               // ae0-aef
2997
  u8 unused01:7,                                               // af0
2998
     mastervice:1;
2999
  u8 unused02:3,                                               // af1
3000
     mrevive:1,
3001
     mkill:1,
3002
     mstun:1,
3003
     mmonitor:1,
3004
     minspection:1;
3005
  u8 unused03:3,                                               // af2
3006
     vrevive:1,
3007
     vkill:1,
3008
     vstun:1,
3009
     vmonitor:1,
3010
     vinspection:1;
3011
  u8 unused04:6,                                               // af3
3012
     txdisable:1,
3013
     rxdisable:1;
3014
  u8 groupcode;                                                // af4
3015
  u8 spacecode;                                                // af5
3016
  u8 delayproctime; // * 100 + 100ms                           // af6
3017
  u8 resettime;     // * 100 + 100ms                           // af7
3018
} dtmf_settings;
3019

    
3020
#seekto 0x2D00;
3021
struct {
3022
  struct {
3023
    ul16 freq1;
3024
    u8 unused01[6];
3025
    ul16 freq2;
3026
    u8 unused02[6];
3027
  } _2tone_encode[15];
3028
  u8 duration_1st_tone; // *10ms
3029
  u8 duration_2nd_tone; // *10ms
3030
  u8 duration_gap;      // *10ms
3031
  u8 unused03[13];
3032
  struct {
3033
    struct {
3034
      u8 dec;      // one out of LIST_2TONE_DEC
3035
      u8 response; // one out of LIST_2TONE_RESPONSE
3036
      u8 alert;    // 1-16
3037
    } decs[4];
3038
    u8 unused04[4];
3039
  } _2tone_decode[15];
3040
  u8 unused05[16];
3041

    
3042
  struct {
3043
    ul16 freqA;
3044
    ul16 freqB;
3045
    ul16 freqC;
3046
    ul16 freqD;
3047
    // unknown what those values mean, but they are
3048
    // derived from configured frequencies
3049
    ul16 derived_from_freqA; // 2304000/freqA
3050
    ul16 derived_from_freqB; // 2304000/freqB
3051
    ul16 derived_from_freqC; // 2304000/freqC
3052
    ul16 derived_from_freqD; // 2304000/freqD
3053
  }freqs[15];
3054
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3055
} _2tone;
3056

    
3057
#seekto 0x3D80;
3058
struct {
3059
  u8 vhf_low[3];
3060
  u8 vhf_high[3];
3061
  u8 unknown1[4];
3062
  u8 unknown2[6];
3063
  u8 vhf2_low[3];
3064
  u8 vhf2_high[3];
3065
  u8 unknown3[4];
3066
  u8 unknown4[6];
3067
  u8 uhf_low[3];
3068
  u8 uhf_high[3];
3069
  u8 unknown5[4];
3070
  u8 unknown6[6];
3071
  u8 uhf2_low[3];
3072
  u8 uhf2_high[3];
3073
} ranges;
3074

    
3075
#seekto 0x3F70;
3076
struct {
3077
  char fp[6];
3078
} fingerprint;
3079

    
3080
"""
3081

    
3082

    
3083
COLOR20V2_MEM_FORMAT = """
3084
#seekto 0x0000;
3085
struct {
3086
  lbcd rxfreq[4];
3087
  lbcd txfreq[4];
3088
  ul16 rxtone;
3089
  ul16 txtone;
3090
  u8 unknown0:4,
3091
     scode:4;
3092
  u8 unknown1:2,
3093
     spmute:2,
3094
     unknown2:2,
3095
     optsig:2;
3096
  u8 unknown3:3,
3097
     scramble:1,
3098
     unknown4:2,
3099
     power:2;
3100
  u8 unknown5:1,
3101
     wide:1,
3102
     unknown6:2,
3103
     bcl:1,
3104
     add:1,
3105
     pttid:2;
3106
} memory[200];
3107

    
3108
#seekto 0x0E00;
3109
struct {
3110
  u8 tmr;             // e00      00 confirmed
3111
  u8 unknown1;        // e01
3112
  u8 sql;             // e02      02 confirmed
3113
  u8 unknown2;        // e03
3114
  u8 autolock;        // e04      04 confirmed
3115
  u8 tot;             // e05      05 confirmed
3116
  u8 apo;             // e06      06 confirmed
3117
  u8 unknown3;        // e07
3118
  u8 abr;             // e08      08 confirmed
3119
  u8 beep;            // e09      09 confirmed
3120
  u8 unknown4[4];     // e0a-e0d
3121
  u8 dtmfst;          // e0e      14 confirmed
3122
  u8 unknown5[2];     // e0f-e10
3123
  u8 screv;           // e11      17 confirmed
3124
  u8 unknown6[2];     // e12-e13
3125
  u8 pttid;           // e14      20 confirmed
3126
  u8 pttlt;           // e15      21 confirmed
3127
  u8 unknown7;        // e16
3128
  u8 emctp;           // e17      23 confirmed
3129
  u8 emcch;           // e18      24 confirmed
3130
  u8 sigbp;           // e19      25confirmed
3131
  u8 unknown8;        // e1a
3132
  u8 camdf;           // e1b      27 confirmed
3133
  u8 cbmdf;           // e1c      28 confirmed
3134
  u8 ccmdf;           // e1d      29 confirmed
3135
  u8 vox;             // e1e      30 confirmed
3136
  u8 voxt;            // e1f      31 confirmed
3137
  u8 sync;            // e20      32 confirmed
3138
  u8 asfc;            // e21      33 confirmed  above stat fore color
3139
  u8 mainfc;          // e22      34 confirmed  main fore color
3140
  u8 a_fc;            // e23      35 confirmed  a - fore color
3141
  u8 b_fc;            // e24      36 confirmed  b - fore color
3142
  u8 c_fc;            // e25      37 confirmed  c - fore color
3143
  u8 subfc;           // e26      38 confirmed  sub fore color
3144
  u8 battfc;          // e27      39 confirmed  batt fore color
3145
  u8 sigfc;           // e28      40 confirmed  signale fore color
3146
  u8 menufc;          // e29      41 confirmed  menu fore color
3147
  u8 txfc;            // e2a      42 confirmed  tx fore color
3148
  u8 rxfc;            // e2b      43 confirmed  rx fore color
3149
  u8 repsw;           // e2c      44 confirmed
3150
  u8 dsub;            // e2d      45 confirmed
3151
  u8 unknown9[5];     // e2e-e32
3152
  u8 anil;            // e33      51 confirmed
3153
  u8 reps;            // e34      52 confirmed
3154
  u8 repm;            // e35      53 confirmed
3155
  u8 tmrmr;           // e36      54 confirmed
3156
  u8 ste;             // e37      55 confirmed
3157
  u8 rpste;           // e38      56 confirmed
3158
  u8 rptdl;           // e39      57 confirmed
3159
  u8 dtmfg;           // e3a      58 confirmed
3160
  u8 mgain;           // e3b      59 confirmed
3161
  u8 skiptx;          // e3c      60 confirmed
3162
  u8 scmode;          // e3d      61 confirmed
3163
  u8 tmrtx;           // e3e      62 confirmed
3164
  u8 volume;          // e3f      63 confirmed
3165
  u8 unknown_10;      // e40
3166
  u8 save;            // e41      65 confirmed
3167
} settings;
3168

    
3169
#seekto 0x0E80;
3170
struct {
3171
  u8 unknown1;
3172
  u8 vfomr;
3173
  u8 keylock;
3174
  u8 unknown2;
3175
  u8 unknown3:4,
3176
     vfomren:1,
3177
     unknown4:1,
3178
     reseten:1,
3179
     menuen:1;
3180
  u8 unknown5[11];
3181
  u8 dispab;
3182
  u8 unknown6[2];
3183
  u8 menu;
3184
  u8 unknown7[7];
3185
  u8 vfomra;
3186
  u8 vfomrb;
3187
  u8 vfomrc;
3188
  u8 vfomrd;
3189
  u8 mrcha;
3190
  u8 mrchb;
3191
  u8 mrchc;
3192
  u8 mrchd;
3193
} settings2;
3194

    
3195
struct settings_vfo {
3196
  u8 freq[8];
3197
  u8 offset[6];
3198
  u8 unknown2[2];
3199
  ul16 rxtone;
3200
  ul16 txtone;
3201
  u8 scode;
3202
  u8 spmute;
3203
  u8 optsig;
3204
  u8 scramble;
3205
  u8 wide;
3206
  u8 power;
3207
  u8 shiftd;
3208
  u8 step;
3209
  u8 unknown3[4];
3210
};
3211

    
3212
#seekto 0x0F00;
3213
struct {
3214
  struct settings_vfo a;
3215
  struct settings_vfo b;
3216
  struct settings_vfo c;
3217
  struct settings_vfo d;
3218
} vfo;
3219

    
3220
#seekto 0x0F80;
3221
struct {
3222
  char line1[8];
3223
  char line2[8];
3224
  char line3[8];
3225
  char line4[8];
3226
  char line5[8];
3227
  char line6[8];
3228
  char line7[8];
3229
  char line8[8];
3230
} poweron_msg;
3231

    
3232
#seekto 0x0FE0;
3233
struct {
3234
  char line[16];
3235
} static_msg;
3236

    
3237
#seekto 0x1000;
3238
struct {
3239
  char name[7];
3240
  u8 unknown1[9];
3241
} names[200];
3242

    
3243
#seekto 0x2400;
3244
struct {
3245
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
3246
  u8 group_tone;
3247
  u8 repeat_tone;
3248
  u8 unused[13];
3249
} _5tone_std_settings[15];
3250

    
3251
#seekto 0x2500;
3252
struct {
3253
  u8 frame1[5];
3254
  u8 frame2[5];
3255
  u8 frame3[5];
3256
  u8 standard;   // one out of LIST_5TONE_STANDARDS
3257
} _5tone_codes[15];
3258

    
3259
#seekto 0x25F0;
3260
struct {
3261
  u8 _5tone_delay1; // * 10ms
3262
  u8 _5tone_delay2; // * 10ms
3263
  u8 _5tone_delay3; // * 10ms
3264
  u8 _5tone_first_digit_ext_length;
3265
  u8 unknown1;
3266
  u8 unknown2;
3267
  u8 unknown3;
3268
  u8 unknown4;
3269
  u8 decode_standard;
3270
  u8 unknown5:5,
3271
     _5tone_decode_call_frame3:1,
3272
     _5tone_decode_call_frame2:1,
3273
     _5tone_decode_call_frame1:1;
3274
  u8 unknown6:5,
3275
     _5tone_decode_disp_frame3:1,
3276
     _5tone_decode_disp_frame2:1,
3277
     _5tone_decode_disp_frame1:1;
3278
  u8 decode_reset_time; // * 100 + 100ms
3279
} _5tone_settings;
3280

    
3281
#seekto 0x2900;
3282
struct {
3283
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
3284
} dtmf_codes[15];
3285

    
3286
#seekto 0x29F0;
3287
struct {
3288
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
3289
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
3290
  u8 unknown0[14];
3291
  u8 inspection[16];
3292
  u8 monitor[16];
3293
  u8 alarmcode[16];
3294
  u8 stun[16];
3295
  u8 kill[16];
3296
  u8 revive[16];
3297
  u8 unknown1[16];
3298
  u8 unknown2[16];
3299
  u8 unknown3[16];
3300
  u8 unknown4[16];
3301
  u8 unknown5[16];
3302
  u8 unknown6[16];
3303
  u8 unknown7[16];
3304
  u8 masterid[16];
3305
  u8 viceid[16];
3306
  u8 unused01:7,
3307
     mastervice:1;
3308
  u8 unused02:3,
3309
     mrevive:1,
3310
     mkill:1,
3311
     mstun:1,
3312
     mmonitor:1,
3313
     minspection:1;
3314
  u8 unused03:3,
3315
     vrevive:1,
3316
     vkill:1,
3317
     vstun:1,
3318
     vmonitor:1,
3319
     vinspection:1;
3320
  u8 unused04:6,
3321
     txdisable:1,
3322
     rxdisable:1;
3323
  u8 groupcode;
3324
  u8 spacecode;
3325
  u8 delayproctime; // * 100 + 100ms
3326
  u8 resettime;     // * 100 + 100ms
3327
} dtmf_settings;
3328

    
3329
#seekto 0x2D00;
3330
struct {
3331
  struct {
3332
    ul16 freq1;
3333
    u8 unused01[6];
3334
    ul16 freq2;
3335
    u8 unused02[6];
3336
  } _2tone_encode[15];
3337
  u8 duration_1st_tone; // *10ms
3338
  u8 duration_2nd_tone; // *10ms
3339
  u8 duration_gap;      // *10ms
3340
  u8 unused03[13];
3341
  struct {
3342
    struct {
3343
      u8 dec;      // one out of LIST_2TONE_DEC
3344
      u8 response; // one out of LIST_2TONE_RESPONSE
3345
      u8 alert;    // 1-16
3346
    } decs[4];
3347
    u8 unused04[4];
3348
  } _2tone_decode[15];
3349
  u8 unused05[16];
3350

    
3351
  struct {
3352
    ul16 freqA;
3353
    ul16 freqB;
3354
    ul16 freqC;
3355
    ul16 freqD;
3356
    // unknown what those values mean, but they are
3357
    // derived from configured frequencies
3358
    ul16 derived_from_freqA; // 2304000/freqA
3359
    ul16 derived_from_freqB; // 2304000/freqB
3360
    ul16 derived_from_freqC; // 2304000/freqC
3361
    ul16 derived_from_freqD; // 2304000/freqD
3362
  }freqs[15];
3363
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
3364
} _2tone;
3365

    
3366
#seekto 0x3D80;
3367
struct {
3368
  u8 vhf_low[3];
3369
  u8 vhf_high[3];
3370
  u8 unknown1[4];
3371
  u8 unknown2[6];
3372
  u8 vhf2_low[3];
3373
  u8 vhf2_high[3];
3374
  u8 unknown3[4];
3375
  u8 unknown4[6];
3376
  u8 uhf_low[3];
3377
  u8 uhf_high[3];
3378
  u8 unknown5[4];
3379
  u8 unknown6[6];
3380
  u8 uhf2_low[3];
3381
  u8 uhf2_high[3];
3382
} ranges;
3383

    
3384
#seekto 0x3F70;
3385
struct {
3386
  char fp[6];
3387
} fingerprint;
3388

    
3389
"""
3390

    
3391

    
3392
class BTechColorWP(BTechMobileCommon):
3393
    """BTECH's Color WP Mobile and alike radios"""
3394
    COLOR_LCD = True
3395
    COLOR_LCD4 = True
3396
    NAME_LENGTH = 7
3397
    LIST_TMR = LIST_TMR7
3398

    
3399
    #def process_mmap(self):
3400
    #    """Process the mem map into the mem object"""
3401

    
3402
    #    # Get it
3403
    #    self._memobj = bitwise.parse(COLORWP_MEM_FORMAT, self._mmap)
3404

    
3405
    #    # load specific parameters from the radio image
3406
    #    self.set_options()
3407

    
3408
    def set_options(self):
3409
        """This is to read the options from the image and set it in the
3410
        environment, for now just the limits of the freqs in the VHF/UHF
3411
        ranges"""
3412

    
3413
        # setting the correct ranges for each radio type
3414
        ranges = self._memobj.ranges
3415

    
3416
        # the normal dual bands
3417
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
3418
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
3419

    
3420
        # DEBUG
3421
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
3422
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
3423

    
3424
        # set the class with the real data
3425
        self._vhf_range = vhf
3426
        self._uhf_range = uhf
3427

    
3428

    
3429
@directory.register
3430
class WP9900(BTechColorWP):
3431
    """Anysecu WP-9900"""
3432
    VENDOR = "Anysecu"
3433
    MODEL = "WP-9900"
3434
    BANDS = 2
3435
    ##LIST_TMR = LIST_TMR7
3436
    UPLOAD_MEM_SIZE = 0X3100
3437
    ##UPLOAD_MEM_SIZE = 0X4000
3438
    _power_levels = [chirp_common.PowerLevel("High", watts=25),
3439
                     chirp_common.PowerLevel("Low", watts=5)]
3440
    _upper = 199
3441
    _magic = MSTRING_WP9900
3442
    _fileid = [WP9900_fp, ]
3443
    _gmrs = False  ##True
3444

    
3445

    
3446
    def process_mmap(self):
3447
        """Process the mem map into the mem object"""
3448

    
3449
        # Get it
3450
        self._memobj = bitwise.parse(COLOR9900_MEM_FORMAT, self._mmap)
3451

    
3452
        # load specific parameters from the radio image
3453
        self.set_options()
3454

    
3455

    
3456
# real radios
3457
@directory.register
3458
class GMRS20V2(BTechColorWP):
3459
    """Baofeng Tech GMRS-20V2"""
3460
    MODEL = "GMRS-20V2"
3461
    BANDS = 2
3462
    ##LIST_TMR = LIST_TMR7
3463
    UPLOAD_MEM_SIZE = 0X3100
3464
    ##UPLOAD_MEM_SIZE = 0X4000
3465
    _power_levels = [chirp_common.PowerLevel("High", watts=20),
3466
                     chirp_common.PowerLevel("", watts=6),
3467
                     chirp_common.PowerLevel("Low", watts=5)]
3468
    _upper = 199
3469
    _magic = MSTRING_GMRS20V2
3470
    _fileid = [GMRS20V2_fp, ]
3471
    _gmrs = True
3472

    
3473
    def process_mmap(self):
3474
        """Process the mem map into the mem object"""
3475

    
3476
        # Get it
3477
        self._memobj = bitwise.parse(COLOR20V2_MEM_FORMAT, self._mmap)
3478

    
3479
        # load specific parameters from the radio image
3480
        self.set_options()
(2-2/13)