vgc_vr6600_test3.py

more delay between uploaded blocks - Jim Unroe, 11/28/2016 02:32 am

Download (49.2 kB)

 
1
# Copyright 2016:
2
# * Jim Unroe KC9HI, <rock.unroe@gmail.com>
3
# * Pavel Milanes CO7WT <pavelmc@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
import re
22

    
23
LOG = logging.getLogger(__name__)
24

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

    
33
MEM_FORMAT = """
34
struct mem {
35
  lbcd rxfreq[4];
36
  lbcd txfreq[4];
37
  lbcd rxtone[2];
38
  lbcd txtone[2];
39
  u8 unknown0:2,
40
     txp:2,
41
     wn:2,
42
     unknown1:1,
43
     bcl:1;
44
  u8 unknown2:2,
45
     revert:1,
46
     dname:1,
47
     unknown3:4;
48
  u8 unknown4[2];
49
};
50

    
51
struct nam {
52
  char name[6];
53
  u8 unknown1[2];
54
};
55

    
56
#seekto 0x0000;
57
struct mem left_memory[500];
58

    
59
#seekto 0x2000;
60
struct mem right_memory[500];
61

    
62
#seekto 0x4000;
63
struct nam left_names[500];
64

    
65
#seekto 0x5000;
66
struct nam right_names[500];
67

    
68
#seekto 0x6000;
69
u8 left_usedflags[64];
70

    
71
#seekto 0x6040;
72
u8 left_scanflags[64];
73

    
74
#seekto 0x6080;
75
u8 right_usedflags[64];
76

    
77
#seekto 0x60C0;
78
u8 right_scanflags[64];
79

    
80
#seekto 0x6160;
81
struct {
82
  char line32[32];
83
} embedded_msg;
84

    
85
#seekto 0x6180;
86
struct {
87
  u8  sbmute:2,        // sub band mute
88
      unknown1:1,
89
      workmodb:1,      // work mode (right side)
90
      dw:1,            // dual watch
91
      audio:1,         // audio output mode (stereo/mono)
92
      unknown2:1,
93
      workmoda:1;      // work mode (left side)
94
  u8  scansb:1,        // scan stop beep
95
      aftone:3,        // af tone control
96
      scand:1,         // scan directon
97
      scanr:3;         // scan resume
98
  u8  rxexp:1,         // rx expansion
99
      ptt:1,           // ptt mode
100
      display:1,       // display select (frequency/clock)
101
      omode:1,         // operaton mode
102
      beep:2,          // beep volume
103
      spkr:2;          // speaker
104
  u8  cpuclk:1,        // operating mode(cpu clock)
105
      fkey:3,          // fkey function
106
      mrscan:1,        // memory scan type
107
      color:3;         // lcd backlight color
108
  u8  vox:2,           // vox
109
      voxs:3,          // vox sensitivity
110
      mgain:3;         // mic gain
111
  u8  wbandb:4,        // work band (right side)
112
      wbanda:4;        // work band (left side)
113
  u8  sqlb:4,          // squelch level (right side)
114
      sqla:4;          // squelch level (left side)
115
  u8  apo:4,           // auto power off
116
      ars:1,           // automatic repeater shift
117
      tot:3;           // time out timer
118
  u8  stepb:4,         // auto step (right side)
119
      stepa:4;         // auto step (left side)
120
  u8  rxcoverm:1,      // rx coverage-memory
121
      lcdc:3,          // lcd contrast
122
      rxcoverv:1,      // rx coverage-vfo
123
      lcdb:3;          // lcd brightness
124
  u8  smode:1,         // smart function mode
125
      timefmt:1,       // time format
126
      datefmt:2,       // date format
127
      timesig:1,       // time signal
128
      keyb:3;          // key/led brightness
129
  u8  dwstop:1,        // dual watch stop
130
      unknown3:1,
131
      sqlexp:1,        // sql expansion
132
      decbandsel:1,    // decoding band select
133
      dtmfmodenc:1,    // dtmf mode encode
134
      bell:3;          // bell ringer
135
  u8  unknown4:2,
136
      btime:6;         // lcd backlight time
137
  u8  unknown5:2,
138
      tz:6;            // time zone
139
  u8  unknown618E;
140
  u8  unknown618F;
141
  ul16  offseta;       // work offset (left side)
142
  ul16  offsetb;       // work offset (right side)
143
  ul16  mrcha;         // selected memory channel (left)
144
  ul16  mrchb;         // selected memory channel (right)
145
  ul16  wpricha;       // work priority channel (left)
146
  ul16  wprichb;       // work priority channel (right)
147
  u8  unknown6:3,
148
      datasql:2,       // data squelch
149
      dataspd:1,       // data speed
150
      databnd:2;       // data band select
151
  u8  unknown7:1,
152
      pfkey2:3,        // mic p2 key
153
      unknown8:1,
154
      pfkey1:3;        // mic p1 key
155
  u8  unknown9:1,
156
      pfkey4:3,        // mic p4 key
157
      unknowna:1,
158
      pfkey3:3;        // mic p3 key
159
  u8  unknownb:7,
160
      dtmfmoddec:1;    // dtmf mode decode
161
} settings;
162

    
163
#seekto 0x61B0;
164
struct {
165
  char line16[16];
166
} poweron_msg;
167

    
168
#seekto 0x6300;
169
struct {
170
  u8  unknown1:3,
171
      ttdgt:5;         // dtmf digit time
172
  u8  unknown2:3,
173
      ttint:5;         // dtmf interval time
174
  u8  unknown3:3,
175
      tt1stdgt:5;      // dtmf 1st digit time
176
  u8  unknown4:3,
177
      tt1stdly:5;      // dtmf 1st digit delay
178
  u8  unknown5:3,
179
      ttdlyqt:5;       // dtmf delay when use qt
180
  u8  unknown6:3,
181
      ttdkey:5;        // dtmf d key function
182
  u8  unknown7;
183
  u8  unknown8:4,
184
      ttautod:4;       // dtmf auto dial group
185
} dtmf;
186

    
187
#seekto 0x6330;
188
struct {
189
  u8  unknown1:7,
190
      ttsig:1;         // dtmf signal
191
  u8  unknown2:4,
192
      ttintcode:4;     // dtmf interval code
193
  u8  unknown3:5,
194
      ttgrpcode:3;     // dtmf group code
195
  u8  unknown4:4,
196
      ttautorst:4;     // dtmf auto reset time
197
  u8  unknown5:5,
198
      ttalert:3;       // dtmf alert tone/transpond
199
} dtmf2;
200

    
201
#seekto 0x6360;
202
struct {
203
  u8 code1[8];         // dtmf code
204
  u8 code1_len;        // dtmf code length
205
  u8 unknown1[7];
206
  u8 code2[8];         // dtmf code
207
  u8 code2_len;        // dtmf code length
208
  u8 unknown2[7];
209
  u8 code3[8];         // dtmf code
210
  u8 code3_len;        // dtmf code length
211
  u8 unknown3[7];
212
  u8 code4[8];         // dtmf code
213
  u8 code4_len;        // dtmf code length
214
  u8 unknown4[7];
215
  u8 code5[8];         // dtmf code
216
  u8 code5_len;        // dtmf code length
217
  u8 unknown5[7];
218
  u8 code6[8];         // dtmf code
219
  u8 code6_len;        // dtmf code length
220
  u8 unknown6[7];
221
  u8 code7[8];         // dtmf code
222
  u8 code7_len;        // dtmf code length
223
  u8 unknown7[7];
224
  u8 code8[8];         // dtmf code
225
  u8 code8_len;        // dtmf code length
226
  u8 unknown8[7];
227
  u8 code9[8];         // dtmf code
228
  u8 code9_len;        // dtmf code length
229
  u8 unknown9[7];
230
} dtmfcode;
231

    
232
"""
233

    
234
MEM_SIZE = 0x8000
235
BLOCK_SIZE = 0x40
236
MODES = ["FM", "Auto", "NFM", "AM"]
237
SKIP_VALUES = ["", "S"]
238
TONES = chirp_common.TONES
239
DTCS_CODES = chirp_common.DTCS_CODES
240
NAME_LENGTH = 6
241
DTMF_CHARS = list("0123456789ABCD*#")
242
####STIMEOUT = 1
243
STIMEOUT = 2
244

    
245
# Basic settings lists
246
LIST_AFTONE = ["Low-3", "Low-2", "Low-1", "Normal", "High-1", "High-2"]
247
LIST_SPKR = ["Off", "Front", "Rear", "Front + Rear"]
248
LIST_AUDIO = ["Monaural", "Stereo"]
249
LIST_SBMUTE = ["Off", "TX", "RX", "Both"]
250
LIST_MLNHM = ["Min", "Low", "Normal", "High", "Max"]
251
LIST_PTT = ["Momentary", "Toggle"]
252
LIST_RXEXP = ["General", "Wide coverage"]
253
LIST_VOX = ["Off", "Internal mic", "Front hand-mic", "Rear hand-mic"]
254
LIST_DISPLAY = ["Frequency", "Timer/Clock"]
255
LIST_MINMAX = ["Min"] + ["%s" % x for x in range(2, 8)] + ["Max"]
256
LIST_COLOR = ["White-Blue", "Sky-Blue", "Marine-Blue", "Green",
257
              "Yellow-Green", "Orange", "Amber", "White"]
