Project

General

Profile

New Model #4263 » vgc_sleep.py

some added delay between downloaded blocks - Jim Unroe, 11/26/2016 06:37 AM

 
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

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

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

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

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

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

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

    
347
# B-TECH UV-50X3 id string
348
UV50X3_id  = "VGC6600MD"
349

    
350

    
351
def _clean_buffer(radio):
352
    radio.pipe.timeout = 0.005
353
    junk = radio.pipe.read(256)
354
    radio.pipe.timeout = STIMEOUT
355
    if junk:
356
        Log.debug("Got %i bytes of junk before starting" % len(junk))
357

    
358

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

    
367

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

    
378
    if len(data) != amount:
379
        _exit_program_mode(radio)
380
        msg = "Error reading data from radio: not the amount of data we want."
381
        raise errors.RadioError(msg)
382

    
383
    return data
384

    
385

    
386
def _rawsend(radio, data):
387
    """Raw send to the radio device"""
388
    try:
389
        radio.pipe.write(data)
390
    except:
391
        raise errors.RadioError("Error sending data to radio")
392

    
393

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

    
403

    
404
def _recv(radio, addr, length=BLOCK_SIZE):
405
    """Get data from the radio """
406
    # read 4 bytes of header
407
    hdr = _rawrecv(radio, 4)
408

    
409
    # check for unexpected extra command byte
410
    c, a, l = struct.unpack(">BHB", hdr)
411
    if hdr[0:2] == "WW" and a != addr:
412
        # extra command byte detected
413
        # throw away the 1st byte and add the next byte in the buffer
414
        hdr = hdr[1:] + _rawrecv(radio, 1)
415

    
416
    # read 64 bytes (0x40) of data
417
    data = _rawrecv(radio, (BLOCK_SIZE))
418

    
419
    # DEBUG
420
    LOG.info("Response:")
421
    LOG.debug(util.hexprint(hdr + data))
422

    
423
    c, a, l = struct.unpack(">BHB", hdr)
424
    if a != addr or l != length or c != ord("W"):
425
        _exit_program_mode(radio)
426
        LOG.error("Invalid answer for block 0x%04x:" % addr)
427
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
428
        raise errors.RadioError("Unknown response from the radio")
429

    
430
    return data
431

    
432

    
433
def _do_ident(radio):
434
    """Put the radio in PROGRAM mode & identify it"""
435
    #  set the serial discipline
436
    radio.pipe.baudrate = 115200
437
    radio.pipe.parity = "N"
438
    radio.pipe.timeout = STIMEOUT
439

    
440
    # flush input buffer
441
    _clean_buffer(radio)
442

    
443
    magic = "V66LINK"
444

    
445
    _rawsend(radio, magic)
446

    
447
    # Ok, get the ident string
448
    ident = _rawrecv(radio, 9)
449

    
450
    # check if ident is OK
451
    if ident != radio.IDENT:
452
        # bad ident
453
        msg = "Incorrect model ID, got this:"
454
        msg +=  util.hexprint(ident)
455
        LOG.debug(msg)
456
        raise errors.RadioError("Radio identification failed.")
457

    
458
    # DEBUG
459
    LOG.info("Positive ident, got this:")
460
    LOG.debug(util.hexprint(ident))
461

    
462
    return True
463

    
464

    
465
def _exit_program_mode(radio):
466
    endframe = "\x45"
467
    _rawsend(radio, endframe)
468

    
469

    
470
def _download(radio):
471
    """Get the memory map"""
472

    
473
    # put radio in program mode and identify it
474
    _do_ident(radio)
475

    
476
    # UI progress
477
    status = chirp_common.Status()
478
    status.cur = 0
479
    status.max = MEM_SIZE / BLOCK_SIZE
480
    status.msg = "Cloning from radio..."
481
    radio.status_fn(status)
482

    
483
    data = ""
484
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
485
        frame = _make_frame("R", addr, BLOCK_SIZE)
486
        # DEBUG
487
        LOG.info("Request sent:")
488
        LOG.debug(util.hexprint(frame))
489

    
490
        # sending the read request
491
        _rawsend(radio, frame)
492

    
493
        # now we read
494
        d = _recv(radio, addr)
495
        time.sleep(0.1)
496

    
497
        # aggregate the data
498
        data += d
499

    
500
        # UI Update
501
        status.cur = addr / BLOCK_SIZE
502
        status.msg = "Cloning from radio..."
503
        radio.status_fn(status)
504

    
505
    _exit_program_mode(radio)
506

    
507
    return data
508

    
509

    
510
def _upload(radio):
511
    """Upload procedure"""
512

    
513
    MEM_SIZE = 0x7000
514

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

    
518
    # UI progress
519
    status = chirp_common.Status()
520
    status.cur = 0
521
    status.max = MEM_SIZE / BLOCK_SIZE
522
    status.msg = "Cloning to radio..."
523
    radio.status_fn(status)
524

    
525
    # the fun start here
526
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
527
        # sending the data
528
        data = radio.get_mmap()[addr:addr + BLOCK_SIZE]
