Project

General

Profile

Bug #2377 » kguv8d.py

As supplied by Jim Unroe - Stan White, 03/01/2015 02:08 PM

 
1
# Copyright 2014 Ron Wellsted <ron@wellsted.org.uk> M0RNW
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 3 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
"""Wouxun KG-UV8D radio management module"""
17

    
18
import time
19
import os
20
from chirp import util, chirp_common, bitwise, memmap, errors, directory
21
from chirp.settings import RadioSetting, RadioSettingGroup, \
22
                RadioSettingValueBoolean, RadioSettingValueList, \
23
                RadioSettingValueInteger, RadioSettingValueString
24

    
25
if os.getenv("CHIRP_DEBUG"):
26
    CHIRP_DEBUG = True
27
else:
28
    CHIRP_DEBUG = False
29

    
30
CMD_ID = 128
31
CMD_END = 129
32
CMD_RD = 130
33
CMD_WR = 131
34

    
35
MEM_VALID = 158
36

    
37
AB_LIST = ["A", "B"]
38
STEPS = [5.0, 6.25, 10.0, 12.5, 25.0, 50.0, 100.0]
39
STEP_LIST = [str(x) for x in STEPS]
40
ROGER_LIST = ["Off", "BOT", "EOT", "Both"]
41
TIMEOUT_LIST = ["Off"] + [str(x) + "s" for x in range(15, 901, 15)]
42
VOX_LIST = ["Off"] + ["%s" % x for x in range(1, 10)]
43
BANDWIDTH_LIST = ["Narrow", "Wide"]
44
VOICE_LIST = ["Off", "On"]
45
LANGUAGE_LIST = ["Chinese", "English"]
46
SCANMODE_LIST = ["TO", "CO", "SE"]
47
PF1KEY_LIST = ["Call", "VFTX"]
48
PF3KEY_LIST = ["Scan", "Lamp", "Tele Alarm", "SOS-CH", "Radio", "Disable"]
49
WORKMODE_LIST = ["VFO", "Channel No.", "Frequency + No", "Name"]
50
BACKLIGHT_LIST = ["Always On"] + [str(x) + "s" for x in range(1, 21)] + \
51
                ["Always Off"]
