retevis_rt23_20170531.py

Jim Unroe, 05/31/2017 04:52 pm

Download (27.9 kB)

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

    
16
import time
17
import os
18
import struct
19
import re
20
import logging
21

    
22
from chirp import chirp_common, directory, memmap
23
from chirp import bitwise, errors, util
24
from chirp.settings import RadioSetting, RadioSettingGroup, \
25
    RadioSettingValueInteger, RadioSettingValueList, \
26
    RadioSettingValueBoolean, RadioSettingValueString, \
27
    RadioSettings
28

    
29
LOG = logging.getLogger(__name__)
30

    
31
MEM_FORMAT = """
32
struct memory {
33
  lbcd rxfreq[4];
34
  lbcd txfreq[4];
35
  lbcd rxtone[2];
36
  lbcd txtone[2];
37
  u8 unknown1;
38
  u8 pttid:2,     // PTT-ID
39
     unknown2:1,
40
     signaling:1, // Signaling(ANI)
41
     unknown3:1,
42
     bcl:1,       // Busy Channel Lockout
43
     unknown4:2;
44
  u8 unknown5:3,
45
     highpower:1, // Power Level
46
     isnarrow:1,  // Bandwidth
47
     scan:1,      // Scan Add
48
     unknown6:2;
49
  u8 unknown7;
50
};
51

    
52
#seekto 0x0010;
53
struct memory channels[128];
54

    
55
#seekto 0x0810;
56
struct memory vfo_a;
57
struct memory vfo_b;
58

    
59
#seekto 0x0830;
60
struct {
61
  u8 unknown_0830_1:4,      // 0x0830
62
     color:2,               // Background Color
63
     dst:1,                 // DTMF Side Tone
64
     txsel:1;               // Priority TX Channel Select
65
  u8 scans:2,               // Scan Mode - 0x0831
66
     unknown_0831:1,
67
     autolk:1,              // Auto Key Lock
68
     save:1,                // Battery Save
69
     beep:1,                // Key Beep
70
     voice:2;               // Voice Prompt
71
  u8 vfomr_fm:1,            // FM Radio Display Mode - 0x0832
72
     led:2,                 // Background Light
73
     unknown_0832_2:1,
74
     dw:1,                  // FM Radio Dual Watch
75
     name:1,                // Display Names
76
     vfomr_a:2;             // Display Mode A
77
  u8 opnset:2,              // Power On Message - 0x0833
78
     unknown_0833_1:3,
79
     dwait:1,               // Dual Standby
80
     vfomr_b:2;             // Display Mode B
81
  u8 mrcha;                 // mr a ch num - 0x0834
82
  u8 mrchb;                 // mr b ch num - 0x0835
83
  u8 fmch;                  // fm radio ch num - 0x0836
84
  u8 unknown_0837_1:1,      // 0x0837
85
     ste:1,                 // Squelch Tail Eliminate
86
     roger:1,               // Roger Beep
87
     unknown_0837_2:1,
88
     vox:4;                 // VOX
89
  u8 step:4,                // Step - 0x0838
90
     unknown_0838_1:4;
91
  u8 squelch;               // Squelch - 0x0839
92
  u8 tot;                   // Time Out Timer - 0x083A
93
  u8 rptmod:1,              // Repeater Mode - 0x083B
94
     volmod:2,              // Volume Mode
95
     rptptt:1,              // Repeater PTT Switch
96
     rptspk:1,              // Repeater Speaker
97
     relay:3;               // Cross Band Repeater Enable
98
  u8 unknown_083C:4,        // 0x083C
99
     rptrl:4;               // Repeater TX Delay
100
  u8 pf1:4,                 // Function Key 1 - 0x083D
101
     pf2:4;                 // Function Key 2
102
  u8 vot;                   // VOX Delay Time - 0x083E
103
} settings;
104

    
105
#seekto 0x0848;
106
struct {
107
  char line1[7];
108
} poweron_msg;
109

    
110
struct limit {
111
  bbcd lower[2];
112
  bbcd upper[2];
113
};
114

    
115
#seekto 0x0850;
116
struct {
117
  struct limit vhf;
118
  struct limit uhf;
119
} limits;
120

    
121
struct fmmemory {
122
  lbcd fmfreq[4];
123
};
124

    
125
#seekto 0x085E;
126
struct fmmemory fmchannels[25];
127
struct fmmemory fmvfo;
128

    
129
#seekto 0x08D0;
130
struct {
131
  char name[7];             // Channel Names
132
  u8 unknown2[1];
133
} names[128];
134

    
135
#seekto 0x0D20;
136
u8 usedflags[16];
137
u8 scanflags[16];
138

    
139
//#seekto 0x0E50;
140
//struct memory rpt_vfo;
141
//struct memory rpt_1;
142
//struct memory rpt_2;
143
//struct memory rpt_3;
144
//struct memory rpt_4;
145
//struct memory rpt_5;
146

    
147
//#seekto 0x0EB0;
148
//struct {
149
//    u8 unknown:2,
150
//       rpt_5_enable:1,
151
//       rpt_4_enable:1,
152
//       rpt_3_enable:1,
153
//       rpt_2_enable:1,
154
//       rpt_1_enable:1,
155
//       rpt_vfo_enable:1;
156
//} special_enables;
157

    
158
#seekto 0x0FA0;
159
struct {
160
  u8 unknown_0FA0_1:4,
161
     dispab:1,              // select a/b
162
     unknown_0FA0_2:3;
163
} settings2;
164
"""
165

    
166
CMD_ACK = "\x06"
167
BLOCK_SIZE = 0x10
168

    
169
RT23_POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.00),
170
                     chirp_common.PowerLevel("High", watts=2.50)]