258
LIST_BTIME = ["Continuous"] + ["%s" % x for x in range(1, 61)]
259
LIST_MRSCAN = ["All", "Selected"]
260
LIST_DWSTOP = ["Auto", "Hold"]
261
LIST_SCAND = ["Down", "Up"]
262
LIST_SCANR = ["Busy", "Hold", "1 sec", "3 sec", "5 sec"]
263
LIST_APO = ["Off", ".5", "1", "1.5"] + ["%s" % x for x in range(2, 13)]
264
LIST_BEEP = ["Off", "Low", "High"]
265
LIST_FKEY = ["MHz/AD-F", "AF Dual 1(line-in)", "AF Dual 2(AM)", "AF Dual 3(FM)",
266
             "PA", "SQL off", "T-call", "WX"]
267
LIST_PFKEY = ["Off", "SQL off", "TX power", "Scan", "RPT shift", "Reverse",
268
              "T-Call"]
269
LIST_AB = ["A", "B"]
270
LIST_COVERAGE = ["In band", "All"]
271
LIST_TOT = ["Off"] + ["%s" % x for x in range(5, 25, 5)] + ["30"]
272
LIST_DATEFMT = ["yyyy/mm/dd", "yyyy/dd/mm", "mm/dd/yyyy", "dd/mm/yyyy"]
273
LIST_TIMEFMT = ["24H", "12H"]
274
LIST_TZ = ["-12 INT DL W",
275
           "-11 MIDWAY",
276
           "-10 HAST",
277
           "-9 AKST",
278
           "-8 PST",
279
           "-7 MST",
280
           "-6 CST",
281
           "-5 EST",
282
           "-4:30 CARACAS",
283
           "-4 AST",
284
           "-3:30 NST",
285
           "-3 BRASILIA",
286
           "-2 MATLANTIC",
287
           "-1 AZORES",
288
           "-0 LONDON",
289
           "+0 LONDON",
290
           "+1 ROME",
291
           "+2 ATHENS",
292
           "+3 MOSCOW",
293
           "+3:30 REHRW",
294
           "+4 ABUDNABI",
295
           "+4:30 KABUL",
296
           "+5 ISLMABAD",
297
           "+5:30 NEWDELHI",
298
           "+6 DHAKA",
299
           "+6:30 YANGON",
300
           "+7 BANKOK",
301
           "+8 BEIJING",
302
           "+9 TOKYO",
303
           "+10 ADELAIDE",
304
           "+10 SYDNET",
305
           "+11 NWCLDNIA",
306
           "+12 FIJI",
307
           "+13 NUKALOFA"
308
           ]
309
LIST_BELL = ["Off", "1 time", "3 times", "5 times", "8 times", "Continuous"]
310
LIST_DATABND = ["Main band", "Sub band", "Left band-fixed", "Right band-fixed"]
311
LIST_DATASPD = ["1200 bps", "9600 bps"]
312
LIST_DATASQL = ["Busy/TX", "Busy", "TX"]
313

    
314
# Other settings lists
315
LIST_CPUCLK = ["Clock frequency 1", "Clock frequency 2"]
316

    
317
# Work mode settings lists
318
LIST_WORK = ["VFO", "Memory System"]
319
LIST_WBANDB = ["Air", "H-V", "GR1-V", "GR1-U", "H-U", "GR2"]
320
LIST_WBANDA = ["Line-in", "AM", "FM"] + LIST_WBANDB
321
LIST_SQL = ["Open"] + ["%s" % x for x in range(1, 10)]
322
LIST_STEP = ["Auto", "2.50 KHz", "5.00 KHz", "6.25 KHz", "8.33 KHz",
323
             "9.00 KHz", "10.00 KHz", "12.50 KHz", "15.00 KHz", "20.00 KHz",
324
             "25.00 KHz", "50.00 KHz", "100.00 KHz", "200.00 KHz"]
325
LIST_SMODE = ["F-1", "F-2"]
326

    
327
# DTMF settings lists
328
LIST_TTDKEY = ["D code"] + ["Send delay %s s" % x for x in range(1, 17)]
329
LIST_TT200 = ["%s ms" % x for x in range(50, 210, 10)]
330
LIST_TT1000 = ["%s ms" % x for x in range(100, 1050, 50)]
331
LIST_TTSIG = ["Code squelch", "Select call"]
332
LIST_TTAUTORST = ["Off"] + ["%s s" % x for x in range(1, 16)]
333
LIST_TTGRPCODE = ["Off"] + list("ABCD*#")
334
LIST_TTINTCODE = DTMF_CHARS
335
LIST_TTALERT = ["Off", "Alert tone", "Transpond", "Transpond-ID code",
336
                "Transpond-transpond code"]
337
LIST_TTAUTOD = ["%s" % x for x in range(1, 10)]
338

    
339
# valid chars on the LCD
340
VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
341
    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
342

    
343
# Power Levels
344
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=5),
345
                chirp_common.PowerLevel("Mid", watts=20),
346
                chirp_common.PowerLevel("High", watts=50)]
347

    
348
# B-TECH UV-50X3 id string
349
####UV50X3_id  = "VGC6600MD"
350
UV50X3_id  = "USA6600A52013417"
351

    
352
# VGC VR-6600PRO id string
353
VR6600PRO_id  = "USA660052013417A"
354

    
355
def _clean_buffer(radio):
356
    radio.pipe.timeout = 0.005
357
    junk = radio.pipe.read(256)
358
    radio.pipe.timeout = STIMEOUT
359
    if junk:
360
        Log.debug("Got %i bytes of junk before starting" % len(junk))