52
OFFSET_LIST = ["+", "-"]
53
PONMSG_LIST = ["Bitmap", "Battery Volts"]
54
SPMUTE_LIST = ["QT", "QT+DTMF", "QT*DTMF"]
55
DTMFST_LIST = ["DT-ST", "ANI-ST", "DT-ANI", "Off"]
56
RPTSET_LIST = ["X-DIRPT", "X-TWRPT"]
57
ALERTS = [1750, 2100, 1000, 1450]
58
ALERTS_LIST = [str(x) for x in ALERTS]
59
PTTID_LIST = ["BOT", "EOT", "Both"]
60
LIST_10 = ["Off"] + ["%s" % x for x in range(1, 11)]
61
SCANGRP_LIST = ["All"] + ["%s" % x for x in range(1, 11)]
62
SCQT_LIST = ["All", "Decoder", "Encoder"]
63
SMUTESET_LIST = ["Off", "Tx", "Rx", "Tx/Rx"]
64
POWER_LIST = ["Lo", "Hi"]
65

    
66
# memory slot 0 is not used, start at 1 (so need 1000 slots, not 999)
67
# structure elements whose name starts with x are currently unidentified
68
_MEM_FORMAT = """
69
    #seekto 0x0044;
70
    struct {
71
        u32    rx_start;
72
        u32    rx_stop;
73
        u32    tx_start;
74
        u32    tx_stop;
75
    } uhf_limits;
76

    
77
    #seekto 0x0054;
78
    struct {
79
        u32    rx_start;
80
        u32    rx_stop;
81
        u32    tx_start;
82
        u32    tx_stop;
83
    } vhf_limits;
84

    
85
    #seekto 0x0400;
86
    struct {
87
        char    model[8];
88
        u8      unknown[2];
89
        char    oem1[10];
90
        char    oem2[10];
91
        char    unknown2[8];
92
        char    version[10];
93
        u8      unknown3[6];
94
        char    date[8];
95
    } oem_info;
96

    
97
    #seekto 0x0480;
98
    struct {
99
        u16    lower;
100
        u16    upper;
101
    } scan_groups[10];
102

    
103
    #seekto 0x0500;
104
    struct {
105
        u8    call_code[6];
106
    } call_groups[20];
107

    
108
    #seekto 0x0580;
109
    struct {
110
        char    call_name[6];
111
    } call_group_name[20];
112

    
113
    #seekto 0x0800;
114
    struct {
115
        u8      ponmsg;
116
        char    dispstr[15];
117
        u8 x0810;
118
        u8 x0811;
119
        u8 x0812;
120
        u8 x0813;
121
        u8 x0814;
122
        u8      voice;
123
        u8      timeout;
124
        u8      toalarm;
125
        u8      channel_menu;
126
        u8      power_save;
127
        u8      autolock;
128
        u8      keylock;
129
        u8      beep;
130
        u8      stopwatch;
131
        u8      vox;
132
        u8      scan_rev;
133
        u8      backlight;
134
        u8      roger_beep;
135
        u8      mode_sw_pwd[6];
136
        u8      reset_pwd[6];
137
        u16     pri_ch;
138
        u8      ani_sw;
139
        u8      ptt_delay;
140
        u8      ani[6];
141
        u8      dtmf_st;
142
        u8      bcl_a;
143
        u8      bcl_b;
144
        u8      ptt_id;
145
        u8      prich_sw;
146
        u8      rpt_set;
147
        u8      rpt_spk;
148
        u8      rpt_ptt;
149
        u8      alert;
150
        u8      pf1_func;
151
        u8      pf3_func;
152
        u8      workmode_a;
153
        u8      workmode_b;
154
        u8 x0845;
155
        u8      dtmf_tx_time;
156
        u8      dtmf_interval;
157
        u8      main_ab;
158
        u16     work_cha;
159
        u16     work_chb;
160
        u8 x084d;
161
        u8 x084e;
162
        u8 x084f;
163
        u8 x0850;
164
        u8 x0851;
165
        u8 x0852;
166
        u8 x0853;
167
        u8 x0854;
168
        u8      rpt_mode;
169
        u8      language;
170
        u8 x0857;
171
        u8 x0858;
172
        u8 x0859;
173
        u8 x085a;
174
        u8 x085b;
175
        u8 x085c;
176
        u8 x085d;
177
        u8 x085e;
178
        u8      single_display;
179
        u8      ring;
180
        u8      scg_a;
181
        u8      scg_b;
182
        u8 x0863;
183
        u8      rpt_tone;
184
        u8      rpt_hold;
185
        u8      scan_det;
186
        u8      sc_qt;
187
        u8 x0868;
188
        u8      smuteset;
189
        u8      callcode;
190
    } settings;
191

    
192
    #seekto 0x0880;
193
    struct {
194
        u32     rxfreq;
195
        u32     txoffset;
196
        u16     rxtone;
197
        u16     txtone;
198
        u8      unknown1:6,
199
                power:1,
200
                unknown2:1;
201
        u8      unknown3:1,
202
                shift_dir:2
203
                unknown4:2,
204
                mute_mode:2,
205
                iswide:1;
206
        u8      step;
207
        u8      squelch;
208
      } vfoa;
209

    
210
    #seekto 0x08c0;
211
    struct {
212
        u32     rxfreq;
213
        u32     txoffset;
214
        u16     rxtone;
215
        u16     txtone;
216
        u8      unknown1:6,
217
                power:1,
218
                unknown2:1;
219
        u8      unknown3:1,
220
                shift_dir:2
221
                unknown4:2,
222
                mute_mode:2,
223
                iswide:1;
224
        u8      step;
225
        u8      squelch;
226
    } vfob;
227

    
228
    #seekto 0x0900;
229
    struct {
230
        u32     rxfreq;
231
        u32     txfreq;
232
        u16     rxtone;
233
        u16     txtone;
234
        u8      unknown1:6,
235
                power:1,
236
                unknown2:1;
237
        u8      unknown3:2,
238
                scan_add:1,
239
                unknown4:2,
240
                mute_mode:2,
241
                iswide:1;
242
        u16     padding;
243
    } memory[1000];
244

    
245
    #seekto 0x4780;
246
    struct {
247
        u8    name[8];
248
    } names[1000];
249

    
250
    #seekto 0x6700;
251
    u8          valid[1000];
252
    """
253

    
254
# Support for the Wouxun KG-UV8D radio
255
# Serial coms are at 19200 baud
256
# The data is passed in variable length records
257
# Record structure:
258
#  Offset   Usage
259
#    0      start of record (\x7d)
260
#    1      Command (\x80 Identify \x81 End/Reboot \x82 Read \x83 Write)
261
#    2      direction (\xff PC-> Radio, \x00 Radio -> PC)
262
#    3      length of payload (excluding header/checksum) (n)
263
#    4      payload (n bytes)
264
#    4+n+1  checksum - byte sum (% 256) of bytes 1 -> 4+n
265
#
266
# Memory Read Records:
267
# the payload is 3 bytes, first 2 are offset (big endian),
268
# 3rd is number of bytes to read
269
# Memory Write Records:
270
# the maximum payload size (from the Wouxun software) seems to be 66 bytes
271
#  (2 bytes location + 64 bytes data).
272

    
273

    
274
@directory.register
275
class KGUV8DRadio(chirp_common.CloneModeRadio,
276
    chirp_common.ExperimentalRadio):