529

    
530
        frame = _make_frame("W", addr, BLOCK_SIZE, data)
531

    
532
        _rawsend(radio, frame)
533

    
534
        # receiving the response
535
        ack = _rawrecv(radio, 1)
536
        if ack != "\x06":
537
            _exit_program_mode(radio)
538
            msg = "Bad ack writing block 0x%04x" % addr
539
            raise errors.RadioError(msg)
540

    
541
        _check_for_double_ack(radio)
542

    
543
        # UI Update
544
        status.cur = addr / BLOCK_SIZE
545
        status.msg = "Cloning to radio..."
546
        radio.status_fn(status)
547

    
548
    _exit_program_mode(radio)
549

    
550

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

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

    
559
    return False
560

    
561

    
562
class VGCStyleRadio(chirp_common.CloneModeRadio,
563
                    chirp_common.ExperimentalRadio):
564
    """BTECH's UV-50X3"""
565
    VENDOR = "BTECH"
566
    _air_range = (108000000, 136000000)
567
    _vhf_range = (136000000, 174000000)
568
    _vhf2_range = (174000000, 250000000)
569
    _220_range = (222000000, 225000000)
570
    _gen1_range = (300000000, 400000000)
571
    _uhf_range = (400000000, 480000000)
572
    _gen2_range = (480000000, 520000000)
573
    _upper = 499
574
    MODEL = ""
575
    IDENT = ""
576

    
577
    @classmethod
578
    def get_prompts(cls):
579
        rp = chirp_common.RadioPrompts()
580
        rp.experimental = \
581
            ('The UV-50X3 driver is a beta version.\n'
582
             '\n'
583
             'Please save an unedited copy of your first successful\n'
584
             'download to a CHIRP Radio Images(*.img) file.'
585
             )
586
        rp.pre_download = _(dedent("""\
587
            Follow this instructions to download your info:
588

    
589
            1 - Turn off your radio
590
            2 - Connect your interface cable
591
            3 - Turn on your radio
592
            4 - Do the download of your radio data
593
            """))
594
        rp.pre_upload = _(dedent("""\
595
            Follow this instructions to upload your info:
596

    
597
            1 - Turn off your radio
598
            2 - Connect your interface cable
599
            3 - Turn on your radio
600
            4 - Do the upload of your radio data
601
            """))
602
        return rp
603

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

    
645
    def get_sub_devices(self):
646
        return [UV50X3Left(self._mmap), UV50X3Right(self._mmap)]
647

    
648
    def sync_in(self):
649
        """Download from radio"""
650
        try:
651
            data = _download(self)
652
        except errors.RadioError:
653
            # Pass through any real errors we raise
654
            raise
655
        except:
656
            # If anything unexpected happens, make sure we raise
657
            # a RadioError and log the problem
658
            LOG.exception('Unexpected error during download')
659
            raise errors.RadioError('Unexpected error communicating '
660
                                    'with the radio')
661
        self._mmap = memmap.MemoryMap(data)
662
        self.process_mmap()
663

    
664
    def sync_out(self):
665
        """Upload to radio"""
666
        try:
667
            _upload(self)
668
        except:
669
            # If anything unexpected happens, make sure we raise
670
            # a RadioError and log the problem
671
            LOG.exception('Unexpected error during upload')
672
            raise errors.RadioError('Unexpected error communicating '
673
                                    'with the radio')
674

    
675
    def process_mmap(self):
676
        """Process the mem map into the mem object"""
677
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
678

    
679
    def get_raw_memory(self, number):
680
        return repr(self._memobj.memory[number])
681

    
682
    def decode_tone(self, val):