171

    
172

    
173
RT23_DTCS = sorted(chirp_common.DTCS_CODES + 
174
                   [17, 50, 55, 135, 217, 254, 305, 645, 765])
175

    
176
RT23_CHARSET = chirp_common.CHARSET_UPPER_NUMERIC + \
177
    ":;<=>?@ !\"#$%&'()*+,-./"
178

    
179
LIST_SIGNALING = ["No", "DTMF"]
180
LIST_STEP = ["2.50K", "5.00K", "6.25K", "10.00K", "12,50K", "20.00K", "25.00K", 
181
             "50.00K"]
182
####LIST_OFFON = ["OFF", "ON"]
183
LIST_VOX = ["OFF"] + ["%s" % x for x in range(1, 6)]
184
LIST_TOT = ["OFF"] + ["%s seconds" % x for x in range(30, 300, 30)]
185
LIST_COLOR = ["Blue", "Orange", "Purple"]
186
LIST_VOT = ["0.5S", "1.0S", "1.5S", "2.0S", "3.0S"]
187
LIST_LED = ["Off", "On", "Auto"]
188
LIST_VOICE = ["Off", "Chinese", "English"]
189
LIST_OPNSET = ["Full", "Voltage", "Message"]
190
LIST_SCANS = ["Time Operated", "Carrier Operated", "Search"]
191

    
192
LIST_RPTRL = ["0.5S", "1.0S", "1.5S", "2.0S", "2.5S", "3.0S", "3.5S", "4.0S", 
193
              "4.5S"]
194
LIST_RPTMOD = ["Single", "Double"]
195
LIST_VOLMOD = ["Off", "Sub", "Main"]
196
LIST_TXSEL = ["Edit", "Busy"]
197

    
198
LIST_VFOMR = ["VFO", "MR(Frequency)", "MR(Channel #/Name)"]
199
LIST_VFOMRFM = ["VFO", "Channel"]
200

    
201
LIST_PTTID = ["Off", "BOT", "EOT", "Both"]
202

    
203
LIST_PFKEY = [
204
    "Radio", 
205
    "Sub-channel Sent", 
206
    "Scan", 
207
    "Alarm", 
208
    "DTMF", 
209
    "Squelch Off Momentarily", 
210
    "Battery Power Indicator",
211
    "Tone 1750",
212
    "Tone 2100",
213
    "Tone 1000",
214
    "Tone 1450"]
215

    
216
####RT23_SPECIAL = ["rpt_vfo", "rpt_1", "rpt_2", "rpt_3", "rpt_4", "rpt_5",
217
####                "vfo_a", "vfo_b"]
218

    
219

    
220
def _rt23_enter_programming_mode(radio):
221
    serial = radio.pipe
222

    
223
    #magic = "PROIUAM"
224
    try:
225
        serial.write("PROIUAM")