361

    
362

    
363
#def _check_for_double_ack(radio):
364
#    radio.pipe.timeout = 0.005
365
#    c = radio.pipe.read(1)
366
#    radio.pipe.timeout = STIMEOUT
367
#    if c and c != '\x06':
368
#        _exit_program_mode(radio)
369
#        raise errors.RadioError('Expected nothing or ACK, got %r' % c)
370

    
371

    
372
def _rawrecv(radio, amount):
373
    """Raw read from the radio device"""
374
    data = ""
375
    try:
376
        data = radio.pipe.read(amount)
377
    except:
378
        _exit_program_mode(radio)
379
        msg = "Generic error reading data from radio; check your cable."
380
        raise errors.RadioError(msg)
381

    
382
    if len(data) != amount:
383
        _exit_program_mode(radio)
384
        msg = "Error reading data from radio: not the amount of data we want."
385
        raise errors.RadioError(msg)
386

    
387
    return data
388

    
389

    
390
def _rawsend(radio, data):
391
    """Raw send to the radio device"""
392
    try:
393
        radio.pipe.write(data)
394
    except:
395
        raise errors.RadioError("Error sending data to radio")
396

    
397

    
398
def _make_frame(cmd, addr, length, data=""):
399
    """Pack the info in the headder format"""
400
    frame = struct.pack(">BHB", ord(cmd), addr, length)
401
    # add the data if set
402
    if len(data) != 0:
403
        frame += data
404
    # return the data
405
    return frame
406

    
407

    
408
def _recv(radio, addr, length=BLOCK_SIZE):
409
    """Get data from the radio """
410
    # read 4 bytes of header
411
    hdr = _rawrecv(radio, 4)
412

    
413
    if radio.MODEL == "UV-50X3":
414
        # check for unexpected extra command byte
415
        c, a, l = struct.unpack(">BHB", hdr)
416
        if hdr[0:2] == "WW" and a != addr:
417
            LOG.debug("Double CMD byte detected at ADDR: %04x" % (addr))
418
            # extra command byte detected
419
            # throw away the 1st byte and add the next byte in the buffer
420
            hdr = hdr[1:] + _rawrecv(radio, 1)
421

    
422
    # read 64 bytes (0x40) of data
423
    data = _rawrecv(radio, (BLOCK_SIZE))
424

    
425
    # DEBUG
426
    LOG.info("Response:")
427
    LOG.debug(util.hexprint(hdr + data))
428

    
429
    c, a, l = struct.unpack(">BHB", hdr)
430
    ####if a != addr or l != length or c != ord("W"):
431
    if l != length or c != ord("W"):
432
        _exit_program_mode(radio)
433
        LOG.error("Invalid answer for block 0x%04x:" % addr)
434
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
435
        raise errors.RadioError("Unknown response from the radio")
436

    
437
    return data
438

    
439

    
440
def _do_ident(radio):
441
    """Put the radio in PROGRAM mode & identify it"""
442
    #  set the serial discipline
443
    radio.pipe.baudrate = 115200
444
    radio.pipe.parity = "N"
445
    radio.pipe.timeout = STIMEOUT
446

    
447
    # flush input buffer
448
    _clean_buffer(radio)
449

    
450
    magic = "V66LINK"
451

    
452
    _rawsend(radio, magic)
453

    
454
    # Ok, get the ident string
455
    ident = _rawrecv(radio, 9)
456

    
457
    # check if ident is OK
458
    ####if ident != radio.IDENT:
459
    if ident != "VGC6600MD":
460
        # bad ident
461
        msg = "Incorrect model ID, got this:"
462
        msg +=  util.hexprint(ident)
463
        LOG.debug(msg)
464
        raise errors.RadioError("Radio identification failed.")
465

    
466
    # DEBUG
467
    LOG.info("Positive ident, got this:")
468
    LOG.debug(util.hexprint(ident))
469

    
470
    return True
471

    
472

    
473
def _exit_program_mode(radio):
474
    endframe = "\x45"
475
    _rawsend(radio, endframe)
476

    
477

    
478
def _download(radio):
479
    """Get the memory map"""
480

    
481
    # put radio in program mode and identify it
482
    _do_ident(radio)
483

    
484
    # UI progress
485
    status = chirp_common.Status()
486
    status.cur = 0
487
    status.max = MEM_SIZE / BLOCK_SIZE
488
    status.msg = "Cloning from radio..."
489
    radio.status_fn(status)
490

    
491
    data = ""
492
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
493
        frame = _make_frame("R", addr, BLOCK_SIZE)
494
        # DEBUG
495
        LOG.info("Request sent:")
496
        LOG.debug(util.hexprint(frame))
497

    
498
        # sending the read request
499
        _rawsend(radio, frame)
500

    
501
        # now we read
502
        d = _recv(radio, addr)
503

    
504
        # aggregate the data
505
        data += d
506

    
507
        # UI Update
508
        status.cur = addr / BLOCK_SIZE
509
        status.msg = "Cloning from radio..."
510
        radio.status_fn(status)
511

    
512
    _exit_program_mode(radio)
513

    
514
    return data
515

    
516

    
517
def _upload(radio):
518
    """Upload procedure"""
519

    
520
    MEM_SIZE = 0x7000
521

    
522
    # put radio in program mode and identify it
523
    _do_ident(radio)
524

    
525
    # UI progress
526
    status = chirp_common.Status()
527
    status.cur = 0
528
    status.max = MEM_SIZE / BLOCK_SIZE
529
    status.msg = "Cloning to radio..."
530
    radio.status_fn(status)
531

    
532
    # the fun start here
533
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
534
        # sending the data
535
        data = radio.get_mmap()[addr:addr + BLOCK_SIZE]
536

    
537
        frame = _make_frame("W", addr, BLOCK_SIZE, data)
538

    
539
        _rawsend(radio, frame)
540

    
541
        # receiving the response
542
        ack = _rawrecv(radio, 1)
543
        if ack != "\x06":
544
            _exit_program_mode(radio)
545
            msg = "Bad ack writing block 0x%04x" % addr
546
            raise errors.RadioError(msg)
547

    
548
        time.sleep(0.1)
549
        ####_check_for_double_ack(radio)
550

    
551
        # UI Update
552
        status.cur = addr / BLOCK_SIZE
553
        status.msg = "Cloning to radio..."
554
        radio.status_fn(status)
555

    
556
    _exit_program_mode(radio)
557

    
558

    
559
def model_match(cls, data):
560
    """Match the opened/downloaded image to the correct version"""
561
    ####rid = data[0x6140:0x6148]
562
    rid = data[0x7FF0:0x8000]
563

    
564
    #if rid in cls._fileid:
565
    if rid in cls.IDENT:
566
        return True
567

    
568
    return False
569

    
570

    
571
class VGCStyleRadio(chirp_common.CloneModeRadio,
572
                    chirp_common.ExperimentalRadio):
573
    """BTECH's UV-50X3"""
574
    VENDOR = ""
575
    _air_range = (108000000, 136000000)
576
    _vhf_range = (136000000, 174000000)
577
    _vhf2_range = (174000000, 250000000)
578
    _220_range = (222000000, 225000000)
579
    _gen1_range = (300000000, 400000000)
580
    _uhf_range = (400000000, 480000000)
581
    _gen2_range = (480000000, 520000000)
582
    _upper = 499
583
    MODEL = ""
584
    IDENT = ""
585

    
586
    @classmethod
587
    def get_prompts(cls):
588
        rp = chirp_common.RadioPrompts()