683
        """Parse the tone data to decode from mem, it returns:
684
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
685
        if val.get_raw() == "\xFF\xFF":
686
            return '', None, None
687

    
688
        val = int(val)
689
        if val >= 12000:
690
            a = val - 12000
691
            return 'DTCS', a, 'R'
692
        elif val >= 8000:
693
            a = val - 8000
694
            return 'DTCS', a, 'N'
695
        else:
696
            a = val / 10.0
697
            return 'Tone', a, None
698

    
699
    def encode_tone(self, memval, mode, value, pol):
700
        """Parse the tone data to encode from UI to mem"""
701
        if mode == '':
702
            memval[0].set_raw(0xFF)
703
            memval[1].set_raw(0xFF)
704
        elif mode == 'Tone':
705
            memval.set_value(int(value * 10))
706
        elif mode == 'DTCS':
707
            flag = 0x80 if pol == 'N' else 0xC0
708
            memval.set_value(value)
709
            memval[1].set_bits(flag)
710
        else:
711
            raise Exception("Internal error: invalid mode `%s'" % mode)
712

    
713
    def _memory_obj(self, suffix=""):
714
        return getattr(self._memobj, "%s_memory%s" % (self._vfo, suffix))
715

    
716
    def _name_obj(self, suffix=""):
717
        return getattr(self._memobj, "%s_names%s" % (self._vfo, suffix))
718

    
719
    def _scan_obj(self, suffix=""):
720
        return getattr(self._memobj, "%s_scanflags%s" % (self._vfo, suffix))
721

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

    
725
    def get_memory(self, number):
726
        """Get the mem representation from the radio image"""
727
        bitpos = (1 << (number % 8))
728
        bytepos = (number / 8)
729

    
730
        _mem = self._memory_obj()[number]
731
        _names = self._name_obj()[number]
732
        _scn = self._scan_obj()[bytepos]
733
        _usd = self._used_obj()[bytepos]
734

    
735
        isused = bitpos & int(_usd)
736
        isscan = bitpos & int(_scn)
737

    
738
        # Create a high-level memory object to return to the UI
739
        mem = chirp_common.Memory()
740

    
741
        # Memory number
742
        mem.number = number
743

    
744
        if not isused:
745
            mem.empty = True
746
            return mem
747

    
748
        # Freq and offset
749
        mem.freq = int(_mem.rxfreq) * 10
750
        # tx freq can be blank
751
        if _mem.get_raw()[4] == "\xFF":
752
            # TX freq not set
753
            mem.offset = 0
754
            mem.duplex = "off"
755
        else:
756
            # TX feq set
757
            offset = (int(_mem.txfreq) * 10) - mem.freq
758
            if offset < 0:
759
                mem.offset = abs(offset)
760
                mem.duplex = "-"
761
            elif offset > 0:
762
                mem.offset = offset
763
                mem.duplex = "+"
764
            else:
765
                mem.offset = 0
766

    
767
        # skip
768
        if not isscan:
769
            mem.skip = "S"
770

    
771
        # name TAG of the channel
772
        mem.name = str(_names.name).strip("\xFF")
773

    
774
        # power
775
        mem.power = POWER_LEVELS[int(_mem.txp)]
776

    
777
        # wide/narrow
778
        mem.mode = MODES[int(_mem.wn)]
779

    
780
        # tone data
781
        rxtone = txtone = None
782
        txtone = self.decode_tone(_mem.txtone)
783
        rxtone = self.decode_tone(_mem.rxtone)
784
        chirp_common.split_tone_decode(mem, txtone, rxtone)
785

    
786
        # Extra
787
        mem.extra = RadioSettingGroup("extra", "Extra")
788

    
789
        bcl = RadioSetting("bcl", "Busy channel lockout",
790
                              RadioSettingValueBoolean(bool(_mem.bcl)))
791
        mem.extra.append(bcl)
792

    
793
        revert = RadioSetting("revert", "Revert",
794
                              RadioSettingValueBoolean(bool(_mem.revert)))
795
        mem.extra.append(revert)
796

    
797
        dname = RadioSetting("dname", "Display name",
798
                             RadioSettingValueBoolean(bool(_mem.dname)))
799
        mem.extra.append(dname)
800

    
801
        return mem
802

    
803
    def set_memory(self, mem):
804
        """Set the memory data in the eeprom img from the UI"""
805
        bitpos = (1 << (mem.number % 8))
806
        bytepos = (mem.number / 8)
807

    
808
        _mem = self._memory_obj()[mem.number]
809
        _names = self._name_obj()[mem.number]
810
        _scn = self._scan_obj()[bytepos]
811
        _usd = self._used_obj()[bytepos]
812

    
813
        if mem.empty:
814
            _usd &= ~bitpos
815
            _scn &= ~bitpos
816
            _mem.set_raw("\xFF" * 16)
817
            _names.name = ("\xFF" * 6)
818
            return
819
        else:
820
            _usd |= bitpos
821

    
822
        # frequency
823
        _mem.rxfreq = mem.freq / 10
824

    
825
        # duplex
826
        if mem.duplex == "+":
827
            _mem.txfreq = (mem.freq + mem.offset) / 10
828
        elif mem.duplex == "-":
829
            _mem.txfreq = (mem.freq - mem.offset) / 10
830
        elif mem.duplex == "off":
831
            for i in _mem.txfreq:
832
                i.set_raw("\xFF")
833
        elif mem.duplex == "split":
834
            _mem.txfreq = mem.offset / 10
835
        else:
836
            _mem.txfreq = mem.freq / 10
837

    
838
        # tone data
839
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
840
            chirp_common.split_tone_encode(mem)
841
        self.encode_tone(_mem.txtone, txmode, txtone, txpol)
842
        self.encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
843

    
844
        # name TAG of the channel
845
        _names.name = mem.name.ljust(6, "\xFF")
846

    
847
        # power level, # default power level is low
848
        _mem.txp = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
849

    
850
        # wide/narrow
851
        _mem.wn = MODES.index(mem.mode)
852

    
853
        if mem.skip == "S":
854
            _scn &= ~bitpos
855
        else:
856
            _scn |= bitpos
857

    
858
        # autoset display to display name if filled
859
        if mem.extra:
860
            # mem.extra only seems to be populated when called from edit panel
861
            dname = mem.extra["dname"]
862
        else:
863
            dname = None
864
        if mem.name:
865
            _mem.dname = True
866
            if dname and not dname.changed():
867
                dname.value = True
868
        else:
869
            _mem.dname = False
870
            if dname and not dname.changed():
871
                dname.value = False
872

    
873
        # reseting unknowns, this has to be set by hand
874
        _mem.unknown0 = 0
875
        _mem.unknown1 = 0
876
        _mem.unknown2 = 0
877
        _mem.unknown3 = 0
878

    
879
        # extra settings
880
        if len(mem.extra) > 0:
881
            # there are setting, parse
882
            for setting in mem.extra:
883
                setattr(_mem, setting.get_name(), setting.value)
884
        else:
885
            # there are no extra settings, load defaults
886
            _mem.bcl = 0
887
            _mem.revert = 0
888
            _mem.dname = 1
889

    
890
    def _bbcd2dtmf(self, bcdarr, strlen=16):
891
        # doing bbcd, but with support for ABCD*#
892
        LOG.debug(bcdarr.get_value())
893
        string = ''.join("%02X" % b for b in bcdarr)
894
        LOG.debug("@_bbcd2dtmf, received: %s" % string)
895
        string = string.replace('E', '*').replace('F', '#')
896
        if strlen <= 16:
897
            string = string[:strlen]
898
        return string
899

    
900
    def _dtmf2bbcd(self, value):
901
        dtmfstr = value.get_value()
902
        dtmfstr = dtmfstr.replace('*', 'E').replace('#', 'F')
903
        dtmfstr = str.ljust(dtmfstr.strip(), 16, "F")
904
        bcdarr = list(bytearray.fromhex(dtmfstr))
905
        LOG.debug("@_dtmf2bbcd, sending: %s" % bcdarr)
906
        return bcdarr
907

    
908
    def get_settings(self):
909
        """Translate the bit in the mem_struct into settings in the UI"""
910
        _mem = self._memobj
911
        basic = RadioSettingGroup("basic", "Basic Settings")
912
        other = RadioSettingGroup("other", "Other Settings")
913
        work = RadioSettingGroup("work", "Work Mode Settings")
914
        dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
915
        top = RadioSettings(basic, other, work, dtmf)
916

    
917
        # Basic
918

    
919
        # Audio: A01-A04
920

    
921
        aftone = RadioSetting("settings.aftone", "AF tone control",
922
                              RadioSettingValueList(LIST_AFTONE, LIST_AFTONE[
923
                                  _mem.settings.aftone]))
924
        basic.append(aftone)
925

    
926
        spkr = RadioSetting("settings.spkr", "Speaker",
927
                            RadioSettingValueList(LIST_SPKR,LIST_SPKR[
928
                                _mem.settings.spkr]))
929
        basic.append(spkr)
930

    
931
        audio = RadioSetting("settings.audio", "Stereo/Mono",
932
                             RadioSettingValueList(LIST_AUDIO, LIST_AUDIO[
933
                                 _mem.settings.audio]))
934
        basic.append(audio)
935

    
936
        sbmute = RadioSetting("settings.sbmute", "Sub band mute",
937
                              RadioSettingValueList(LIST_SBMUTE, LIST_SBMUTE[
938
                                  _mem.settings.sbmute]))
939
        basic.append(sbmute)
940

    
941
        # TX/RX: B01-B08
942

    
943
        mgain = RadioSetting("settings.mgain", "Mic gain",
944
                             RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
945
                                 _mem.settings.mgain]))
946
        basic.append(mgain)
947

    
948
        ptt = RadioSetting("settings.ptt", "PTT mode",
949
                           RadioSettingValueList(LIST_PTT,LIST_PTT[
950
                               _mem.settings.ptt]))
951
        basic.append(ptt)
952

    
953
        # B03 (per channel)
954
        # B04 (per channel)
955

    
956
        rxexp = RadioSetting("settings.rxexp", "RX expansion",
957
                             RadioSettingValueList(LIST_RXEXP,LIST_RXEXP[
958
                                 _mem.settings.rxexp]))
959
        basic.append(rxexp)
960

    
961
        vox = RadioSetting("settings.vox", "Vox",
962
                           RadioSettingValueList(LIST_VOX, LIST_VOX[
963
                               _mem.settings.vox]))
964
        basic.append(vox)
965

    
966
        voxs = RadioSetting("settings.voxs", "Vox sensitivity",
967
                            RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
968
                                _mem.settings.voxs]))
969
        basic.append(voxs)
970

    
971
        # B08 (per channel)
972

    
973
        # Display: C01-C06
974

    
975
        display = RadioSetting("settings.display", "Display select",
976
                               RadioSettingValueList(LIST_DISPLAY,
977
                                   LIST_DISPLAY[_mem.settings.display]))
978
        basic.append(display)
979

    
980
        lcdb = RadioSetting("settings.lcdb", "LCD brightness",
981
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
982
                                _mem.settings.lcdb]))
983
        basic.append(lcdb)
984

    
985
        color = RadioSetting("settings.color", "LCD color",
986
                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
987
                                 _mem.settings.color]))
988
        basic.append(color)
989

    
990
        lcdc = RadioSetting("settings.lcdc", "LCD contrast",
991
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
992
                                _mem.settings.lcdc]))
993
        basic.append(lcdc)
994

    
995
        btime = RadioSetting("settings.btime", "LCD backlight time",
996
                             RadioSettingValueList(LIST_BTIME, LIST_BTIME[
997
                                 _mem.settings.btime]))
998
        basic.append(btime)
999

    
1000
        keyb = RadioSetting("settings.keyb", "Key brightness",
1001
                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
1002
                                _mem.settings.keyb]))
1003
        basic.append(keyb)
1004

    
1005
        # Memory: D01-D04
1006

    
1007
        # D01 (per channel)
1008
        # D02 (per channel)
1009

    
1010
        mrscan = RadioSetting("settings.mrscan", "Memory scan type",
1011
                              RadioSettingValueList(LIST_MRSCAN, LIST_MRSCAN[
1012
                                  _mem.settings.mrscan]))
1013
        basic.append(mrscan)
1014

    
1015
        # D04 (per channel)
1016

    
1017
        # Scan: E01-E04
1018

    
1019
        dwstop = RadioSetting("settings.dwstop", "Dual watch stop",
1020
                              RadioSettingValueList(LIST_DWSTOP, LIST_DWSTOP[
1021
                                  _mem.settings.dwstop]))
1022
        basic.append(dwstop)
1023

    
1024
        scand = RadioSetting("settings.scand", "Scan direction",
1025
                             RadioSettingValueList(LIST_SCAND,LIST_SCAND[
1026
                                 _mem.settings.scand]))
1027
        basic.append(scand)
1028

    
1029
        scanr = RadioSetting("settings.scanr", "Scan resume",
1030
                             RadioSettingValueList(LIST_SCANR,LIST_SCANR[
1031
                                 _mem.settings.scanr]))
1032
        basic.append(scanr)
1033

    
1034
        scansb = RadioSetting("settings.scansb", "Scan stop beep",
1035
                              RadioSettingValueBoolean(_mem.settings.scansb))
1036
        basic.append(scansb)
1037

    
1038
        # System: F01-F09
1039

    
1040
        apo = RadioSetting("settings.apo", "Automatic power off [hours]",
1041
                           RadioSettingValueList(LIST_APO, LIST_APO[
1042
                               _mem.settings.apo]))
1043
        basic.append(apo)
1044

    
1045
        ars = RadioSetting("settings.ars", "Automatic repeater shift",
1046
                           RadioSettingValueBoolean(_mem.settings.ars))
1047
        basic.append(ars)
1048

    
1049
        beep = RadioSetting("settings.beep", "Beep volume",
1050
                            RadioSettingValueList(LIST_BEEP,LIST_BEEP[
1051
                                _mem.settings.beep]))
1052
        basic.append(beep)
1053

    
1054
        fkey = RadioSetting("settings.fkey", "F key",
1055
                            RadioSettingValueList(LIST_FKEY,LIST_FKEY[
1056
                                _mem.settings.fkey]))
1057
        basic.append(fkey)
1058

    
1059
        pfkey1 = RadioSetting("settings.pfkey1", "Mic P1 key",
1060
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1061
                                  _mem.settings.pfkey1]))
1062
        basic.append(pfkey1)
1063

    
1064
        pfkey2 = RadioSetting("settings.pfkey2", "Mic P2 key",
1065
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1066
                                  _mem.settings.pfkey2]))
1067
        basic.append(pfkey2)
1068

    
1069
        pfkey3 = RadioSetting("settings.pfkey3", "Mic P3 key",
1070
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1071
                                  _mem.settings.pfkey3]))
1072
        basic.append(pfkey3)
1073

    
1074
        pfkey4 = RadioSetting("settings.pfkey4", "Mic P4 key",
1075
                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
1076
                                  _mem.settings.pfkey4]))
1077
        basic.append(pfkey4)
1078

    
1079
        omode = RadioSetting("settings.omode", "Operation mode",
1080
                             RadioSettingValueList(LIST_AB,LIST_AB[
1081
                                 _mem.settings.omode]))
1082
        basic.append(omode)
1083

    
1084
        rxcoverm = RadioSetting("settings.rxcoverm", "RX coverage - memory",
1085
                                RadioSettingValueList(LIST_COVERAGE, 
1086
                                    LIST_COVERAGE[_mem.settings.rxcoverm]))
1087
        basic.append(rxcoverm)
1088

    
1089
        rxcoverv = RadioSetting("settings.rxcoverv", "RX coverage - VFO",
1090
                                RadioSettingValueList(LIST_COVERAGE, 
1091
                                    LIST_COVERAGE[_mem.settings.rxcoverv]))
1092
        basic.append(rxcoverv)
1093

    
1094
        tot = RadioSetting("settings.tot", "Time out timer [min]",
1095
                           RadioSettingValueList(LIST_TOT, LIST_TOT[
1096
                               _mem.settings.tot]))
1097
        basic.append(tot)
1098

    
1099
        # Timer/Clock: G01-G04
1100

    
1101
        # G01
1102
        datefmt = RadioSetting("settings.datefmt", "Date format",
1103
                               RadioSettingValueList(LIST_DATEFMT,
1104
                                   LIST_DATEFMT[_mem.settings.datefmt]))
1105
        basic.append(datefmt)
1106

    
1107
        timefmt = RadioSetting("settings.timefmt", "Time format",
1108
                               RadioSettingValueList(LIST_TIMEFMT,
1109
                                   LIST_TIMEFMT[_mem.settings.timefmt]))
1110
        basic.append(timefmt)
1111

    
1112
        timesig = RadioSetting("settings.timesig", "Time signal",
1113
                               RadioSettingValueBoolean(_mem.settings.timesig))
1114
        basic.append(timesig)
1115

    
1116
        tz = RadioSetting("settings.tz", "Time zone",
1117
                          RadioSettingValueList(LIST_TZ, LIST_TZ[
1118
                              _mem.settings.tz]))
1119
        basic.append(tz)
1120

    
1121
        # Signaling: H01-H06
1122

    
1123
        bell = RadioSetting("settings.bell", "Bell ringer",
1124
                            RadioSettingValueList(LIST_BELL, LIST_BELL[
1125
                                _mem.settings.bell]))
1126
        basic.append(bell)
1127

    
1128
        # H02 (per channel)
1129

    
1130
        dtmfmodenc = RadioSetting("settings.dtmfmodenc", "DTMF mode encode",
1131
                                  RadioSettingValueBoolean(
1132
                                      _mem.settings.dtmfmodenc))
1133
        basic.append(dtmfmodenc)
1134

    
1135
        dtmfmoddec = RadioSetting("settings.dtmfmoddec", "DTMF mode decode",
1136
                                  RadioSettingValueBoolean(
1137
                                      _mem.settings.dtmfmoddec))
1138
        basic.append(dtmfmoddec)
1139

    
1140
        # H04 (per channel)
1141

    
1142
        decbandsel = RadioSetting("settings.decbandsel", "DTMF band select",
1143
                                  RadioSettingValueList(LIST_AB,LIST_AB[
1144
                                      _mem.settings.decbandsel]))
1145
        basic.append(decbandsel)
1146

    
1147
        sqlexp = RadioSetting("settings.sqlexp", "SQL expansion",
1148
                              RadioSettingValueBoolean(_mem.settings.sqlexp))
1149
        basic.append(sqlexp)
1150

    
1151
        # Pkt: I01-I03
1152

    
1153
        databnd = RadioSetting("settings.databnd", "Packet data band",
1154
                               RadioSettingValueList(LIST_DATABND,LIST_DATABND[
1155
                                   _mem.settings.databnd]))
1156
        basic.append(databnd)
1157

    
1158
        dataspd = RadioSetting("settings.dataspd", "Packet data speed",
1159
                               RadioSettingValueList(LIST_DATASPD,LIST_DATASPD[
1160
                                   _mem.settings.dataspd]))
1161
        basic.append(dataspd)
1162

    
1163
        datasql = RadioSetting("settings.datasql", "Packet data squelch",
1164
                               RadioSettingValueList(LIST_DATASQL,LIST_DATASQL[
1165
                                   _mem.settings.datasql]))
1166
        basic.append(datasql)
1167

    
1168
        # Other
1169

    
1170
        dw = RadioSetting("settings.dw", "Dual watch",
1171
                          RadioSettingValueBoolean(_mem.settings.dw))
1172
        other.append(dw)
1173

    
1174
        cpuclk = RadioSetting("settings.cpuclk", "CPU clock frequency",
1175
                              RadioSettingValueList(LIST_CPUCLK,LIST_CPUCLK[
1176
                                  _mem.settings.cpuclk]))
1177
        other.append(cpuclk)
1178

    
1179
        def _filter(name):
1180
            filtered = ""
1181
            for char in str(name):
1182
                if char in VALID_CHARS:
1183
                    filtered += char
1184
                else:
1185
                    filtered += " "
1186
            return filtered
1187

    
1188
        line16 = RadioSetting("poweron_msg.line16", "Power-on message",
1189
                              RadioSettingValueString(0, 16, _filter(
1190
                                  _mem.poweron_msg.line16)))
1191
        other.append(line16)
1192

    
1193
        line32 = RadioSetting("embedded_msg.line32", "Embedded message",
1194
                              RadioSettingValueString(0, 32, _filter(
1195
                                  _mem.embedded_msg.line32)))
1196
        other.append(line32)
1197

    
1198
        # Work
1199

    
1200
        workmoda = RadioSetting("settings.workmoda", "Work mode A",
1201
                                RadioSettingValueList(LIST_WORK,LIST_WORK[
1202
                                    _mem.settings.workmoda]))
1203
        work.append(workmoda)
1204

    
1205
        workmodb = RadioSetting("settings.workmodb", "Work mode B",
1206
                                RadioSettingValueList(LIST_WORK,LIST_WORK[
1207
                                    _mem.settings.workmodb]))
1208
        work.append(workmodb)
1209

    
1210
        wbanda = RadioSetting("settings.wbanda", "Work band A",
1211
                              RadioSettingValueList(LIST_WBANDA, LIST_WBANDA[
1212
                                  (_mem.settings.wbanda) - 1]))
1213
        work.append(wbanda)
1214

    
1215
        wbandb = RadioSetting("settings.wbandb", "Work band B",
1216
                              RadioSettingValueList(LIST_WBANDB, LIST_WBANDB[
1217
                                  (_mem.settings.wbandb) - 4]))
1218
        work.append(wbandb)
1219

    
1220
        sqla = RadioSetting("settings.sqla", "Squelch A",
1221
                            RadioSettingValueList(LIST_SQL, LIST_SQL[
1222
                                _mem.settings.sqla]))
1223
        work.append(sqla)
1224

    
1225
        sqlb = RadioSetting("settings.sqlb", "Squelch B",
1226
                            RadioSettingValueList(LIST_SQL, LIST_SQL[
1227
                                _mem.settings.sqlb]))
1228
        work.append(sqlb)
1229

    
1230
        stepa = RadioSetting("settings.stepa", "Auto step A",
1231
                             RadioSettingValueList(LIST_STEP,LIST_STEP[
1232
                                 _mem.settings.stepa]))
1233
        work.append(stepa)
1234

    
1235
        stepb = RadioSetting("settings.stepb", "Auto step B",
1236
                             RadioSettingValueList(LIST_STEP,LIST_STEP[
1237
                                 _mem.settings.stepb]))
1238
        work.append(stepb)
1239

    
1240
        mrcha = RadioSetting("settings.mrcha", "Current channel A",
1241
                             RadioSettingValueInteger(0, 499,
1242
                                 _mem.settings.mrcha))
1243
        work.append(mrcha)
1244

    
1245
        mrchb = RadioSetting("settings.mrchb", "Current channel B",
1246
                             RadioSettingValueInteger(0, 499,
1247
                                 _mem.settings.mrchb))
1248
        work.append(mrchb)
1249

    
1250
        val = _mem.settings.offseta / 100.00
1251
        offseta = RadioSetting("settings.offseta", "Offset A (0-37.95)",
1252
                               RadioSettingValueFloat(0, 38.00, val, 0.05, 2))
1253
        work.append(offseta)
1254

    
1255
        val = _mem.settings.offsetb / 100.00
1256
        offsetb = RadioSetting("settings.offsetb", "Offset B (0-79.95)",
1257
                               RadioSettingValueFloat(0, 80.00, val, 0.05, 2))
1258
        work.append(offsetb)
1259

    
1260
        wpricha = RadioSetting("settings.wpricha", "Priority channel A",
1261
                               RadioSettingValueInteger(0, 499,
1262
                                   _mem.settings.wpricha))
1263
        work.append(wpricha)
1264

    
1265
        wprichb = RadioSetting("settings.wprichb", "Priority channel B",
1266
                               RadioSettingValueInteger(0, 499,
1267
                                   _mem.settings.wprichb))
1268
        work.append(wprichb)
1269

    
1270
        smode = RadioSetting("settings.smode", "Smart function mode",
1271
                             RadioSettingValueList(LIST_SMODE,LIST_SMODE[
1272
                                 _mem.settings.smode]))
1273
        work.append(smode)
1274

    
1275
        # dtmf
1276

    
1277
        ttdkey = RadioSetting("dtmf.ttdkey", "D key function",
1278
                              RadioSettingValueList(LIST_TTDKEY, LIST_TTDKEY[
1279
                                  _mem.dtmf.ttdkey]))
1280
        dtmf.append(ttdkey)
1281

    
1282
        ttdgt = RadioSetting("dtmf.ttdgt", "Digit time",
1283
                              RadioSettingValueList(LIST_TT200, LIST_TT200[
1284
                                  (_mem.dtmf.ttdgt) - 5]))
1285
        dtmf.append(ttdgt)
1286

    
1287
        ttint = RadioSetting("dtmf.ttint", "Interval time",
1288
                              RadioSettingValueList(LIST_TT200, LIST_TT200[
1289
                                  (_mem.dtmf.ttint) - 5]))
1290
        dtmf.append(ttint)
1291

    
1292
        tt1stdgt = RadioSetting("dtmf.tt1stdgt", "1st digit time",
1293
                                RadioSettingValueList(LIST_TT200, LIST_TT200[
1294
                                    (_mem.dtmf.tt1stdgt) - 5]))
1295
        dtmf.append(tt1stdgt)
1296

    
1297
        tt1stdly = RadioSetting("dtmf.tt1stdly", "1st digit delay time",
1298
                                RadioSettingValueList(LIST_TT1000, LIST_TT1000[
1299
                                    (_mem.dtmf.tt1stdly) - 2]))
1300
        dtmf.append(tt1stdly)
1301

    
1302
        ttdlyqt = RadioSetting("dtmf.ttdlyqt", "Digit delay when use qt",
1303
                               RadioSettingValueList(LIST_TT1000, LIST_TT1000[
1304
                                   (_mem.dtmf.ttdlyqt) - 2]))
1305
        dtmf.append(ttdlyqt)
1306

    
1307
        ttsig = RadioSetting("dtmf2.ttsig", "Signal",
1308
                             RadioSettingValueList(LIST_TTSIG, LIST_TTSIG[
1309
                                 _mem.dtmf2.ttsig]))
1310
        dtmf.append(ttsig)
1311

    
1312
        ttautorst = RadioSetting("dtmf2.ttautorst", "Auto reset time",
1313
                                 RadioSettingValueList(LIST_TTAUTORST,
1314
                                     LIST_TTAUTORST[_mem.dtmf2.ttautorst]))
1315
        dtmf.append(ttautorst)
1316

    
1317
        if _mem.dtmf2.ttgrpcode > 0x06:
1318
            val = 0x00
1319
        else:
1320
            val = _mem.dtmf2.ttgrpcode
1321
        ttgrpcode = RadioSetting("dtmf2.ttgrpcode", "Group code",
1322
                                 RadioSettingValueList(LIST_TTGRPCODE,
1323
                                     LIST_TTGRPCODE[val]))
1324
        dtmf.append(ttgrpcode)
1325

    
1326
        ttintcode = RadioSetting("dtmf2.ttintcode", "Interval code",
1327
                                 RadioSettingValueList(LIST_TTINTCODE,
1328
                                     LIST_TTINTCODE[_mem.dtmf2.ttintcode]))
1329
        dtmf.append(ttintcode)
1330

    
1331
        if _mem.dtmf2.ttalert > 0x04:
1332
            val = 0x00
1333
        else:
1334
            val = _mem.dtmf2.ttalert
1335
        ttalert = RadioSetting("dtmf2.ttalert", "Alert tone/transpond",
1336
                               RadioSettingValueList(LIST_TTALERT,
1337
                                   LIST_TTALERT[val]))
1338
        dtmf.append(ttalert)
1339

    
1340
        ttautod = RadioSetting("dtmf.ttautod", "Auto dial group",
1341
                               RadioSettingValueList(LIST_TTAUTOD,
1342
                                   LIST_TTAUTOD[_mem.dtmf.ttautod]))
1343
        dtmf.append(ttautod)
1344

    
1345
        # setup 9 dtmf autodial entries
1346
        for i in map(str, range(1, 10)):
1347
            objname = "code" + i
1348
            strname = "Code " + str(i)
1349
            dtmfsetting = getattr(_mem.dtmfcode, objname)
1350
            dtmflen = getattr(_mem.dtmfcode, objname + "_len")
1351
            dtmfstr = self._bbcd2dtmf(dtmfsetting, dtmflen)
1352
            code = RadioSettingValueString(0, 16, dtmfstr)
1353
            code.set_charset(DTMF_CHARS + list(" "))
1354
            rs = RadioSetting("dtmfcode." + objname, strname, code)
1355
            dtmf.append(rs)
1356
        return top
1357

    
1358
    def set_settings(self, settings):
1359
        _settings = self._memobj.settings
1360
        _mem = self._memobj
1361
        for element in settings:
1362
            if not isinstance(element, RadioSetting):
1363
                self.set_settings(element)
1364
                continue
1365
            else:
1366
                try:
1367
                    name = element.get_name()
1368
                    if "." in name:
1369
                        bits = name.split(".")
1370
                        obj = self._memobj
1371
                        for bit in bits[:-1]:
1372
                            if "/" in bit:
1373
                                bit, index = bit.split("/", 1)
1374
                                index = int(index)
1375
                                obj = getattr(obj, bit)[index]
1376
                            else:
1377
                                obj = getattr(obj, bit)
1378
                        setting = bits[-1]
1379
                    else:
1380
                        obj = _settings
1381
                        setting = element.get_name()
1382

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

    
1417

    
1418
    @classmethod
1419
    def match_model(cls, filedata, filename):
1420
        match_size = False
1421
        match_model = False
1422

    
1423
        # testing the file data size
1424
        if len(filedata) == MEM_SIZE:
1425
            match_size = True
1426

    
1427
        # testing the firmware model fingerprint
1428
        match_model = model_match(cls, filedata)
1429

    
1430
        if match_size and match_model:
1431
            return True
1432
        else:
1433
            return False
1434

    
1435

    
1436
@directory.register
1437
class UV50X3(VGCStyleRadio):
1438
    """BTech UV-50X3"""
1439
    MODEL = "UV-50X3"
1440
    IDENT = UV50X3_id
1441

    
1442

    
1443
class UV50X3Left(UV50X3):
1444
    VARIANT = "Left"
1445
    _vfo = "left"
1446

    
1447

    
1448
class UV50X3Right(UV50X3):
1449
    VARIANT = "Right"
1450
    _vfo = "right"
(2-2/19)