226
        #for j in range(0, len(magic)):
227
        #    time.sleep(0.002)
228
        #    serial.write(magic[j])
229
        ack = serial.read(1)
230
    except:
231
        raise errors.RadioError("Error communicating with radio")
232

    
233
    if not ack:
234
        raise errors.RadioError("No response from radio")
235
    elif ack != CMD_ACK:
236
        raise errors.RadioError("Radio refused to enter programming mode")
237

    
238
    try:
239
        serial.write("\x02")
240
        ident = serial.read(8)
241
    except:
242
        raise errors.RadioError("Error communicating with radio")
243

    
244
    if not ident.startswith("P31183"):
245
        LOG.debug(util.hexprint(ident))
246
        raise errors.RadioError("Radio returned unknown identification string")
247

    
248
    try:
249
        serial.write(CMD_ACK)
250
        ack = serial.read(1)
251
    except:
252
        raise errors.RadioError("Error communicating with radio")
253

    
254
    if ack != CMD_ACK:
255
        raise errors.RadioError("Radio refused to enter programming mode")
256

    
257

    
258
def _rt23_exit_programming_mode(radio):
259
    serial = radio.pipe
260
    try:
261
        serial.write("E")
262
    except:
263
        raise errors.RadioError("Radio refused to exit programming mode")
264

    
265

    
266
def _rt23_read_block(radio, block_addr, block_size):
267
    serial = radio.pipe
268

    
269
    cmd = struct.pack(">cHb", 'R', block_addr, BLOCK_SIZE)
270
    expectedresponse = "W" + cmd[1:]
271
    LOG.debug("Reading block %04x..." % (block_addr))
272

    
273
    try:
274
        serial.write(cmd)
275
        response = serial.read(4 + BLOCK_SIZE + 1)
276
        if response[:4] != expectedresponse:
277
            raise Exception("Error reading block %04x." % (block_addr))
278

    
279
        chunk = response[4:]
280

    
281
        cs = 0
282
        for byte in chunk[:-1]:
283
            cs += ord(byte)
284
        if ord(chunk[-1]) != (cs & 0xFF):
285
            raise Exception("Block failed checksum!")
286

    
287
        block_data = chunk[:-1]
288
    except:
289
        raise errors.RadioError("Failed to read block at %04x" % block_addr)
290

    
291
    return block_data
292

    
293

    
294
def _rt23_write_block(radio, block_addr, block_size):
295
    serial = radio.pipe
296

    
297
    cmd = struct.pack(">cHb", 'W', block_addr, BLOCK_SIZE)
298
    data = radio.get_mmap()[block_addr:block_addr + BLOCK_SIZE]
299
    cs = 0
300
    for byte in data:
301
        cs += ord(byte)
302
    data += chr(cs & 0xFF)
303

    
304
    LOG.debug("Writing Data:")
305
    LOG.debug(util.hexprint(cmd + data))
306

    
307
    try:
308
        #serial.write(cmd + data)
309
        for j in range(0, len(cmd)):
310
            time.sleep(0.002)
311
            serial.write(cmd[j])
312
        for j in range(0, len(data)):
313
            time.sleep(0.002)
314
            serial.write(data[j])
315
        if serial.read(1) != CMD_ACK:
316
            raise Exception("No ACK")
317
    except:
318
        raise errors.RadioError("Failed to send block "
319
                                "to radio at %04x" % block_addr)
320

    
321

    
322
def do_download(radio):
323
    LOG.debug("download")
324
    _rt23_enter_programming_mode(radio)
325

    
326
    data = ""
327

    
328
    status = chirp_common.Status()
329
    status.msg = "Cloning from radio"
330

    
331
    status.cur = 0
332
    status.max = radio._memsize
333

    
334
    for addr in range(0, radio._memsize, BLOCK_SIZE):
335
        status.cur = addr + BLOCK_SIZE
336
        radio.status_fn(status)
337

    
338
        ####print "Address: %s" % addr
339
        block = _rt23_read_block(radio, addr, BLOCK_SIZE)
340
        if addr == 0 and block.startswith("\xFF" * 6):
341
            block = "P31183" + "\xFF" * 10
342
        data += block
343

    
344
        LOG.debug("Address: %04x" % addr)
345
        LOG.debug(util.hexprint(block))