277
    """Wouxun KG-UV8D"""
278
    VENDOR = "Wouxun"
279
    MODEL = "KG-UV8D"
280
    _model = "KG-UV8D"
281
    _file_ident = "KGUV8D"
282
    BAUD_RATE = 19200
283
    POWER_LEVELS = [chirp_common.PowerLevel("L", watts=1),
284
                chirp_common.PowerLevel("H", watts=5)]
285
    _mmap = ""
286

    
287
    def _checksum(self, data):
288
        cs = 0
289
        for byte in data:
290
            cs += ord(byte)
291
        return cs % 256
292

    
293
    def _write_record(self, cmd, payload=None):
294
        # build the packet
295
        _packet = '\x7d' + chr(cmd) + '\xff'
296
        _length = 0
297
        if payload:
298
            _length = len(payload)
299
        # update the length field
300
        _packet += chr(_length)
301
        if payload:
302
            # add the chars to the packet
303
            _packet += payload
304
        # calculate and add the checksum to the packet
305
        _packet += chr(self._checksum(_packet[1:]))
306
        if CHIRP_DEBUG:
307
            print "Sent:\n%s" % util.hexprint(_packet)
308
        self.pipe.write(_packet)
309

    
310
    def _read_record(self):
311
        # read 4 chars for the header
312
        _header = self.pipe.read(4)
313
        _length = ord(_header[3])
314
        _packet = self.pipe.read(_length)
315
        _cs = self._checksum(_header[1:])
316
        _cs += self._checksum(_packet)
317
        _cs %= 256
318
        _rcs = ord(self.pipe.read(1))
319
        if CHIRP_DEBUG:
320
            print "_cs =", _cs
321
            print "_rcs=", _rcs
322
        return (_rcs != _cs, _packet)
323

    
324
# Identify the radio
325
#
326
# A Gotcha: the first identify packet returns a bad checksum, subsequent
327
# attempts return the correct checksum... (well it does on my radio!)
328
#
329
# The ID record returned by the radio also includes the current frequency range
330
# as 4 bytes big-endian in 10Hz increments
331
#
332
# Offset
333
#  0:10     Model, zero padded (Use first 7 chars for 'KG-UV8D')
334
#  11:14    UHF rx lower limit (in units of 10Hz)
335
#  15:18    UHF rx upper limit
336
#  19:22    UHF tx lower limit
337
#  23:26    UHF tx upper limit
338
#  27:30    VHF rx lower limit
339
#  31:34    VHF rx upper limit
340
#  35:38    VHF tx lower limit
341
#  39:42    VHF tx upper limit
342
##
343
    @classmethod
344
    def match_model(cls, filedata, filename):
345
        return cls._file_ident in filedata[0x400:0x408]
346

    
347
    def _identify(self):
348
        """Do the identification dance"""
349
        for _i in range(0, 10):
350
            self._write_record(CMD_ID)
351
            _chksum_err, _resp = self._read_record()
352
            if CHIRP_DEBUG:
353
                print "Got:\n%s" % util.hexprint(_resp)
354
            if _chksum_err:
355
                print "Checksum error: retrying ident..."
356
                time.sleep(0.100)
357
                continue
358
            if CHIRP_DEBUG:
359
                print "Model %s" % util.hexprint(_resp[0:7])
360
            if _resp[0:7] == self._model:
361
                return
362
            if len(_resp) == 0:
363
                raise Exception("Radio not responding")
364
            else:
365
                raise Exception("Unable to identify radio")
366

    
367
    def _finish(self):
368
        self._write_record(CMD_END)
369

    
370
    def process_mmap(self):
371
        self._memobj = bitwise.parse(_MEM_FORMAT, self._mmap)
372

    
373
    def sync_in(self):
374
        try:
375
            self._mmap = self._download()
376
        except errors.RadioError:
377
            raise
378
        except Exception, e:
379
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
380
        self.process_mmap()
381

    
382
    def sync_out(self):
383
        self._upload()
384

    
385
    # TODO: This is a dumb, brute force method of downlolading the memory.
386
    # it would be smarter to only load the active areas and none of
387
    # the padding/unused areas.
388
    def _download(self):
389
        """Talk to a wouxun KG-UV8D and do a download"""
390
        try:
391
            self._identify()
392
            return self._do_download(0, 32768, 64)
393
        except errors.RadioError:
394
            raise
395
        except Exception, e:
396
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
397

    
398
    def _do_download(self, start, end, blocksize):
399
        # allocate & fill memory
400
        image = ""
401
        for i in range(start, end, blocksize):
402
            req = chr(i / 256) + chr(i % 256) + chr(blocksize)