589
        rp.experimental = \
590
            ('The UV-50X3 driver is a beta version.\n'
591
             '\n'
592
             'Please save an unedited copy of your first successful\n'
593
             'download to a CHIRP Radio Images(*.img) file.'
594
             )
595
        rp.pre_download = _(dedent("""\
596
            Follow this instructions to download your info:
597

    
598
            1 - Turn off your radio
599
            2 - Connect your interface cable
600
            3 - Turn on your radio
601
            4 - Do the download of your radio data
602
            """))
603
        rp.pre_upload = _(dedent("""\
604
            Follow this instructions to upload your info:
605

    
606
            1 - Turn off your radio
607
            2 - Connect your interface cable
608
            3 - Turn on your radio
609
            4 - Do the upload of your radio data
610
            """))
611
        return rp
612

    
613
    def get_features(self):
614
        rf = chirp_common.RadioFeatures()
615
        rf.has_settings = True
616
        rf.has_bank = False
617
        rf.has_tuning_step = False
618
        rf.can_odd_split = True
619
        rf.has_name = True
620
        rf.has_offset = True
621
        rf.has_mode = True
622
        rf.has_dtcs = True
623
        rf.has_rx_dtcs = True
624
        rf.has_dtcs_polarity = True
625
        rf.has_ctone = True
626
        rf.has_cross = True
627
        rf.has_sub_devices = self.VARIANT == ""
628
        rf.valid_modes = MODES
629
        rf.valid_characters = VALID_CHARS
630
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
631
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
632
        rf.valid_cross_modes = [
633
            "Tone->Tone",
634
            "DTCS->",
635
            "->DTCS",
636
            "Tone->DTCS",
637
            "DTCS->Tone",
638
            "->Tone",
639
            "DTCS->DTCS"]
640
        rf.valid_power_levels = POWER_LEVELS
641
        rf.valid_skips = SKIP_VALUES
642
        rf.valid_name_length = NAME_LENGTH
643
        rf.valid_dtcs_codes = DTCS_CODES
644
        rf.valid_bands = [self._air_range,
645
                          self._vhf_range,
646
                          self._vhf2_range,
647
                          self._220_range,
648
                          self._gen1_range,
649
                          self._uhf_range,
650
                          self._gen2_range]
651
        rf.memory_bounds = (0, self._upper)
652
        return rf
653

    
654
    def get_sub_devices(self):
655
        return [UV50X3Left(self._mmap), UV50X3Right(self._mmap)]
656

    
657
    def sync_in(self):
658
        """Download from radio"""
659
        try:
660
            data = _download(self)
661
        except errors.RadioError:
662
            # Pass through any real errors we raise
663
            raise
664
        except:
665
            # If anything unexpected happens, make sure we raise
666
            # a RadioError and log the problem
667
            LOG.exception('Unexpected error during download')
668
            raise errors.RadioError('Unexpected error communicating '
669
                                    'with the radio')
670
        self._mmap = memmap.MemoryMap(data)
671
        self.process_mmap()
672

    
673
    def sync_out(self):
674
        """Upload to radio"""
675
        try:
676
            _upload(self)
677
        except:
678
            # If anything unexpected happens, make sure we raise
679
            # a RadioError and log the problem
680
            LOG.exception('Unexpected error during upload')
681
            raise errors.RadioError('Unexpected error communicating '
682
                                    'with the radio')
683

    
684
    def process_mmap(self):
685
        """Process the mem map into the mem object"""
686
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
687

    
688
    def get_raw_memory(self, number):
689
        return repr(self._memobj.memory[number])
690

    
691
    def decode_tone(self, val):