346

    
347
    ####data += radio.MODEL.ljust(8)
348

    
349
    _rt23_exit_programming_mode(radio)
350

    
351
    return memmap.MemoryMap(data)
352

    
353

    
354
def do_upload(radio):
355
    status = chirp_common.Status()
356
    status.msg = "Uploading to radio"
357

    
358
    _rt23_enter_programming_mode(radio)
359

    
360
    status.cur = 0
361
    status.max = radio._memsize
362

    
363
    for start_addr, end_addr in radio._ranges:
364
        for addr in range(start_addr, end_addr, BLOCK_SIZE):
365
            status.cur = addr + BLOCK_SIZE
366
            radio.status_fn(status)
367
            _rt23_write_block(radio, addr, BLOCK_SIZE)
368

    
369
    ####_rt23_exit_programming_mode(radio)
370

    
371

    
372
def model_match(cls, data):
373
    """Match the opened/downloaded image to the correct version"""
374

    
375
    if len(data) == 0x1000:
376
        rid = data[0x0000:0x0006]
377
        return rid == "P31183"
378
    else:
379
        return False
380

    
381

    
382
def _split(rf, f1, f2):
383
    """Returns False if the two freqs are in the same band (no split)
384
    or True otherwise"""
385

    
386
    # determine if the two freqs are in the same band
387
    for low, high in rf.valid_bands:
388
        if f1 >= low and f1 <= high and \
389
                f2 >= low and f2 <= high:
390
            # if the two freqs are on the same Band this is not a split
391
            return False
392

    
393
    # if you get here is because the freq pairs are split
394
    return True
395

    
396

    
397
@directory.register
398
class RT23Radio(chirp_common.CloneModeRadio):
399
    """RETEVIS RT23"""
400
    VENDOR = "Retevis"
401
    MODEL = "RT23"
402
    BAUD_RATE = 9600
403

    
404
    _ranges = [
405
               (0x0000, 0x0EC0),
406
              ]
407
    _memsize = 0x1000
408

    
409
    def get_features(self):
410
        rf = chirp_common.RadioFeatures()
411
        rf.has_settings = True
412
        rf.has_bank = False
413
        rf.has_ctone = True
414
        rf.has_cross = True
415
        rf.has_rx_dtcs = True
416
        rf.has_tuning_step = False
417
        rf.can_odd_split = True
418
        rf.valid_name_length = 7
419
        rf.valid_characters = RT23_CHARSET
420
        rf.has_name = True
421
        rf.valid_skips = ["", "S"]
422
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
423
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
424
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
425
        rf.valid_power_levels = RT23_POWER_LEVELS
426
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
427
        rf.valid_modes = ["FM", "NFM"]  # 25 KHz, 12.5 KHz.
428
        rf.memory_bounds = (1, 128)
429
        rf.valid_bands = [
430
            (136000000, 174000000),
431
            (400000000, 480000000)]
432
        ####rf.valid_special_chans = RT23_SPECIAL
433

    
434
        return rf
435

    
436
    def process_mmap(self):
437
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
438

    
439
    def sync_in(self):
440
        self._mmap = do_download(self)
441
        self.process_mmap()
442

    
443
    def sync_out(self):
444
        do_upload(self)
445

    
446
    def get_raw_memory(self, number):
447
        return repr(self._memobj.memory[number - 1])
448

    
449
    def decode_tone(self, val):