403
            self._write_record(CMD_RD, req)
404
            cs_error, resp = self._read_record()
405
            if cs_error:
406
                # TODO: probably should retry a few times here
407
                print util.hexprint(resp)
408
                raise Exception("Checksum error on read")
409
            if CHIRP_DEBUG:
410
                print "Got:\n%s" % util.hexprint(resp)
411
            image += resp[2:]
412
            if self.status_fn:
413
                status = chirp_common.Status()
414
                status.cur = i
415
                status.max = end
416
                status.msg = "Cloning from radio"
417
                self.status_fn(status)
418
        self._finish()
419
        return memmap.MemoryMap(''.join(image))
420

    
421
    def _upload(self):
422
        """Talk to a wouxun KG-UV8D and do a upload"""
423
        try:
424
            self._identify()
425
            self._do_upload(0, 32768, 64)
426
        except errors.RadioError:
427
            raise
428
        except Exception, e:
429
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
430
        return
431

    
432
    def _do_upload(self, start, end, blocksize):
433
        ptr = start
434
        for i in range(start, end, blocksize):
435
            req = chr(i / 256) + chr(i % 256)
436
            chunk = self.get_mmap()[ptr:ptr + blocksize]
437
            self._write_record(CMD_WR, req + chunk)
438
            if CHIRP_DEBUG:
439
                print util.hexprint(req + chunk)
440
            cserr, ack = self._read_record()
441
            if CHIRP_DEBUG:
442
                print util.hexprint(ack)
443
            j = ord(ack[0]) * 256 + ord(ack[1])
444
            if cserr or j != ptr:
445
                raise Exception("Radio did not ack block %i" % ptr)
446
            ptr += blocksize
447
            if self.status_fn:
448
                status = chirp_common.Status()
449
                status.cur = i
450
                status.max = end
451
                status.msg = "Cloning to radio"
452
                self.status_fn(status)
453
        self._finish()
454

    
455
    def get_features(self):
456
        # TODO: This probably needs to be setup correctly to match the true
457
        # features of the radio
458
        rf = chirp_common.RadioFeatures()
459
        rf.has_settings = True
460
        rf.has_ctone = True
461
        rf.has_rx_dtcs = True
462
        rf.has_cross = True
463
        rf.has_tuning_step = False
464
        rf.has_bank = False
465
        rf.can_odd_split = True
466
        rf.valid_skips = ["", "S"]
467
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
468
        rf.valid_cross_modes = [
469
                        "Tone->Tone",
470
                        "Tone->DTCS",
471
                        "DTCS->Tone",
472
                        "DTCS->",
473
                        "->Tone",
474
                        "->DTCS",
475
                        "DTCS->DTCS",
476
                    ]
477
        rf.valid_modes = ["FM", "NFM"]
478
        rf.valid_power_levels = self.POWER_LEVELS
479
        rf.valid_name_length = 8
480
        rf.valid_duplexes = ["", "+", "-", "split"]
481
        rf.valid_bands = [(134000000, 175000000),  # supports 2m
482
                          (400000000, 520000000)]  # supports 70cm
483
        rf.valid_characters = chirp_common.CHARSET_ASCII
484
        rf.memory_bounds = (1, 999)  # 999 memories
485
        return rf
486

    
487
    @classmethod
488
    def get_prompts(cls):
489
        rp = chirp_common.RadioPrompts()
490
        rp.experimental = ("This radio driver is currently under development. "
491
                           "There are no known issues with it, but you should "
492
                           "proceed with caution.")
493
        return rp
494

    
495
    def get_raw_memory(self, number):
496
        return repr(self._memobj.memory[number])
497

    
498
    def _get_tone(self, _mem, mem):
499
        def _get_dcs(val):
500
            code = int("%03o" % (val & 0x07FF))
501
            pol = (val & 0x8000) and "R" or "N"
502
            return code, pol
503

    
504
        tpol = False
505
        if _mem.txtone != 0xFFFF and (_mem.txtone & 0x2800) == 0x2800:
506
            tcode, tpol = _get_dcs(_mem.txtone)
507
            mem.dtcs = tcode
508
            txmode = "DTCS"
509
        elif _mem.txtone != 0xFFFF and _mem.txtone != 0x0:
510
            mem.rtone = (_mem.txtone & 0x7fff) / 10.0
511
            txmode = "Tone"
512
        else:
513
            txmode = ""
514

    
515
        rpol = False
516
        if _mem.rxtone != 0xFFFF and (_mem.rxtone & 0x2800) == 0x2800:
517
            rcode, rpol = _get_dcs(_mem.rxtone)
518
            mem.rx_dtcs = rcode
519
            rxmode = "DTCS"
520
        elif _mem.rxtone != 0xFFFF and _mem.rxtone != 0x0:
521
            mem.ctone = (_mem.rxtone & 0x7fff) / 10.0
522
            rxmode = "Tone"
523
        else:
524
            rxmode = ""
525

    
526
        if txmode == "Tone" and not rxmode:
527
            mem.tmode = "Tone"
528
        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
529
            mem.tmode = "TSQL"
530
        elif txmode == rxmode and txmode == "DTCS" and mem.dtcs == mem.rx_dtcs:
531
            mem.tmode = "DTCS"
532
        elif rxmode or txmode:
533
            mem.tmode = "Cross"
534
            mem.cross_mode = "%s->%s" % (txmode, rxmode)
535

    
536
        # always set it even if no dtcs is used
537
        mem.dtcs_polarity = "%s%s" % (tpol or "N", rpol or "N")
538

    
539
        if os.getenv("CHIRP_DEBUG"):
540
            print "Got TX %s (%i) RX %s (%i)" % (txmode, _mem.txtone,
541
                                                 rxmode, _mem.rxtone)
542

    
543
    def get_memory(self, number):
544
        _mem = self._memobj.memory[number]
545
        _nam = self._memobj.names[number]
546

    
547
        mem = chirp_common.Memory()
548
        mem.number = number
549
        _valid = self._memobj.valid[mem.number]
550

    
551
        if CHIRP_DEBUG:
552
            print number, _valid == MEM_VALID
553
        if _valid != MEM_VALID:
554
            mem.empty = True
555
            return mem
556
        else:
557
            mem.empty = False
558

    
559
        mem.freq = int(_mem.rxfreq) * 10
560

    
561
        if int(_mem.rxfreq) == int(_mem.txfreq):
562
            mem.duplex = ""
563
            mem.offset = 0
564
        elif abs(int(_mem.rxfreq) * 10 - int(_mem.txfreq) * 10) > 70000000:
565
            mem.duplex = "split"
566
            mem.offset = int(_mem.txfreq) * 10
567
        else:
568
            mem.duplex = int(_mem.rxfreq) > int(_mem.txfreq) and "-" or "+"
569
            mem.offset = abs(int(_mem.rxfreq) - int(_mem.txfreq)) * 10
570

    
571
        for char in _nam.name:
572
            if char != 0:
573
                mem.name += chr(char)
574
        mem.name = mem.name.rstrip()
575

    
576
        self._get_tone(_mem, mem)
577

    
578
        mem.skip = "" if bool(_mem.scan_add) else "S"
579

    
580
        mem.power = self.POWER_LEVELS[_mem.power]
581
        mem.mode = _mem.iswide and "FM" or "NFM"
582
        return mem
583

    
584
    def _set_tone(self, mem, _mem):
585
        def _set_dcs(code, pol):
586
            val = int("%i" % code, 8) + 0x2800
587
            if pol == "R":
588
                val += 0x8000
589
            return val
590

    
591
        if mem.tmode == "Cross":
592
            tx_mode, rx_mode = mem.cross_mode.split("->")
593
        elif mem.tmode == "Tone":
594
            tx_mode = mem.tmode
595
            rx_mode = None
596
        else:
597
            tx_mode = rx_mode = mem.tmode
598

    
599
        if tx_mode == "DTCS":
600
            _mem.txtone = mem.tmode != "DTCS" and \
601
                _set_dcs(mem.dtcs, mem.dtcs_polarity[0]) or \
602
                _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[0])
603
            _mem.txtone += 0x4000
604
        elif tx_mode:
605
            _mem.txtone = tx_mode == "Tone" and \
606
                int(mem.rtone * 10) or int(mem.ctone * 10)
607
            _mem.txtone += 0x8000
608
        else:
609
            _mem.txtone = 0
610

    
611
        if rx_mode == "DTCS":
612
            _mem.rxtone = _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[1])
613
            _mem.rxtone += 0x4000
614
        elif rx_mode:
615
            _mem.rxtone = int(mem.ctone * 10)
616
            _mem.rxtone += 0x8000
617
        else:
618
            _mem.rxtone = 0
619

    
620
        if CHIRP_DEBUG:
621
            print "Set TX %s (%i) RX %s (%i)" % (tx_mode, _mem.txtone,
622
                                                 rx_mode, _mem.rxtone)
623

    
624
    def set_memory(self, mem):
625
        number = mem.number
626

    
627
        _mem = self._memobj.memory[number]
628
        _nam = self._memobj.names[number]
629

    
630
        if mem.empty:
631
            _mem.set_raw("\x00" * (_mem.size() / 8))
632
            self._memobj.valid[number] = 0
633
            self._memobj.names[number].set_raw("\x00" * (_nam.size() / 8))
634
            return
635

    
636
        _mem.rxfreq = int(mem.freq / 10)
637
        if mem.duplex == "split":