692
        """Parse the tone data to decode from mem, it returns:
693
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
694
        if val.get_raw() == "\xFF\xFF":
695
            return '', None, None
696

    
697
        val = int(val)
698
        if val >= 12000:
699
            a = val - 12000
700
            return 'DTCS', a, 'R'
701
        elif val >= 8000:
702
            a = val - 8000
703
            return 'DTCS', a, 'N'
704
        else:
705
            a = val / 10.0
706
            return 'Tone', a, None
707

    
708
    def encode_tone(self, memval, mode, value, pol):
709
        """Parse the tone data to encode from UI to mem"""
710
        if mode == '':
711
            memval[0].set_raw(0xFF)
712
            memval[1].set_raw(0xFF)
713
        elif mode == 'Tone':
714
            memval.set_value(int(value * 10))
715
        elif mode == 'DTCS':
716
            flag = 0x80 if pol == 'N' else 0xC0
717
            memval.set_value(value)
718
            memval[1].set_bits(flag)
719
        else:
720
            raise Exception("Internal error: invalid mode `%s'" % mode)
721

    
722
    def _memory_obj(self, suffix=""):
723
        return getattr(self._memobj, "%s_memory%s" % (self._vfo, suffix))
724

    
725
    def _name_obj(self, suffix=""):
726
        return getattr(self._memobj, "%s_names%s" % (self._vfo, suffix))
727

    
728
    def _scan_obj(self, suffix=""):
729
        return getattr(self._memobj, "%s_scanflags%s" % (self._vfo, suffix))
730

    
731
    def _used_obj(self, suffix=""):
732
        return getattr(self._memobj, "%s_usedflags%s" % (self._vfo, suffix))
733

    
734
    def get_memory(self, number):
735
        """Get the mem representation from the radio image"""
736
        bitpos = (1 << (number % 8))
737
        bytepos = (number / 8)
738

    
739
        _mem = self._memory_obj()[number]
740
        _names = self._name_obj()[number]
741
        _scn = self._scan_obj()[bytepos]
742
        _usd = self._used_obj()[bytepos]
743

    
744
        isused = bitpos & int(_usd)
745
        isscan = bitpos & int(_scn)
746

    
747
        # Create a high-level memory object to return to the UI
748
        mem = chirp_common.Memory()
749

    
750
        # Memory number
751
        mem.number = number
752

    
753
        if not isused:
754
            mem.empty = True
755
            return mem
756

    
757
        # Freq and offset
758
        mem.freq = int(_mem.rxfreq) * 10
759
        # tx freq can be blank
760
        if _mem.get_raw()[4] == "\xFF":
761
            # TX freq not set
762
            mem.offset = 0
763
            mem.duplex = "off"
764
        else:
765
            # TX feq set
766
            offset = (int(_mem.txfreq) * 10) - mem.freq
767
            if offset < 0:
768
                mem.offset = abs(offset)
769
                mem.duplex = "-"
770
            elif offset > 0:
771
                mem.offset = offset
772
                mem.duplex = "+"
773
            else:
774
                mem.offset = 0
775

    
776
        # skip
777
        if not isscan:
778
            mem.skip = "S"
779

    
780
        # name TAG of the channel
781
        mem.name = str(_names.name).strip("\xFF")
782

    
783
        # power
784
        mem.power = POWER_LEVELS[int(_mem.txp)]
785

    
786
        # wide/narrow
787
        mem.mode = MODES[int(_mem.wn)]
788

    
789
        # tone data
790
        rxtone = txtone = None
791
        txtone = self.decode_tone(_mem.txtone)
792
        rxtone = self.decode_tone(_mem.rxtone)
793
        chirp_common.split_tone_decode(mem, txtone, rxtone)
794

    
795
        # Extra
796
        mem.extra = RadioSettingGroup("extra", "Extra")
797

    
798
        bcl = RadioSetting("bcl", "Busy channel lockout",
799
                              RadioSettingValueBoolean(bool(_mem.bcl)))
800
        mem.extra.append(bcl)
801

    
802
        revert = RadioSetting("revert", "Revert",
803
                              RadioSettingValueBoolean(bool(_mem.revert)))
804
        mem.extra.append(revert)
805

    
806
        dname = RadioSetting("dname", "Display name",
807
                             RadioSettingValueBoolean(bool(_mem.dname)))
808
        mem.extra.append(dname)
809

    
810
        return mem
811

    
812
    def set_memory(self, mem):
813
        """Set the memory data in the eeprom img from the UI"""
814
        bitpos = (1 << (mem.number % 8))
815
        bytepos = (mem.number / 8)
816

    
817
        _mem = self._memory_obj()[mem.number]
818
        _names = self._name_obj()[mem.number]
819
        _scn = self._scan_obj()[bytepos]
820
        _usd = self._used_obj()[bytepos]
821

    
822
        if mem.empty:
823
            _usd &= ~bitpos
824
            _scn &= ~bitpos
825
            _mem.set_raw("\xFF" * 16)
826
            _names.name = ("\xFF" * 6)
827
            return
828
        else:
829
            _usd |= bitpos
830

    
831
        # frequency
832
        _mem.rxfreq = mem.freq / 10
833

    
834
        # duplex
835
        if mem.duplex == "+":
836
            _mem.txfreq = (mem.freq + mem.offset) / 10
837
        elif mem.duplex == "-":
838
            _mem.txfreq = (mem.freq - mem.offset) / 10
839
        elif mem.duplex == "off":
840
            for i in _mem.txfreq:
841
                i.set_raw("\xFF")
842
        elif mem.duplex == "split":
843
            _mem.txfreq = mem.offset / 10
844
        else:
845
            _mem.txfreq = mem.freq / 10
846

    
847
        # tone data
848
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
849
            chirp_common.split_tone_encode(mem)
850
        self.encode_tone(_mem.txtone, txmode, txtone, txpol)
851
        self.encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
852

    
853
        # name TAG of the channel
854
        _names.name = mem.name.ljust(6, "\xFF")
855

    
856
        # power level, # default power level is low
857
        _mem.txp = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
858

    
859
        # wide/narrow
860
        _mem.wn = MODES.index(mem.mode)
861

    
862
        if mem.skip == "S":
863
            _scn &= ~bitpos
864
        else:
865
            _scn |= bitpos
866

    
867
        # autoset display to display name if filled
868
        if mem.extra:
869
            # mem.extra only seems to be populated when called from edit panel
870
            dname = mem.extra["dname"]
871
        else:
872
            dname = None
873
        if mem.name:
874
            _mem.dname = True
875
            if dname and not dname.changed():
876
                dname.value = True
877
        else:
878
            _mem.dname = False
879
            if dname and not dname.changed():
880
                dname.value = False
881

    
882
        # reseting unknowns, this has to be set by hand
883
        _mem.unknown0 = 0
884
        _mem.unknown1 = 0
885
        _mem.unknown2 = 0
886
        _mem.unknown3 = 0
887

    
888
        # extra settings
889
        if len(mem.extra) > 0:
890
            # there are setting, parse
891
            for setting in mem.extra:
892
                setattr(_mem, setting.get_name(), setting.value)
893
        else:
894
            # there are no extra settings, load defaults
895
            _mem.bcl = 0
896
            _mem.revert = 0
897
            _mem.dname = 1
898

    
899
    def _bbcd2dtmf(self, bcdarr, strlen=16):
900
        # doing bbcd, but with support for ABCD*#
901
        LOG.debug(bcdarr.get_value())
902
        string = ''.join("%02X" % b for b in bcdarr)
903
        LOG.debug("@_bbcd2dtmf, received: %s" % string)
904
        string = string.replace('E', '*').replace('F', '#')
905
        if strlen <= 16:
906
            string = string[:strlen]
907
        return string
908

    
909
    def _dtmf2bbcd(self, value):
910
        dtmfstr = value.get_value()
911
        dtmfstr = dtmfstr.replace('*', 'E').replace('#', 'F')
912
        dtmfstr = str.ljust(dtmfstr.strip(), 16, "F")
913
        bcdarr = list(bytearray.fromhex(dtmfstr))
914
        LOG.debug("@_dtmf2bbcd, sending: %s" % bcdarr)
915
        return bcdarr
916

    
917
    def get_settings(self):
918
        """Translate the bit in the mem_struct into settings in the UI"""
919
        _mem = self._memobj
920
        basic = RadioSettingGroup("basic", "Basic Settings")
921
        other = RadioSettingGroup("other", "Other Settings")
922
        work = RadioSettingGroup("work", "Work Mode Settings")
923
        dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
924
        top = RadioSettings(basic, other, work, dtmf)
925

    
926
        # Basic
927

    
928
        # Audio: A01-A04
929

    
930
        aftone = RadioSetting("settings.aftone", "AF tone control",
931
                              RadioSettingValueList(LIST_AFTONE, LIST_AFTONE[
932
                                  _mem.settings.aftone]))
933
        basic.append(aftone)
934

    
935
        spkr = RadioSetting("settings.spkr", "Speaker",
936
                            RadioSettingValueList(LIST_SPKR,LIST_SPKR[
937
                                _mem.settings.spkr]))
938
        basic.append(spkr)
939

    
940
        audio = RadioSetting("settings.audio", "Stereo/Mono",
941
                             RadioSettingValueList(LIST_AUDIO, LIST_AUDIO[
942
                                 _mem.settings.audio]))
943
        basic.append(audio)
944

    
945
        sbmute = RadioSetting("settings.sbmute", "Sub band mute",
946
                              RadioSettingValueList(LIST_SBMUTE, LIST_SBMUTE[
947
                                  _mem.settings.sbmute]))
948
        basic.append(sbmute)
949

    
950
        # TX/RX: B01-B08
951

    
952
        mgain = RadioSetting("settings.mgain", "Mic gain",
953
                             RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
954
                                 _mem.settings.mgain]))
955
        basic.append(mgain)
956

    
957
        ptt = RadioSetting("settings.ptt", "PTT mode",
958
                           RadioSettingValueList(LIST_PTT,LIST_PTT[
959
                               _mem.settings.ptt]))
960
        basic.append(ptt)
961

    
962
        # B03 (per channel)
963
        # B04 (per channel)
964

    
965
        rxexp = RadioSetting("settings.rxexp", "RX expansion",
966
                             RadioSettingValueList(LIST_RXEXP,LIST_RXEXP[
967
                                 _mem.settings.rxexp]))
968
        basic.append(rxexp)
969

    
970
        vox = RadioSetting("settings.vox", "Vox",
971
                           RadioSettingValueList(LIST_VOX, LIST_VOX[
972
                               _mem.settings.vox]))
973
        basic.append(vox)
974

    
975
        voxs = RadioSetting("settings.voxs", "Vox sensitivity",
976
                            RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
977
                                _mem.settings.voxs]))
978
        basic.append(voxs)
979

    
980
        # B08 (per channel)
981

    
982
        # Display: C01-C06
983

    
984
        display = RadioSetting("settings.display", "Display select",
985
                               RadioSettingValueList(LIST_DISPLAY,
986
                                   LIST_DISPLAY[_mem.settings.display]))
987
        basic.append(display)
988

    
989
        lcdb = RadioSetting("settings.lcdb", "LCD brightness",
990
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
991
                                _mem.settings.lcdb]))
992
        basic.append(lcdb)
993

    
994
        color = RadioSetting("settings.color", "LCD color",
995
                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
996
                                 _mem.settings.color]))
997
        basic.append(color)
998

    
999
        lcdc = RadioSetting("settings.lcdc", "LCD contrast",
1000
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
1001
                                _mem.settings.lcdc]))
1002
        basic.append(lcdc)
1003

    
1004
        btime = RadioSetting("settings.btime", "LCD backlight time",
1005
                             RadioSettingValueList(LIST_BTIME, LIST_BTIME[
1006
                                 _mem.settings.btime]))
1007
        basic.append(btime)
1008

    
1009
        keyb = RadioSetting("settings.keyb", "Key brightness",
1010
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
1011
                                _mem.settings.keyb]))
1012
        basic.append(keyb)
1013

    
1014
        # Memory: D01-D04
1015

    
1016
        # D01 (per channel)
1017
        # D02 (per channel)
1018

    
1019
        mrscan = RadioSetting("settings.mrscan", "Memory scan type",
1020
                              RadioSettingValueList(LIST_MRSCAN, LIST_MRSCAN[
1021
                                  _mem.settings.mrscan]))
1022
        basic.append(mrscan)
1023

    
1024
        # D04 (per channel)
1025

    
1026
        # Scan: E01-E04
1027

    
1028
        dwstop = RadioSetting("settings.dwstop", "Dual watch stop",
1029
                              RadioSettingValueList(LIST_DWSTOP, LIST_DWSTOP[
1030
                                  _mem.settings.dwstop]))
1031
        basic.append(dwstop)
1032

    
1033
        scand = RadioSetting("settings.scand", "Scan direction",
1034
                             RadioSettingValueList(LIST_SCAND,LIST_SCAND[
1035
                                 _mem.settings.scand]))
1036
        basic.append(scand)
1037

    
1038
        scanr = RadioSetting("settings.scanr", "Scan resume",
1039
                             RadioSettingValueList(LIST_SCANR,LIST_SCANR[
1040
                                 _mem.settings.scanr]))
1041
        basic.append(scanr)
1042

    
1043
        scansb = RadioSetting("settings.scansb", "Scan stop beep",
1044
                              RadioSettingValueBoolean(_mem.settings.scansb))
1045
        basic.append(scansb)
1046

    
1047
        # System: F01-F09
1048

    
1049
        apo = RadioSetting("settings.apo", "Automatic power off [hours]",
1050
                           RadioSettingValueList(LIST_APO, LIST_APO[
1051
                               _mem.settings.apo]))
1052
        basic.append(apo)
1053

    
1054
        ars = RadioSetting("settings.ars", "Automatic repeater shift",
1055
                           RadioSettingValueBoolean(_mem.settings.ars))
1056
        basic.append(ars)
1057

    
1058
        beep = RadioSetting("settings.beep", "Beep volume",
1059
                            RadioSettingValueList(LIST_BEEP,LIST_BEEP[
1060
                                _mem.settings.beep]))
1061
        basic.append(beep)
1062

    
1063
        fkey = RadioSetting("settings.fkey", "F key",
1064
                            RadioSettingValueList(LIST_FKEY,LIST_FKEY[
1065
                                _mem.settings.fkey]))
1066
        basic.append(fkey)
1067

    
1068
        pfkey1 = RadioSetting("settings.pfkey1", "Mic P1 key",
1069
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1070
                                  _mem.settings.pfkey1]))
1071
        basic.append(pfkey1)
1072

    
1073
        pfkey2 = RadioSetting("settings.pfkey2", "Mic P2 key",
1074
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1075
                                  _mem.settings.pfkey2]))
1076
        basic.append(pfkey2)
1077

    
1078
        pfkey3 = RadioSetting("settings.pfkey3", "Mic P3 key",
1079
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1080
                                  _mem.settings.pfkey3]))
1081
        basic.append(pfkey3)
1082

    
1083
        pfkey4 = RadioSetting("settings.pfkey4", "Mic P4 key",
1084
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1085
                                  _mem.settings.pfkey4]))
1086
        basic.append(pfkey4)
1087

    
1088
        omode = RadioSetting("settings.omode", "Operation mode",
1089
                             RadioSettingValueList(LIST_AB,LIST_AB[
1090
                                 _mem.settings.omode]))
1091
        basic.append(omode)
1092

    
1093
        rxcoverm = RadioSetting("settings.rxcoverm", "RX coverage - memory",
1094
                                RadioSettingValueList(LIST_COVERAGE, 
1095
                                    LIST_COVERAGE[_mem.settings.rxcoverm]))
1096
        basic.append(rxcoverm)
1097

    
1098
        rxcoverv = RadioSetting("settings.rxcoverv", "RX coverage - VFO",
1099
                                RadioSettingValueList(LIST_COVERAGE, 
1100
                                    LIST_COVERAGE[_mem.settings.rxcoverv]))
1101
        basic.append(rxcoverv)
1102

    
1103
        tot = RadioSetting("settings.tot", "Time out timer [min]",
1104
                           RadioSettingValueList(LIST_TOT, LIST_TOT[
1105
                               _mem.settings.tot]))
1106
        basic.append(tot)
1107

    
1108
        # Timer/Clock: G01-G04
1109

    
1110
        # G01
1111
        datefmt = RadioSetting("settings.datefmt", "Date format",
1112
                               RadioSettingValueList(LIST_DATEFMT,
1113
                                   LIST_DATEFMT[_mem.settings.datefmt]))
1114
        basic.append(datefmt)
1115

    
1116
        timefmt = RadioSetting("settings.timefmt", "Time format",
1117
                               RadioSettingValueList(LIST_TIMEFMT,
1118
                                   LIST_TIMEFMT[_mem.settings.timefmt]))
1119
        basic.append(timefmt)
1120

    
1121
        timesig = RadioSetting("settings.timesig", "Time signal",
1122
                               RadioSettingValueBoolean(_mem.settings.timesig))
1123
        basic.append(timesig)
1124

    
1125
        tz = RadioSetting("settings.tz", "Time zone",
1126
                          RadioSettingValueList(LIST_TZ, LIST_TZ[
1127
                              _mem.settings.tz]))
1128
        basic.append(tz)
1129

    
1130
        # Signaling: H01-H06
1131

    
1132
        bell = RadioSetting("settings.bell", "Bell ringer",
1133
                            RadioSettingValueList(LIST_BELL, LIST_BELL[
1134
                                _mem.settings.bell]))
1135
        basic.append(bell)
1136

    
1137
        # H02 (per channel)
1138

    
1139
        dtmfmodenc = RadioSetting("settings.dtmfmodenc", "DTMF mode encode",
1140
                                  RadioSettingValueBoolean(
1141
                                      _mem.settings.dtmfmodenc))
1142
        basic.append(dtmfmodenc)
1143

    
1144
        dtmfmoddec = RadioSetting("settings.dtmfmoddec", "DTMF mode decode",
1145
                                  RadioSettingValueBoolean(
1146
                                      _mem.settings.dtmfmoddec))
1147
        basic.append(dtmfmoddec)
1148

    
1149
        # H04 (per channel)
1150

    
1151
        decbandsel = RadioSetting("settings.decbandsel", "DTMF band select",
1152
                                  RadioSettingValueList(LIST_AB,LIST_AB[
1153
                                      _mem.settings.decbandsel]))
1154
        basic.append(decbandsel)
1155

    
1156
        sqlexp = RadioSetting("settings.sqlexp", "SQL expansion",
1157
                              RadioSettingValueBoolean(_mem.settings.sqlexp))
1158
        basic.append(sqlexp)
1159

    
1160
        # Pkt: I01-I03
1161

    
1162
        databnd = RadioSetting("settings.databnd", "Packet data band",
1163
                               RadioSettingValueList(LIST_DATABND,LIST_DATABND[
1164
                                   _mem.settings.databnd]))
1165
        basic.append(databnd)
1166

    
1167
        dataspd = RadioSetting("settings.dataspd", "Packet data speed",
1168
                               RadioSettingValueList(LIST_DATASPD,LIST_DATASPD[
1169
                                   _mem.settings.dataspd]))
1170
        basic.append(dataspd)
1171

    
1172
        datasql = RadioSetting("settings.datasql", "Packet data squelch",
1173
                               RadioSettingValueList(LIST_DATASQL,LIST_DATASQL[
1174
                                   _mem.settings.datasql]))
1175
        basic.append(datasql)
1176

    
1177
        # Other
1178

    
1179
        dw = RadioSetting("settings.dw", "Dual watch",
1180
                          RadioSettingValueBoolean(_mem.settings.dw))
1181
        other.append(dw)
1182

    
1183
        cpuclk = RadioSetting("settings.cpuclk", "CPU clock frequency",
1184
                              RadioSettingValueList(LIST_CPUCLK,LIST_CPUCLK[
1185
                                  _mem.settings.cpuclk]))
1186
        other.append(cpuclk)
1187

    
1188
        def _filter(name):
1189
            filtered = ""
1190
            for char in str(name):
1191
                if char in VALID_CHARS:
1192
                    filtered += char
1193
                else:
1194
                    filtered += " "
1195
            return filtered
1196

    
1197
        line16 = RadioSetting("poweron_msg.line16", "Power-on message",
1198
                              RadioSettingValueString(0, 16, _filter(
1199
                                  _mem.poweron_msg.line16)))
1200
        other.append(line16)
1201

    
1202
        line32 = RadioSetting("embedded_msg.line32", "Embedded message",
1203
                              RadioSettingValueString(0, 32, _filter(
1204
                                  _mem.embedded_msg.line32)))
1205
        other.append(line32)
1206

    
1207
        # Work
1208

    
1209
        workmoda = RadioSetting("settings.workmoda", "Work mode A",
1210
                                RadioSettingValueList(LIST_WORK,LIST_WORK[
1211
                                    _mem.settings.workmoda]))
1212
        work.append(workmoda)
1213

    
1214
        workmodb = RadioSetting("settings.workmodb", "Work mode B",
1215
                                RadioSettingValueList(LIST_WORK,LIST_WORK[
1216
                                    _mem.settings.workmodb]))
1217
        work.append(workmodb)
1218

    
1219
        wbanda = RadioSetting("settings.wbanda", "Work band A",
1220
                              RadioSettingValueList(LIST_WBANDA, LIST_WBANDA[
1221
                                  (_mem.settings.wbanda) - 1]))
1222
        work.append(wbanda)
1223

    
1224
        wbandb = RadioSetting("settings.wbandb", "Work band B",
1225
                              RadioSettingValueList(LIST_WBANDB, LIST_WBANDB[
1226
                                  (_mem.settings.wbandb) - 4]))
1227
        work.append(wbandb)
1228

    
1229
        sqla = RadioSetting("settings.sqla", "Squelch A",
1230
                            RadioSettingValueList(LIST_SQL, LIST_SQL[
1231
                                _mem.settings.sqla]))
1232
        work.append(sqla)
1233

    
1234
        sqlb = RadioSetting("settings.sqlb", "Squelch B",
1235
                            RadioSettingValueList(LIST_SQL, LIST_SQL[
1236
                                _mem.settings.sqlb]))
1237
        work.append(sqlb)
1238

    
1239
        stepa = RadioSetting("settings.stepa", "Auto step A",
1240
                             RadioSettingValueList(LIST_STEP,LIST_STEP[
1241
                                 _mem.settings.stepa]))
1242
        work.append(stepa)
1243

    
1244
        stepb = RadioSetting("settings.stepb", "Auto step B",
1245
                             RadioSettingValueList(LIST_STEP,LIST_STEP[
1246
                                 _mem.settings.stepb]))
1247
        work.append(stepb)
1248

    
1249
        mrcha = RadioSetting("settings.mrcha", "Current channel A",
1250
                             RadioSettingValueInteger(0, 499,
1251
                                 _mem.settings.mrcha))
1252
        work.append(mrcha)
1253

    
1254
        mrchb = RadioSetting("settings.mrchb", "Current channel B",
1255
                             RadioSettingValueInteger(0, 499,
1256
                                 _mem.settings.mrchb))
1257
        work.append(mrchb)
1258

    
1259
        val = _mem.settings.offseta / 100.00
1260
        offseta = RadioSetting("settings.offseta", "Offset A (0-37.95)",
1261
                               RadioSettingValueFloat(0, 38.00, val, 0.05, 2))
1262
        work.append(offseta)
1263

    
1264
        val = _mem.settings.offsetb / 100.00
1265
        offsetb = RadioSetting("settings.offsetb", "Offset B (0-79.95)",
1266
                               RadioSettingValueFloat(0, 80.00, val, 0.05, 2))
1267
        work.append(offsetb)
1268

    
1269
        wpricha = RadioSetting("settings.wpricha", "Priority channel A",
1270
                               RadioSettingValueInteger(0, 499,
1271
                                   _mem.settings.wpricha))
1272
        work.append(wpricha)
1273

    
1274
        wprichb = RadioSetting("settings.wprichb", "Priority channel B",
1275
                               RadioSettingValueInteger(0, 499,
1276
                                   _mem.settings.wprichb))
1277
        work.append(wprichb)
1278

    
1279
        smode = RadioSetting("settings.smode", "Smart function mode",
1280
                             RadioSettingValueList(LIST_SMODE,LIST_SMODE[
1281
                                 _mem.settings.smode]))
1282
        work.append(smode)
1283

    
1284
        # dtmf
1285

    
1286
        ttdkey = RadioSetting("dtmf.ttdkey", "D key function",
1287
                              RadioSettingValueList(LIST_TTDKEY, LIST_TTDKEY[
1288
                                  _mem.dtmf.ttdkey]))
1289
        dtmf.append(ttdkey)
1290

    
1291
        ttdgt = RadioSetting("dtmf.ttdgt", "Digit time",
1292
                              RadioSettingValueList(LIST_TT200, LIST_TT200[
1293
                                  (_mem.dtmf.ttdgt) - 5]))
1294
        dtmf.append(ttdgt)
1295

    
1296
        ttint = RadioSetting("dtmf.ttint", "Interval time",
1297
                              RadioSettingValueList(LIST_TT200, LIST_TT200[
1298
                                  (_mem.dtmf.ttint) - 5]))
1299
        dtmf.append(ttint)
1300

    
1301
        tt1stdgt = RadioSetting("dtmf.tt1stdgt", "1st digit time",
1302
                                RadioSettingValueList(LIST_TT200, LIST_TT200[
1303
                                    (_mem.dtmf.tt1stdgt) - 5]))
1304
        dtmf.append(tt1stdgt)
1305

    
1306
        tt1stdly = RadioSetting("dtmf.tt1stdly", "1st digit delay time",
1307
                                RadioSettingValueList(LIST_TT1000, LIST_TT1000[
1308
                                    (_mem.dtmf.tt1stdly) - 2]))
1309
        dtmf.append(tt1stdly)
1310

    
1311
        ttdlyqt = RadioSetting("dtmf.ttdlyqt", "Digit delay when use qt",
1312
                               RadioSettingValueList(LIST_TT1000, LIST_TT1000[
1313
                                   (_mem.dtmf.ttdlyqt) - 2]))
1314
        dtmf.append(ttdlyqt)
1315

    
1316
        ttsig = RadioSetting("dtmf2.ttsig", "Signal",
1317
                             RadioSettingValueList(LIST_TTSIG, LIST_TTSIG[
1318
                                 _mem.dtmf2.ttsig]))
1319
        dtmf.append(ttsig)
1320

    
1321
        ttautorst = RadioSetting("dtmf2.ttautorst", "Auto reset time",
1322
                                 RadioSettingValueList(LIST_TTAUTORST,
1323
                                     LIST_TTAUTORST[_mem.dtmf2.ttautorst]))
1324
        dtmf.append(ttautorst)
1325

    
1326
        if _mem.dtmf2.ttgrpcode > 0x06:
1327
            val = 0x00
1328
        else:
1329
            val = _mem.dtmf2.ttgrpcode
1330
        ttgrpcode = RadioSetting("dtmf2.ttgrpcode", "Group code",
1331
                                 RadioSettingValueList(LIST_TTGRPCODE,
1332
                                     LIST_TTGRPCODE[val]))
1333
        dtmf.append(ttgrpcode)
1334

    
1335
        ttintcode = RadioSetting("dtmf2.ttintcode", "Interval code",
1336
                                 RadioSettingValueList(LIST_TTINTCODE,
1337
                                     LIST_TTINTCODE[_mem.dtmf2.ttintcode]))
1338
        dtmf.append(ttintcode)
1339

    
1340
        if _mem.dtmf2.ttalert > 0x04:
1341
            val = 0x00
1342
        else:
1343
            val = _mem.dtmf2.ttalert
1344
        ttalert = RadioSetting("dtmf2.ttalert", "Alert tone/transpond",
1345
                               RadioSettingValueList(LIST_TTALERT,
1346
                                   LIST_TTALERT[val]))
1347
        dtmf.append(ttalert)
1348

    
1349
        ttautod = RadioSetting("dtmf.ttautod", "Auto dial group",
1350
                               RadioSettingValueList(LIST_TTAUTOD,
1351
                                   LIST_TTAUTOD[_mem.dtmf.ttautod]))
1352
        dtmf.append(ttautod)
1353

    
1354
        # setup 9 dtmf autodial entries
1355
        for i in map(str, range(1, 10)):
1356
            objname = "code" + i
1357
            strname = "Code " + str(i)
1358
            dtmfsetting = getattr(_mem.dtmfcode, objname)
1359
            dtmflen = getattr(_mem.dtmfcode, objname + "_len")
1360
            dtmfstr = self._bbcd2dtmf(dtmfsetting, dtmflen)
1361
            code = RadioSettingValueString(0, 16, dtmfstr)
1362
            code.set_charset(DTMF_CHARS + list(" "))
1363
            rs = RadioSetting("dtmfcode." + objname, strname, code)
1364
            dtmf.append(rs)
1365
        return top
1366

    
1367
    def set_settings(self, settings):
1368
        _settings = self._memobj.settings
1369
        _mem = self._memobj
1370
        for element in settings:
1371
            if not isinstance(element, RadioSetting):
1372
                self.set_settings(element)
1373
                continue
1374
            else:
1375
                try:
1376
                    name = element.get_name()
1377
                    if "." in name:
1378
                        bits = name.split(".")
1379
                        obj = self._memobj
1380
                        for bit in bits[:-1]:
1381
                            if "/" in bit:
1382
                                bit, index = bit.split("/", 1)
1383
                                index = int(index)
1384
                                obj = getattr(obj, bit)[index]
1385
                            else:
1386
                                obj = getattr(obj, bit)
1387
                        setting = bits[-1]
1388
                    else:
1389
                        obj = _settings
1390
                        setting = element.get_name()
1391

    
1392
                    if element.has_apply_callback():
1393
                        LOG.debug("Using apply callback")
1394
                        element.run_apply_callback()
1395
                    elif setting == "line16":
1396
                        setattr(obj, setting, str(element.value).rstrip(
1397
                            " ").ljust(16, "\xFF"))
1398
                    elif setting == "line32":
1399
                        setattr(obj, setting, str(element.value).rstrip(
1400
                            " ").ljust(32, "\xFF"))
1401
                    elif setting == "wbanda":
1402
                        setattr(obj, setting, int(element.value) + 1)
1403
                    elif setting == "wbandb":
1404
                        setattr(obj, setting, int(element.value) + 4)
1405
                    elif setting in ["offseta", "offsetb"]:
1406
                        val = element.value
1407
                        value = int(val.get_value() * 100)
1408
                        setattr(obj, setting, value)
1409
                    elif setting in ["ttdgt", "ttint", "tt1stdgt"]:
1410
                        setattr(obj, setting, int(element.value) + 5)
1411
                    elif setting in ["tt1stdly", "ttdlyqt"]:
1412
                        setattr(obj, setting, int(element.value) + 2)
1413
                    elif re.match('code\d', setting):
1414
                        # set dtmf length field and then get bcd dtmf
1415
                        dtmfstrlen = len(str(element.value).strip())
1416
                        setattr(_mem.dtmfcode, setting + "_len", dtmfstrlen)
1417
                        dtmfstr = self._dtmf2bbcd(element.value)
1418
                        setattr(_mem.dtmfcode, setting, dtmfstr)
1419
                    elif element.value.get_mutable():
1420
                        LOG.debug("Setting %s = %s" % (setting, element.value))
1421
                        setattr(obj, setting, element.value)
1422
                except Exception, e:
1423
                    LOG.debug(element.get_name())
1424
                    raise
1425

    
1426

    
1427
    @classmethod
1428
    def match_model(cls, filedata, filename):
1429
        match_size = False
1430
        match_model = False
1431

    
1432
        # testing the file data size
1433
        if len(filedata) == MEM_SIZE:
1434
            match_size = True
1435

    
1436
        # testing the firmware model fingerprint
1437
        match_model = model_match(cls, filedata)
1438

    
1439
        if match_size and match_model:
1440
            return True
1441
        else:
1442
            return False
1443

    
1444

    
1445
@directory.register
1446
class UV50X3(VGCStyleRadio):
1447
    """BTech UV-50X3"""
1448
    VENDOR = "BTECH"
1449
    MODEL = "UV-50X3"
1450
    IDENT = UV50X3_id
1451

    
1452

    
1453
class UV50X3Left(UV50X3):
1454
    VARIANT = "Left"
1455
    _vfo = "left"
1456

    
1457

    
1458
class UV50X3Right(UV50X3):
1459
    VARIANT = "Right"
1460
    _vfo = "right"
1461

    
1462
@directory.register
1463
class VR6600PRO(VGCStyleRadio):
1464
    """VGC VR-6600PRO"""
1465
    VENDOR = "VGC"
1466
    MODEL = "VR-6600PRO"
1467
    IDENT = VR6600PRO_id
1468

    
1469

    
1470
class VR6600PROLeft(UV50X3):
1471
    VARIANT = "Left"
1472
    _vfo = "left"
1473

    
1474

    
1475
class VR6600PRORight(UV50X3):
1476
    VARIANT = "Right"
1477
    _vfo = "right"