450
        """Parse the tone data to decode from mem, it returns:
451
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
452
        if val.get_raw() == "\xFF\xFF":
453
            return '', None, None
454

    
455
        val = int(val)
456
        if val >= 12000:
457
            a = val - 12000
458
            return 'DTCS', a, 'R'
459
        elif val >= 8000:
460
            a = val - 8000
461
            return 'DTCS', a, 'N'
462
        else:
463
            a = val / 10.0
464
            return 'Tone', a, None
465

    
466
    def encode_tone(self, memval, mode, value, pol):
467
        """Parse the tone data to encode from UI to mem"""
468
        if mode == '':
469
            memval[0].set_raw(0xFF)
470
            memval[1].set_raw(0xFF)
471
        elif mode == 'Tone':
472
            memval.set_value(int(value * 10))
473
        elif mode == 'DTCS':
474
            flag = 0x80 if pol == 'N' else 0xC0
475
            memval.set_value(value)
476
            memval[1].set_bits(flag)
477
        else:
478
            raise Exception("Internal error: invalid mode `%s'" % mode)
479

    
480
    def get_memory(self, number):
481
        mem = chirp_common.Memory()
482
        _mem = self._memobj.channels[number-1]
483
        _nam = self._memobj.names[number - 1]
484
        mem.number = number
485
        bitpos = (1 << ((number - 1) % 8))
486
        bytepos = ((number - 1) / 8)
487
        _scn = self._memobj.scanflags[bytepos]
488
        _usd = self._memobj.usedflags[bytepos]
489
        isused = bitpos & int(_usd)
490
        isscan = bitpos & int(_scn)
491

    
492
        if not isused:
493
            mem.empty = True
494
            return mem
495

    
496
        mem.freq = int(_mem.rxfreq) * 10
497

    
498
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
499
        if mem.freq == 0:
500
            mem.empty = True
501
            return mem
502

    
503
        if _mem.rxfreq.get_raw() == "\xFF\xFF\xFF\xFF":
504
            mem.empty = True
505
            return mem
506

    
507
        if _mem.get_raw() == ("\xFF" * 16):
508
            LOG.debug("Initializing empty memory")
509
            _mem.set_raw("\x00" * 16)
510

    
511
        # Freq and offset
512
        mem.freq = int(_mem.rxfreq) * 10
513
        # tx freq can be blank
514
        if _mem.get_raw()[4] == "\xFF":
515
            # TX freq not set
516
            mem.offset = 0
517
            mem.duplex = "off"
518
        else:
519
            # TX freq set
520
            offset = (int(_mem.txfreq) * 10) - mem.freq
521
            if offset != 0:
522
                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
523
                    mem.duplex = "split"
524
                    mem.offset = int(_mem.txfreq) * 10
525
                elif offset < 0:
526
                    mem.offset = abs(offset)
527
                    mem.duplex = "-"
528
                elif offset > 0:
529
                    mem.offset = offset
530
                    mem.duplex = "+"
531
            else:
532
                mem.offset = 0
533

    
534
        for char in _nam.name:
535
            if str(char) == "\xFF":
536
                char = " "
537
            mem.name += str(char)
538
        mem.name = mem.name.rstrip()
539

    
540
        mem.mode = _mem.isnarrow and "NFM" or "FM"
541

    
542
        rxtone = txtone = None
543
        txtone = self.decode_tone(_mem.txtone)
544
        rxtone = self.decode_tone(_mem.rxtone)
545
        chirp_common.split_tone_decode(mem, txtone, rxtone)
546

    
547
        mem.power = RT23_POWER_LEVELS[_mem.highpower]
548

    
549
        if not isscan:
550
            mem.skip = "S"
551

    
552
        mem.extra = RadioSettingGroup("Extra", "extra")
553

    
554
        rs = RadioSetting("bcl", "BCL",
555
                          RadioSettingValueBoolean(_mem.bcl))
556
        mem.extra.append(rs)
557

    
558
        rs = RadioSetting("pttid", "PTT ID",
559
                          RadioSettingValueList(
560
                              LIST_PTTID, LIST_PTTID[_mem.pttid]))
561
        mem.extra.append(rs)
562

    
563
        rs = RadioSetting("signaling", "Optional Signaling",
564
                          RadioSettingValueList(LIST_SIGNALING,
565
                              LIST_SIGNALING[_mem.signaling]))
566
        mem.extra.append(rs)
567

    
568
        return mem
569

    
570
    def set_memory(self, mem):
571
        LOG.debug("Setting %i(%s)" % (mem.number, mem.extd_number))
572
        _mem = self._memobj.channels[mem.number - 1]
573
        _nam = self._memobj.names[mem.number - 1]
574
        bitpos = (1 << ((mem.number - 1) % 8))
575
        bytepos = ((mem.number - 1) / 8)
576
        _scn = self._memobj.scanflags[bytepos]
577
        _usd = self._memobj.usedflags[bytepos]
578

    
579
        if mem.empty:
580
            _mem.set_raw("\xFF" * 16)
581
            _nam.name = ("\xFF" * 7)
582
            _usd &= ~bitpos
583
            _scn &= ~bitpos
584
            return
585
        else:
586
            _usd |= bitpos
587

    
588
        if _mem.get_raw() == ("\xFF" * 16):
589
            LOG.debug("Initializing empty memory")
590
            _mem.set_raw("\x00" * 16)
591
            _scn |= bitpos
592

    
593
        ##_mem.set_raw("\x00" * 16)
594

    
595
        _mem.rxfreq = mem.freq / 10
596

    
597
        if mem.duplex == "off":
598
            for i in range(0, 4):
599
                _mem.txfreq[i].set_raw("\xFF")
600
        elif mem.duplex == "split":
601
            _mem.txfreq = mem.offset / 10
602
        elif mem.duplex == "+":
603
            _mem.txfreq = (mem.freq + mem.offset) / 10
604
        elif mem.duplex == "-":
605
            _mem.txfreq = (mem.freq - mem.offset) / 10
606
        else:
607
            _mem.txfreq = mem.freq / 10
608

    
609
        _namelength = self.get_features().valid_name_length
610
        for i in range(_namelength):
611
            try:
612
                _nam.name[i] = mem.name[i]
613
            except IndexError:
614
                _nam.name[i] = "\xFF"
615

    
616
        _mem.scan = mem.skip != "S"
617
        if mem.skip == "S":
618
            _scn &= ~bitpos
619
        else:
620
            _scn |= bitpos
621
        _mem.isnarrow = mem.mode == "NFM"
622

    
623
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
624
            chirp_common.split_tone_encode(mem)
625
        self.encode_tone(_mem.txtone, txmode, txtone, txpol)
626
        self.encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
627

    
628
        _mem.highpower = mem.power == RT23_POWER_LEVELS[1]
629

    
630
        for setting in mem.extra:
631
            setattr(_mem, setting.get_name(), setting.value)
632

    
633
    def get_settings(self):
634
        ####_keys = self._memobj.keys
635
        _settings = self._memobj.settings
636
        _mem = self._memobj
637
        basic = RadioSettingGroup("basic", "Basic Settings")
638
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
639
        other = RadioSettingGroup("other", "Other Settings")
640
        workmode = RadioSettingGroup("workmode", "Workmode Settings")
641
        fmradio = RadioSettingGroup("fmradio", "FM Radio Settings")
642
        top = RadioSettings(basic, advanced, other, workmode, fmradio)
643

    
644
        #step = RadioSetting("step", "Step", RadioSettingValueList(
645
        #                    LIST_STEP, LIST_STEP[_settings.step]))
646
        #basic.append(step)
647

    
648
        save = RadioSetting("save", "Battery Saver",
649
                            RadioSettingValueBoolean(_settings.save))
650
        basic.append(save)
651

    
652
        vox = RadioSetting("vox", "VOX Gain",
653
                           RadioSettingValueList(
654
                               LIST_VOX, LIST_VOX[_settings.vox]))
655
        basic.append(vox)
656

    
657
        squelch = RadioSetting("squelch", "Squelch Level",
658
                               RadioSettingValueInteger(
659
                                   0, 9, _settings.squelch))
660
        basic.append(squelch)
661

    
662
        relay = RadioSetting("relay", "Repeater",
663
                             RadioSettingValueBoolean(_settings.relay))
664
        basic.append(relay)
665

    
666
        tot = RadioSetting("tot", "Time-out timer", RadioSettingValueList(
667
                           LIST_TOT, LIST_TOT[_settings.tot]))
668
        basic.append(tot)
669

    
670
        beep = RadioSetting("beep", "Key Beep",
671
                            RadioSettingValueBoolean(_settings.beep))
672
        basic.append(beep)
673

    
674
        color = RadioSetting("color", "Background Color", RadioSettingValueList(
675
                             LIST_COLOR, LIST_COLOR[_settings.color - 1]))
676
        basic.append(color)
677

    
678
        vot = RadioSetting("vot", "VOX Delay Time", RadioSettingValueList(
679
                           LIST_VOT, LIST_VOT[_settings.vot]))
680
        basic.append(vot)
681

    
682
        dwait = RadioSetting("dwait", "Dual Standby",
683
                             RadioSettingValueBoolean(_settings.dwait))
684
        basic.append(dwait)
685

    
686
        led = RadioSetting("led", "Background Light", RadioSettingValueList(
687
                           LIST_LED, LIST_LED[_settings.led]))
688
        basic.append(led)
689

    
690
        voice = RadioSetting("voice", "Voice Prompt", RadioSettingValueList(
691
                             LIST_VOICE, LIST_VOICE[_settings.voice]))
692
        basic.append(voice)
693
        
694
        roger = RadioSetting("roger", "Roger Beep",
695
                             RadioSettingValueBoolean(_settings.roger))
696
        basic.append(roger)
697

    
698
        autolk = RadioSetting("autolk", "Auto Key Lock",
699
                              RadioSettingValueBoolean(_settings.autolk))
700
        basic.append(autolk)
701

    
702
        opnset = RadioSetting("opnset", "Power On Message",
703
                              RadioSettingValueList(
704
                                  LIST_OPNSET, LIST_OPNSET[_settings.opnset]))
705
        basic.append(opnset)
706

    
707
        def _filter(name):
708
            filtered = ""
709
            for char in str(name):
710
                if char in chirp_common.CHARSET_ASCII:
711
                    filtered += char
712
                else:
713
                    filtered += " "
714
            return filtered
715

    
716
        _msg = self._memobj.poweron_msg
717
        ponmsg = RadioSetting("poweron_msg.line1", "Power-On Message 1",
718
                              RadioSettingValueString(
719
                                  0, 7, _filter(_msg.line1)))
720
        basic.append(ponmsg)
721

    
722

    
723
        scans = RadioSetting("scans", "Scan Mode", RadioSettingValueList(
724
                             LIST_SCANS, LIST_SCANS[_settings.scans]))
725
        basic.append(scans)
726

    
727
        dw = RadioSetting("dw", "FM Radio Dual Watch",
728
                          RadioSettingValueBoolean(_settings.dw))
729
        basic.append(dw)
730

    
731
        name = RadioSetting("name", "Display Names",
732
                            RadioSettingValueBoolean(_settings.name))
733
        basic.append(name)
734

    
735
        rptrl = RadioSetting("rptrl", "Repeater TX Delay", RadioSettingValueList(
736
                             LIST_RPTRL, LIST_RPTRL[_settings.rptrl]))
737
        basic.append(rptrl)
738

    
739
        rptspk = RadioSetting("rptspk", "Repeater Speaker",
740
                              RadioSettingValueBoolean(_settings.rptspk))
741
        basic.append(rptspk)
742

    
743
        rptptt = RadioSetting("rptptt", "Repeater PTT Switch",
744
                            RadioSettingValueBoolean(_settings.rptptt))
745
        basic.append(rptptt)
746

    
747
        rptmod = RadioSetting("rptmod", "Repeater Mode",
748
                              RadioSettingValueList(
749
                                  LIST_RPTMOD, LIST_RPTMOD[_settings.rptmod]))
750
        basic.append(rptmod)
751

    
752
        volmod = RadioSetting("volmod", "Volume Mode",
753
                              RadioSettingValueList(
754
                                  LIST_VOLMOD, LIST_VOLMOD[_settings.volmod]))
755
        basic.append(volmod)
756

    
757
        dst = RadioSetting("dst", "DTMF Side Tone",
758
                            RadioSettingValueBoolean(_settings.dst))
759
        basic.append(dst)
760

    
761
        txsel = RadioSetting("txsel", "Priority TX Channel",
762
                             RadioSettingValueList(
763
                                 LIST_TXSEL, LIST_TXSEL[_settings.txsel]))
764
        basic.append(txsel)
765

    
766
        ste = RadioSetting("ste", "Squelch Tail Eliminate",
767
                           RadioSettingValueBoolean(_settings.ste))
768
        basic.append(ste)
769

    
770
        #advanced
771
        if _settings.pf1 > 0x0A:
772
            val = 0x00
773
        else:
774
            val = _settings.pf1
775
        pf1 = RadioSetting("pf1", "PF1 Key",
776
                           RadioSettingValueList(
777
                               LIST_PFKEY, LIST_PFKEY[val]))
778
        advanced.append(pf1)
779

    
780
        if _settings.pf2 > 0x0A:
781
            val = 0x00
782
        else:
783
            val = _settings.pf2
784
        pf2 = RadioSetting("pf2", "PF2 Key",
785
                           RadioSettingValueList(
786
                               LIST_PFKEY, LIST_PFKEY[val]))
787
        advanced.append(pf2)
788

    
789
        # other
790
        _limit = str(int(_mem.limits.vhf.lower) / 10)
791
        val = RadioSettingValueString(0, 3, _limit)
792
        val.set_mutable(False)
793
        rs = RadioSetting("limits.vhf.lower", "VHF low", val)
794
        other.append(rs)
795

    
796
        _limit = str(int(_mem.limits.vhf.upper) / 10)
797
        val = RadioSettingValueString(0, 3, _limit)
798
        val.set_mutable(False)
799
        rs = RadioSetting("limits.vhf.upper", "VHF high", val)
800
        other.append(rs)
801

    
802
        _limit = str(int(_mem.limits.uhf.lower) / 10)
803
        val = RadioSettingValueString(0, 3, _limit)
804
        val.set_mutable(False)
805
        rs = RadioSetting("limits.uhf.lower", "UHF low", val)
806
        other.append(rs)
807

    
808
        _limit = str(int(_mem.limits.uhf.upper) / 10)
809
        val = RadioSettingValueString(0, 3, _limit)
810
        val.set_mutable(False)
811
        rs = RadioSetting("limits.uhf.upper", "UHF high", val)
812
        other.append(rs)
813

    
814
        #work mode
815
        vfomr_a = RadioSetting("vfomr_a", "Display Mode A",
816
                               RadioSettingValueList(
817
                                   LIST_VFOMR, LIST_VFOMR[_settings.vfomr_a]))
818
        workmode.append(vfomr_a)
819

    
820
        vfomr_b = RadioSetting("vfomr_b", "Display Mode B",
821
                               RadioSettingValueList(
822
                                   LIST_VFOMR, LIST_VFOMR[_settings.vfomr_b]))
823
        workmode.append(vfomr_b)
824

    
825
        mrcha = RadioSetting("mrcha", "Channel # A",
826
                             RadioSettingValueInteger(
827
                                 1, 128, _settings.mrcha))
828
        workmode.append(mrcha)
829

    
830
        mrchb = RadioSetting("mrchb", "Channel # B",
831
                             RadioSettingValueInteger(
832
                                 1, 128, _settings.mrchb))
833
        workmode.append(mrchb)
834

    
835
        #fm radio
836
        vfomr_fm = RadioSetting("vfomr_fm", "FM Radio Display Mode",
837
                                RadioSettingValueList(
838
                                    LIST_VFOMRFM, LIST_VFOMRFM[_settings.vfomr_fm]))
839
        fmradio.append(vfomr_fm)
840

    
841
        fmch = RadioSetting("fmch", "FM Radio Channel #",
842
                            RadioSettingValueInteger(
843
                                 1, 25, _settings.fmch))
844
        fmradio.append(fmch)
845

    
846
        return top
847

    
848
    def set_settings(self, settings):
849
        for element in settings:
850
            if not isinstance(element, RadioSetting):
851
                self.set_settings(element)
852
                continue
853
            else:
854
                try:
855
                    if "." in element.get_name():
856
                        bits = element.get_name().split(".")
857
                        obj = self._memobj
858
                        for bit in bits[:-1]:
859
                            obj = getattr(obj, bit)
860
                        setting = bits[-1]
861
                    else:
862
                        obj = self._memobj.settings
863
                        setting = element.get_name()
864

    
865
                    if element.has_apply_callback():
866
                        LOG.debug("Using apply callback")
867
                        element.run_apply_callback()
868
                    elif setting == "color":
869
                        setattr(obj, setting, int(element.value) + 1)
870
                    elif element.value.get_mutable():
871
                        LOG.debug("Setting %s = %s" % (setting, element.value))
872
                        setattr(obj, setting, element.value)
873
                except Exception, e:
874
                    LOG.debug(element.get_name())
875
                    raise
876

    
877
    @classmethod
878
    def match_model(cls, filedata, filename):
879
        match_size = False
880
        match_model = False
881

    
882
        # testing the file data size
883
        if len(filedata) in [0x1000, ]:
884
            match_size = True
885

    
886
        # testing the model fingerprint
887
        match_model = model_match(cls, filedata)
888

    
889
        if match_size and match_model:
890
            return True
891
        else:
892
            return False