638
            _mem.txfreq = int(mem.offset / 10)
639
        elif mem.duplex == "off":
640
            for i in range(0, 4):
641
                _mem.txfreq[i].set_raw("\xFF")
642
        elif mem.duplex == "+":
643
            _mem.txfreq = int(mem.freq / 10) + int(mem.offset / 10)
644
        elif mem.duplex == "-":
645
            _mem.txfreq = int(mem.freq / 10) - int(mem.offset / 10)
646
        else:
647
            _mem.txfreq = int(mem.freq / 10)
648
        _mem.scan_add = int(mem.skip != "S")
649
        _mem.iswide = int(mem.mode == "FM")
650
        # set the tone
651
        self._set_tone(mem, _mem)
652
        # set the power
653
        if mem.power:
654
            _mem.power = self.POWER_LEVELS.index(mem.power)
655
        else:
656
            _mem.power = True
657
        # TODO: sett the correct mute mode, for now just
658
        # set to mute mode to QT (not QT+DTMF or QT*DTMF)
659
        _mem.mute_mode = 0
660

    
661
        for i in range(0, len(_nam.name)):
662
            if i < len(mem.name) and mem.name[i]:
663
                _nam.name[i] = ord(mem.name[i])
664
            else:
665
                _nam.name[i] = 0x0
666
        self._memobj.valid[mem.number] = MEM_VALID
667

    
668
    def _get_settings(self):
669
        _settings = self._memobj.settings
670
        _vfoa = self._memobj.vfoa
671
        _vfob = self._memobj.vfob
672
        cfg_grp = RadioSettingGroup("cfg_grp", "Configuration Settings")
673
        vfoa_grp = RadioSettingGroup("vfoa_grp", "VFO A Settings")
674
        vfob_grp = RadioSettingGroup("vfob_grp", "VFO B Settings")
675
        key_grp = RadioSettingGroup("key_grp", "Key Settings")
676
        scn_grp = RadioSettingGroup("scn_grp", "Scan Group")
677
        cal_grp = RadioSettingGroup("cal_grp", "Call Group")
678
        lmt_grp = RadioSettingGroup("lmt_grp", "Frequency Limits")
679
        oem_grp = RadioSettingGroup("oem_grp", "OEM Info")
680
        group = RadioSettingGroup("top", "All Settings", cfg_grp, vfoa_grp,
681
                        vfob_grp, key_grp, scn_grp, cal_grp, lmt_grp, oem_grp)
682

    
683
        #
684
        # Configuration Settings
685
        #
686
        rs = RadioSetting("ponmsg", "Poweron message", RadioSettingValueList(
687
                        PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
688
        cfg_grp.append(rs)
689
        rs = RadioSetting("voice", "Voice Guide", RadioSettingValueBoolean(
690
                            _settings.voice))
691
        cfg_grp.append(rs)
692
        rs = RadioSetting("language", "Language", RadioSettingValueList(
693
                        LANGUAGE_LIST, LANGUAGE_LIST[_settings.language]))
694
        cfg_grp.append(rs)
695
        rs = RadioSetting("timeout", "Timeout Timer", RadioSettingValueInteger(
696
                        15, 900, _settings.timeout * 15, 15))
697
        cfg_grp.append(rs)
698
        rs = RadioSetting("toalarm", "Timeout Alarm",
699
                        RadioSettingValueInteger(0, 10, _settings.toalarm))
700
        cfg_grp.append(rs)
701
        rs = RadioSetting("channel_menu", "Menu available in channel mode",
702
                            RadioSettingValueBoolean(_settings.channel_menu))
703
        cfg_grp.append(rs)
704
        rs = RadioSetting("power_save", "Power save", RadioSettingValueBoolean(
705
                            _settings.power_save))
706
        cfg_grp.append(rs)
707
        rs = RadioSetting("autolock", "Autolock", RadioSettingValueBoolean(
708
                            _settings.autolock))
709
        cfg_grp.append(rs)
710
        rs = RadioSetting("keylock", "Keypad Lock", RadioSettingValueBoolean(
711
                            _settings.keylock))
712
        cfg_grp.append(rs)
713
        rs = RadioSetting("beep", "Keypad Beep", RadioSettingValueBoolean(
714
                            _settings.keylock))
715
        cfg_grp.append(rs)
716
        rs = RadioSetting("stopwatch", "Stopwatch", RadioSettingValueBoolean(
717
                            _settings.keylock))
718
        cfg_grp.append(rs)
719

    
720
        #
721
        # VFO A Settings
722
        #
723
        rs = RadioSetting("vfoa_mode", "VFO A Workmode", RadioSettingValueList(
724
                        WORKMODE_LIST, WORKMODE_LIST[_settings.workmode_a]))
725
        vfoa_grp.append(rs)
726
        rs = RadioSetting("vfoa_chan", "VFO A Channel",
727
                        RadioSettingValueInteger(1, 999, _settings.work_cha))
728
        vfoa_grp.append(rs)
729
        rs = RadioSetting("rxfreqa", "VFO A Rx Frequency",
730
                        RadioSettingValueInteger(134000000, 520000000,
731
                            _vfoa.rxfreq * 10, 5000))
732
        vfoa_grp.append(rs)
733
        #   u32   txoffset;
734
        #   u16   rxtone;
735
        #   u16   txtone;
736
        #   u8    unknown1:6,
737
        rs = RadioSetting("vfoa_power", "VFO A Power", RadioSettingValueList(
738
                        POWER_LIST, POWER_LIST[_vfoa.power]))
739
        vfoa_grp.append(rs)
740
        #         unknown2:1;
741
        #   u8    unknown3:1,
742
        #         shift_dir:2
743
        #         unknown4:2,
744
        rs = RadioSetting("vfoa_mute_mode", "VFO A Mute", RadioSettingValueList(
745
                        SPMUTE_LIST, SPMUTE_LIST[_vfoa.mute_mode]))
746
        vfoa_grp.append(rs)
747
        rs = RadioSetting("vfoa_iswide", "VFO A NBFM", RadioSettingValueList(
748
                        BANDWIDTH_LIST, BANDWIDTH_LIST[_vfoa.iswide]))
749
        vfoa_grp.append(rs)
750
        rs = RadioSetting("vfoa_step", "VFO A Step (kHz)",
751
                        RadioSettingValueList(STEP_LIST, STEP_LIST[_vfoa.step]))
752
        vfoa_grp.append(rs)
753
        rs = RadioSetting("vfoa_squelch", "VFO A Squelch",
754
                        RadioSettingValueList(LIST_10, LIST_10[_vfoa.squelch]))
755
        vfoa_grp.append(rs)
756
        #
757
        # VFO B Settings
758
        #
759
        rs = RadioSetting("vfob_mode", "VFO B Workmode", RadioSettingValueList(
760
                        WORKMODE_LIST, WORKMODE_LIST[_settings.workmode_b]))
761
        vfob_grp.append(rs)
762
        rs = RadioSetting("vfob_chan", "VFO B Channel",
763
                        RadioSettingValueInteger(1, 999, _settings.work_chb))
764
        vfob_grp.append(rs)
765
        rs = RadioSetting("rxfreqb", "VFO B Rx Frequency",
766
                        RadioSettingValueInteger(134000000, 520000000,
767
                            _vfob.rxfreq * 10, 5000))
768
        vfob_grp.append(rs)
769
        #   u32   txoffset;
770
        #   u16   rxtone;
771
        #   u16   txtone;
772
        #   u8    unknown1:6,
773
        rs = RadioSetting("vfob_power", "VFO B Power", RadioSettingValueList(
774
                        POWER_LIST, POWER_LIST[_vfob.power]))
775
        vfob_grp.append(rs)
776
        #         unknown2:1;
777
        #   u8    unknown3:1,
778
        #         shift_dir:2
779
        #         unknown4:2,
780
        rs = RadioSetting("vfob_mute_mode", "VFO B Mute", RadioSettingValueList(
781
                        SPMUTE_LIST, SPMUTE_LIST[_vfob.mute_mode]))
782
        vfob_grp.append(rs)
783
        rs = RadioSetting("vfob_iswide", "VFO B NBFM", RadioSettingValueList(
784
                        BANDWIDTH_LIST, BANDWIDTH_LIST[_vfob.iswide]))
785
        vfob_grp.append(rs)
786
        rs = RadioSetting("vfob_step", "VFO B Step (kHz)",
787
                        RadioSettingValueList(STEP_LIST, STEP_LIST[_vfob.step]))
788
        vfob_grp.append(rs)
789
        rs = RadioSetting("vfob_squelch", "VFO B Squelch",
790
                        RadioSettingValueList(LIST_10, LIST_10[_vfob.squelch]))
791
        vfob_grp.append(rs)
792

    
793

    
794
        #
795
        # Key Settings
796
        #
797
        _msg = str(_settings.dispstr).split("\0")[0]
798
        val = RadioSettingValueString(0, 15, _msg)
799
        val.set_mutable(True)
800
        rs = RadioSetting("dispstr", "Display Message", val)
801
        key_grp.append(rs)
802
        _ani = ""
803
        for i in _settings.ani:
804
            if i < 10:
805
                _ani += chr(i + 0x30)
806
            else:
807
                break
808
        val = RadioSettingValueString(0, 6, _ani)
809
        val.set_mutable(True)
810
        rs = RadioSetting("ani", "ANI code", val)
811
        key_grp.append(rs)
812
        rs = RadioSetting("pf1_func", "PF1 Key function", RadioSettingValueList(
813
                            PF1KEY_LIST,
814
                            PF1KEY_LIST[self._memobj.settings.pf1_func]))
815
        key_grp.append(rs)
816
        rs = RadioSetting("pf3_func", "PF3 Key function", RadioSettingValueList(
817
                            PF3KEY_LIST,
818
                            PF3KEY_LIST[self._memobj.settings.pf3_func]))
819
        key_grp.append(rs)
820

    
821
        #
822
        # Scan Group Settings
823
        #
824
        # settings:
825
        #   u8    scg_a;
826
        #   u8    scg_b;
827
        #
828
        #   struct {
829
        #       u16    lower;
830
        #       u16    upper;
831
        #   } scan_groups[10];
832

    
833

    
834
        #
835
        # Call group settings
836
        #
837

    
838
        #
839
        # Limits settings
840
        #
841
        rs = RadioSetting("urx_start", "UHF RX Lower Limit",
842
                            RadioSettingValueInteger(400000000, 520000000,
843
                                self._memobj.uhf_limits.rx_start * 10, 5000))
844
        lmt_grp.append(rs)
845
        rs = RadioSetting("urx_stop", "UHF RX Upper Limit",
846
                            RadioSettingValueInteger(400000000, 520000000,
847
                                self._memobj.uhf_limits.rx_stop * 10, 5000))
848
        lmt_grp.append(rs)
849
        rs = RadioSetting("utx_start", "UHF TX Lower Limit",
850
                            RadioSettingValueInteger(400000000, 520000000,
851
                                self._memobj.uhf_limits.tx_start * 10, 5000))
852
        lmt_grp.append(rs)
853
        rs = RadioSetting("utx_stop", "UHF TX Upper Limit",
854
                            RadioSettingValueInteger(400000000, 520000000,
855
                                self._memobj.uhf_limits.tx_stop * 10, 5000))
856
        lmt_grp.append(rs)
857
        rs = RadioSetting("vrx_start", "VHF RX Lower Limit",
858
                            RadioSettingValueInteger(134000000, 174997500,
859
                                self._memobj.vhf_limits.rx_start * 10, 5000))
860
        lmt_grp.append(rs)
861
        rs = RadioSetting("vrx_stop", "VHF RX Upper Limit",
862
                            RadioSettingValueInteger(134000000, 174997500,
863
                                self._memobj.vhf_limits.rx_stop * 10, 5000))
864
        lmt_grp.append(rs)
865
        rs = RadioSetting("vtx_start", "VHF TX Lower Limit",
866
                            RadioSettingValueInteger(134000000, 174997500,
867
                                self._memobj.vhf_limits.tx_start * 10, 5000))
868
        lmt_grp.append(rs)
869
        rs = RadioSetting("vtx_stop", "VHF TX Upper Limit",
870
                            RadioSettingValueInteger(134000000, 174997500,
871
                                self._memobj.vhf_limits.tx_stop * 10, 5000))
872
        lmt_grp.append(rs)
873

    
874
        #
875
        # OEM info
876
        #
877
        _str = str(self._memobj.oem_info.model).split("\0")[0]
878
        val = RadioSettingValueString(0, 15, _str)
879
        val.set_mutable(False)
880
        rs = RadioSetting("model", "Model", val)
881
        oem_grp.append(rs)
882
        #_str = str(self._memobj.oem_info.oem1).split("\0")[0]
883
        #val = RadioSettingValueString(0, 15, _str)
884
        #val.set_mutable(False)
885
        #rs = RadioSetting("oem1", "OEM String 1", val)
886
        #oem_grp.append(rs)
887
        #_str = str(self._memobj.oem_info.oem2).split("\0")[0]
888
        #val = RadioSettingValueString(0, 15, _str)
889
        #val.set_mutable(False)
890
        #rs = RadioSetting("oem2", "OEM String 2", val)
891
        #oem_grp.append(rs)
892
        _str = str(self._memobj.oem_info.version).split("\0")[0]
893
        val = RadioSettingValueString(0, 15, _str)
894
        val.set_mutable(False)
895
        rs = RadioSetting("version", "Software Version", val)
896
        oem_grp.append(rs)
897
        _str = str(self._memobj.oem_info.date).split("\0")[0]
898
        val = RadioSettingValueString(0, 15, _str)
899
        val.set_mutable(False)
900
        rs = RadioSetting("date", "OEM Date", val)
901
        oem_grp.append(rs)
902

    
903
        return group
904

    
905
    def get_settings(self):
906
        try:
907
            return self._get_settings()
908
        except:
909
            import traceback
910
            print "Failed to parse settings:"
911
            traceback.print_exc()
912
            return None
(1-1/3)