Project

General

Profile

New Model #9489 » kg935g MRT test b1.5.py

Mel Terechenok, 08/07/2022 05:55 PM

 
1
# MRT Beta 1.5 - Corrected minor typos found in previous versions
2
#              - Made pri_ch,work_ch_a,work_ch_b U16 instead of U8 to fix errors when specifying channel values > 255
3
#              - Changed node_sw_pwd to mode_sw_pwd
4
#              - Added Compander to VFOA and VFOB settings
5
#              - Added Scan Group Upper/Lower to Scan Settings
6
#                    - changed group_x_lower and upper to group_lowerx to support for loop use
7
#              - Added Call Names to the Call Settings
8
#              - Added Mode SW Pwd and Reset Pwd to Config Settings
9
#              - Display FM Radio Presets in proper format / units (MHz)
10
#              - Display VFOA/VFOB/Rx Freq and RX Freq limits in units of MHz
11
#                   VHF freq Limit range is 30 - 299.999999 MHz
12
#                   UHF freq Limit range is 300 - 999.999999 MHz 
13
#                   VFOA/VFOB Freq Range is 30 - 999.999999 MHz
14
# 
15
#              - SETTINGS NOT YET IMPLEMENTED
16
#                   VFOA/VFOB RxTone and TxTone
17
#              - Note:  For channels shown on Memories tab 
18
#                   Compander, Descramble and SP Mute are not currently modifiable through the Memories tab.
19
#                   If any changes are made to a channel's settings, they are always defaulted to Off, Off, QT respectively. 
20
#                   
21

    
22

    
23

    
24

    
25
# MRT Beta 1.4 - updated User Interface to support settings modifications through the settings tab
26
# melvin.terechenok@gmail.com
27

    
28
# MRT 935G BETA1.3 - Update to handle 935G
29
# MRT Mapped most Wouxon CPS settings in Chirp
30
# MRT Upload to Radio and Download from Radio are working
31
# MRT Settings are available under the Browser tab in Hex/Dec/Bin format
32
# MRT No limits or error checking is done on values entered
33
# MRT Incorrect/Invalid values may cause radio malfunctions
34

    
35

    
36

    
37
# Copyright 2019 Pavel Milanes CO7WT <pavelmc@gmail.com>
38
#
39
# Based on the work of Krystian Struzik <toner_82@tlen.pl>
40
# who figured out the crypt used and made possible the
41
# Wuoxun KG-UV8D Plus driver, in which this work is based.
42
#
43
# This program is free software: you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License as published by
45
# the Free Software Foundation, either version 3 of the License, or
46
# (at your option) any later version.
47
#
48
# This program is distributed in the hope that it will be useful,
49
# but WITHOUT ANY WARRANTY; without even the implied warranty of
50
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
51
# GNU General Public License for more details.
52
#
53
# You should have received a copy of the GNU General Public License
54
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
55

    
56
"""Wouxun KG-935G radio management module"""
57

    
58
import time
59
import os
60
import logging
61

    
62
from chirp import util, chirp_common, bitwise, memmap, errors, directory
63
from chirp.settings import RadioSetting, RadioSettingGroup, \
64
    RadioSettingValueBoolean, RadioSettingValueList, \
65
    RadioSettingValueInteger, RadioSettingValueString, \
66
    RadioSettingValueFloat, RadioSettings
67

    
68

    
69
LOG = logging.getLogger(__name__)
70

    
71
CMD_ID = 128    # \x80
72
CMD_END = 129   # \x81
73
CMD_RD = 130    # \82
74
CMD_WR = 131    # \83
75

    
76
MEM_VALID = 158
77

    
78
AB_LIST = ["A", "B"]
79
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0, 50.0, 100.0]
80
STEP_LIST = [str(x) for x in STEPS]
81
ROGER_LIST = ["Off", "Begin", "End", "Both"]
82
TIMEOUT_LIST = ["Off"] + [str(x) + "s" for x in range(15, 901, 15)]
83
VOX_LIST = ["Off"] + ["%s" % x for x in range(1, 10)]
84
BANDWIDTH_LIST = ["Narrow", "Wide"]
85
VOICE_LIST = ["Off", "On"]
86
LANGUAGE_LIST = ["Chinese", "English"]
87
SCANMODE_LIST = ["TO", "CO", "SE"]
88
#MRT - EDIT FOR 935G
89
PFKEYLONG_LIST = ["undef", "FRQ2-PTT", "Selec Call", "Scan", "Flashlight", "Alarm", "SOS", "FM Radio", "Moni", "Strobe", "Weather", "Tlk A", "Reverse", "CTC Scan", "DCS Scan", "BRT"]
90
PFKEYSHORT_LIST = ["undef", "Scan", "Flashlight", "Alarm", "SOS", "FM Radio", "Moni", "Strobe", "Weather", "Tlk A", "Reverse", "CTC Scan", "DCS Scan", "BRT"]
91
#
92
WORKMODE_LIST = ["VFO", "Ch.Number.", "Ch.Freq.", "Ch.Name"]
93
BACKLIGHT_LIST = ["Always On"] + [str(x) + "s" for x in range(1, 21)] + \
94
    ["Always Off"]
95
OFFSET_LIST = ["+", "-"]
96
PONMSG_LIST = ["MSG - Bitmap", "Battery Volts"]
97
SPMUTE_LIST = ["QT", "QT+DTMF", "QT*DTMF"]
98
DTMFST_LIST = ["OFF", "DTMF", "ANI", "DTMF+ANI"]
99
DTMF_TIMES = [str(x) + "ms" for x in range(0, 501, 10)]
100
RPTSET_LIST = ["", "X-DIRRPT", "X-TWRPT"] # TODO < what is index 0?
101
ALERTS = [1750, 2100, 1000, 1450]
102
ALERTS_LIST = [str(x) for x in ALERTS]
103
PTTID_LIST = ["BOT", "EOT", "Both"]
104
LIST_10 = ["Off"] + ["%s" % x for x in range(1, 11)]
105
SCANGRP_LIST = ["All"] + ["%s" % x for x in range(1, 11)]
106
SCQT_LIST = ["Decoder", "Encoder", "All"]
107
SMUTESET_LIST = ["Off", "Tx", "Rx", "Tx+Rx"]
108
POWER_LIST = ["Lo", "Mid", "Hi"]
109
HOLD_TIMES = ["Off"] + ["%s" % x for x in range(100, 5001, 100)]
110
RPTMODE_LIST = ["Radio", "Repeater"]
111
#MRT ADDED NEW LISTS
112
THEME_LIST = ["White-1", "White-2", "Black-1", "Black-2"]
113
DSPBRTSBY_LIST = ["OFF", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
114
DSPBRTACT_LIST = ["OFF-DO NOT USE", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
115
TONESCANSAVELIST= ["Rx", "Tx", "Tx/Rx"]
116
PTTDELAY_LIST = [str(x) + "ms" for x in range(0, 3001, 100)]
117
SCRAMBLE_LIST = ["OFF"] + [str(x) for x in range(1,9)]
118
ONOFF_LIST = ["OFF", "ON"]
119
# memory slot 0 is not used, start at 1 (so need 1000 slots, not 999)
120
# structure elements whose name starts with x are currently unidentified
121

    
122
# MRT made Power = 2 bits to handle 935G's 3 power levels
123
# MRT beta 1.3 - updates to structure to match KG935G Custom programming SW configuration settings, FM Radio presets, Key Settings
124
# MRT beta 1.4 - modified some value names and merged FM Radio memories with other settings to resolve errors in UI implementation
125
_MEM_FORMAT = """
126
    #seekto 0x0044;
127
    struct {
128
        u32    rx_start;
129
        u32    rx_stop;
130
        u32    tx_start;
131
        u32    tx_stop;
132
    } uhf_limits;
133

    
134
    #seekto 0x0054;
135
    struct {
136
        u32    rx_start;
137
        u32    rx_stop;
138
        u32    tx_start;
139
        u32    tx_stop;
140
    } vhf_limits;
141

    
142
    #seekto 0x0400;
143
    struct {
144
        char     oem1[8];
145
        char     unknown[2];
146
        u8     unknown2[10];
147
        u8     unknown3[10];
148
        u8     unknown4[8];
149
        char     oem2[10];
150
        u8     version[6];
151
        char     date[8];
152
        u8     unknown5[2];
153
        char     model[8];     
154
    } oem_info;
155

    
156
    #seekto 0x0480;
157
    struct {
158
        u16    Group_lower1;
159
        u16    Group_upper1;
160
        u16    Group_lower2;
161
        u16    Group_upper2;
162
        u16    Group_lower3;
163
        u16    Group_upper3;
164
        u16    Group_lower4;
165
        u16    Group_upper4;
166
        u16    Group_lower5;
167
        u16    Group_upper5;
168
        u16    Group_lower6;
169
        u16    Group_upper6;
170
        u16    Group_lower7;
171
        u16    Group_upper7;
172
        u16    Group_lower8;
173
        u16    Group_upper8;
174
        u16    Group_lower9;
175
        u16    Group_upper9;
176
        u16    Group_lower10;
177
        u16    Group_upper10;
178
    } scan_groups;
179

    
180
    #seekto 0x0500;
181
    struct {
182
        u8    call_code_1[6];
183
        u8    call_code_2[6];
184
        u8    call_code_3[6];
185
        u8    call_code_4[6];
186
        u8    call_code_5[6];
187
        u8    call_code_6[6];
188
        u8    call_code_7[6];
189
        u8    call_code_8[6];
190
        u8    call_code_9[6];
191
        u8    call_code_10[6];
192
        u8    call_code_11[6];
193
        u8    call_code_12[6];
194
        u8    call_code_13[6];
195
        u8    call_code_14[6];
196
        u8    call_code_15[6];
197
        u8    call_code_16[6];
198
        u8    call_code_17[6];
199
        u8    call_code_18[6];
200
        u8    call_code_19[6];
201
        u8    call_code_20[6];
202
    } call_groups;
203

    
204
    #seekto 0x0580;
205
    struct {
206
        char    call_name1[6];
207
        char    call_name2[6];
208
        char    call_name3[6];
209
        char    call_name4[6];
210
        char    call_name5[6];
211
        char    call_name6[6];
212
        char    call_name7[6];
213
        char    call_name8[6];
214
        char    call_name9[6];
215
        char    call_name10[6];
216
        char    call_name11[6];
217
        char    call_name12[6];
218
        char    call_name13[6];
219
        char    call_name14[6];
220
        char    call_name15[6];
221
        char    call_name16[6];
222
        char    call_name17[6];
223
        char    call_name18[6];
224
        char    call_name19[6];
225
        char    call_name20[6];
226
    } call_names;
227

    
228

    
229
    #seekto 0x0600;
230
    struct {
231
        u16    FM_radio1;
232
        u16    FM_radio2;
233
        u16    FM_radio3;
234
        u16    FM_radio4;
235
        u16    FM_radio5;
236
        u16    FM_radio6;
237
        u16    FM_radio7;
238
        u16    FM_radio8;
239
        u16    FM_radio9;
240
        u16    FM_radio10;
241
        u16    FM_radio11;
242
        u16    FM_radio12;
243
        u16    FM_radio13;
244
        u16    FM_radio14;
245
        u16    FM_radio15;
246
        u16    FM_radio16;
247
        u16    FM_radio17;
248
        u16    FM_radio18;
249
        u16    FM_radio19;
250
        u16    FM_radio20;
251
        u16 unknown_pad_x0640[235];
252
        u8 unknown07fe;
253
        u8 unknown07ff;
254
        u8      ponmsg;
255
        char    dispstr[15];
256
        u8 unknown0810;
257
        u8 unknown0811;
258
        u8 unknown0812;
259
        u8 unknown0813;
260
        u8 unknown0814;
261
        u8      voice;
262
        u8      timeout;
263
        u8      toalarm;
264
        u8      channel_menu;
265
        u8      power_save;
266
        u8      autolock;
267
        u8      keylock;
268
        u8      beep;
269
        u8      stopwatch;
270
        u8      vox;
271
        u8      scan_rev;
272
        u8      backlight;
273
        u8      roger_beep;
274
        char      mode_sw_pwd[6];
275
        char      reset_pwd[6];
276
        u16     pri_ch;
277
        u8      ani_sw;
278
        u8      ptt_delay;
279
        u8      ani_code[6];
280
        u8      dtmf_st;
281
        u8      BCL_A;
282
        u8      BCL_B;
283
        u8      ptt_id;
284
        u8      prich_sw;
285
        u8 unknown083d;
286
        u8 unknown083e;
287
        u8 unknown083f;
288
        u8      alert;
289
        u8      pf1_shrt;
290
        u8      pf1_long;
291
        u8      pf2_shrt;
292
        u8      pf2_long;
293
        u8 unknown0845;
294
        u8      work_mode_a;
295
        u8      work_mode_b;
296
        u8      dtmf_tx_time;
297
        u8      dtmf_interval;
298
        u8      main_band;
299
        u16      work_ch_a;
300
        u16      work_ch_b;
301
        u8 unknown084f;
302
        u8 unknown0850;
303
        u8 unknown0851;
304
        u8 unknown0852;
305
        u8 unknown0853;
306
        u8 unknown0854;
307
        u8 unknown0855;
308
        u8 unknown0856;
309
        u8 unknown0857;
310
        u8 unknown0858;
311
        u8 unknown0859;
312
        u8 unknown085a;
313
        u8 unknown085b;
314
        u8 unknown085c;
315
        u8 unknown085d;
316
        u8 unknown085e;
317
        u8 unknown085f;
318
        u8 unknown0860;
319
        u8      TDR_single_mode;
320
        u8      ring_time;
321
        u8      ScnGrpA_Act;
322
        u8      ScnGrpB_Act;
323
        u8 unknown0865;
324
        u8      rpt_tone;
325
        u8 unknown0867;
326
        u8      scan_det;
327
        u8      ToneScnSave;
328
        u8 unknown086a;
329
        u8      smuteset;
330
        u8 unknown086c;
331
        u8      DspBrtAct;
332
        u8      DspBrtSby;
333
        u8 unknown086f;
334
        u8      theme;
335
        u8      wxalert;
336
        u8      VFO_repeater_a;
337
        u8      VFO_repeater_b;
338
        u8 unknown0874;
339
        u8 unknown0875;
340
        u8 unknown0876;
341
        u8 unknown0877;
342
        u8 unknown0878;
343
        u8 unknown0879;
344
        u8 unknown087a;
345
        u8 unknown087b;
346
        u8 unknown087c;
347
        u8 unknown087d;
348
        u8 unknown087e;
349
        u8 unknown087f;
350
    } settings;
351

    
352
    #seekto 0x0880;
353
    struct {
354
        u32     rxfreq;
355
        u32     unknown0;
356
        u16     rxtone;
357
        u16     txtone;
358
        u8      scrambler:4,
359
                unknown1:2,
360
                power:2; 
361
        u8      unknown3:1,
362
                unknown5:2
363
                unknown4:1,
364
                cmpndr:1,
365
                mute_mode:2,
366
                iswide:1;
367
        u8      step;
368
        u8      squelch;
369
      } vfoa;
370

    
371
    #seekto 0x08c0;
372
    struct {
373
        u32     rxfreq;
374
        u32     unknown0;
375
        u16     rxtone;
376
        u16     txtone;
377
        u8      scrambler:4,
378
                unknown1:2,
379
                power:2; 
380
        u8      unknown3:1,
381
                unknown5:2
382
                unknown4:1,
383
                cmpndr:1,
384
                mute_mode:2,
385
                iswide:1;
386
        u8      step;
387
        u8      squelch;
388
    } vfob;
389

    
390
    #seekto 0x0900;
391
    struct {
392
        u32     rxfreq;
393
        u32     txfreq;
394
        u16     rxtone;
395
        u16     txtone;
396
        u8      scrambler:4,
397
                unknown1:2,
398
                power:2; 
399
        u8      unknown3:2,
400
                scan_add:1,
401
                unknown4:1,
402
                compander:1,
403
                mute_mode:2,
404
                iswide:1;
405
        u8      unknown5;
406
        u8      unknown6;
407
    } memory[1000];
408

    
409
    #seekto 0x4780;
410
    struct {
411
        u8    name[8];
412
                u8    unknown[4];
413
    } names[1000];
414

    
415
    #seekto 0x7670;
416
    u8          valid[1000];
417
    """
418

    
419
    # Support for the Wouxun KG-935G radio
420
    # Serial coms are at 19200 baud
421
    # The data is passed in variable length records
422
    # Record structure:
423
    #  Offset   Usage
424
    #    0      start of record (\x7c)
425
    #    1      Command (\x80 Identify \x81 End/Reboot \x82 Read \x83 Write)
426
    #    2      direction (\xff PC-> Radio, \x00 Radio -> PC)
427
    #    3      length of payload (excluding header/checksum) (n)
428
    #    4      payload (n bytes)
429
    #    4+n+1  checksum - byte sum (% 256) of bytes 1 -> 4+n
430
    #
431
    # Memory Read Records:
432
    # the payload is 3 bytes, first 2 are offset (big endian),
433
    # 3rd is number of bytes to read
434
    # Memory Write Records:
435
    # the maximum payload size (from the Wouxun software) seems to be 66 bytes
436
    #  (2 bytes location + 64 bytes data).
437

    
438
#MRT 1.2 correct spelling of Wouxon
439
class KGUV8TRadio(chirp_common.Alias):
440
    VENDOR = "Wouxun"
441
    MODEL = "KG-935G"
442

    
443
@directory.register
444
class KG935GRadio(chirp_common.CloneModeRadio,
445
                  chirp_common.ExperimentalRadio):
446

    
447
    """Wouxun KG-935G"""
448
    VENDOR = "Wouxun"
449
    MODEL = "KG-935G"
450
    _model = "KG-UV8D-B"
451
    _file_ident = "935G"
452
    BAUD_RATE = 19200
453
# MRT - Added Medium Power level for 935G support
454
    POWER_LEVELS = [chirp_common.PowerLevel("L", watts=0.5),
455
                    chirp_common.PowerLevel("M", watts=4.5),
456
                    chirp_common.PowerLevel("H", watts=5.5)]
457
    _mmap = ""
458
    ALIASES = [KGUV8TRadio,]
459

    
460
    def _checksum(self, data):
461
        cs = 0
462
        for byte in data:
463
            cs += ord(byte)
464
        return chr(cs % 256)
465

    
466
    def _write_record(self, cmd, payload = None):
467
        # build the packet
468
        _header = '\x7c' + chr(cmd) + '\xff'
469

    
470
        _length = 0
471
        if payload:
472
            _length = len(payload)
473

    
474
        # update the length field
475
        _header += chr(_length)
476

    
477
        if payload:
478
            # calculate checksum then add it with the payload to the packet and encrypt
479
            crc = self._checksum(_header[1:] + payload)
480
            payload += crc
481
            _header += self.encrypt(payload)
482
        else:
483
            # calculate and add encrypted checksum to the packet
484
            crc = self._checksum(_header[1:])
485
            _header += self.strxor(crc, '\x57')
486

    
487
        try:
488
            self.pipe.write(_header)
489
        except Exception, e:
490
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
491

    
492
    def _read_record(self):
493
        # read 4 chars for the header
494
        _header = self.pipe.read(4)
495
        if len(_header) != 4:
496
            raise errors.RadioError('Radio did not respond')
497
        _length = ord(_header[3])
498
        _packet = self.pipe.read(_length)
499
        _rcs_xor = _packet[-1]
500
        _packet = self.decrypt(_packet)
501
        _cs = ord(self._checksum(_header[1:] + _packet))
502
        # read the checksum and decrypt it
503
        _rcs = ord(self.strxor(self.pipe.read(1), _rcs_xor))
504
        return (_rcs != _cs, _packet)
505

    
506
    def decrypt(self, data):
507
        result = ''
508
        for i in range(len(data)-1, 0, -1):
509
            result += self.strxor(data[i], data[i - 1])
510
        result += self.strxor(data[0], '\x57')
511
        return result[::-1]
512

    
513
    def encrypt(self, data):
514
        result = self.strxor('\x57', data[0])
515
        for i in range(1, len(data), 1):
516
            result += self.strxor(result[i - 1], data[i])
517
        return result
518

    
519
    def strxor (self, xora, xorb):
520
        return chr(ord(xora) ^ ord(xorb))
521

    
522
    # Identify the radio
523
    #
524
    # A Gotcha: the first identify packet returns a bad checksum, subsequent
525
    # attempts return the correct checksum... (well it does on my radio!)
526
    #
527
    # The ID record returned by the radio also includes the current frequency range
528
    # as 4 bytes big-endian in 10Hz increments
529
    #
530
    # Offset
531
    #  0:10     Model, zero padded (Looks for 'KG-UV8D-B')
532

    
533
    @classmethod
534
    def match_model(cls, filedata, filename):
535
        id = cls._file_ident 
536
        return cls._file_ident in filedata[0x426:0x430]
537

    
538
    def _identify(self):
539
        """Do the identification dance"""
540
        for _i in range(0, 10):
541
            self._write_record(CMD_ID)
542
            _chksum_err, _resp = self._read_record()
543
            LOG.debug("Got:\n%s" % util.hexprint(_resp))
544
            if _chksum_err:
545
                LOG.error("Checksum error: retrying ident...")
546
                time.sleep(0.100)
547
                continue
548
            LOG.debug("Model %s" % util.hexprint(_resp[0:9]))
549
            if _resp[0:9] == self._model:
550
                return
551
            if len(_resp) == 0:
552
                raise Exception("Radio not responding")
553
            else:
554
                raise Exception("Unable to identify radio")
555

    
556
    def _finish(self):
557
        self._write_record(CMD_END)
558

    
559
    def process_mmap(self):
560
        self._memobj = bitwise.parse(_MEM_FORMAT, self._mmap)
561

    
562
    def sync_in(self):
563
        try:
564
            self._mmap = self._download()
565
        except errors.RadioError:
566
            raise
567
        except Exception, e:
568
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
569
        self.process_mmap()
570

    
571
    def sync_out(self):
572
        self._upload()
573

    
574
    # TODO: Load all memory.
575
    # It would be smarter to only load the active areas and none of
576
    # the padding/unused areas. Padding still need to be investigated.
577
    def _download(self):
578
        """Talk to a wouxun KG-935G and do a download"""
579
        try:
580
            self._identify()
581
            return self._do_download(0, 32768, 64)
582
        except errors.RadioError:
583
            raise
584
        except Exception, e:
585
            LOG.exception('Unknown error during download process')
586
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
587

    
588
    def _do_download(self, start, end, blocksize):
589
        # allocate & fill memory
590
        image = ""
591
        for i in range(start, end, blocksize):
592
            req = chr(i / 256) + chr(i % 256) + chr(blocksize)
593
            self._write_record(CMD_RD, req)
594
            cs_error, resp = self._read_record()
595
            if cs_error:
596
                LOG.debug(util.hexprint(resp))
597
                raise Exception("Checksum error on read")
598
            # LOG.debug("Got:\n%s" % util.hexprint(resp))
599
            image += resp[2:]
600
            if self.status_fn:
601
                status = chirp_common.Status()
602
                status.cur = i
603
                status.max = end
604
                status.msg = "Cloning from radio"
605
                self.status_fn(status)
606
        self._finish()
607
        return memmap.MemoryMap(''.join(image))
608

    
609
    def _upload(self):
610
        """Talk to a wouxun KG-935G and do a upload"""
611
        try:
612
            self._identify()
613
            self._do_upload(0, 32768, 64)
614
        except errors.RadioError:
615
            raise
616
        except Exception, e:
617
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
618
        return
619

    
620
    def _do_upload(self, start, end, blocksize):
621
        ptr = start
622
        for i in range(start, end, blocksize):
623
            req = chr(i / 256) + chr(i % 256)
624
            chunk = self.get_mmap()[ptr:ptr + blocksize]
625
            self._write_record(CMD_WR, req + chunk)
626
            LOG.debug(util.hexprint(req + chunk))
627
            cserr, ack = self._read_record()
628
            LOG.debug(util.hexprint(ack))
629
            j = ord(ack[0]) * 256 + ord(ack[1])
630
            if cserr or j != ptr:
631
                raise Exception("Radio did not ack block %i" % ptr)
632
            ptr += blocksize
633
            if self.status_fn:
634
                status = chirp_common.Status()
635
                status.cur = i
636
                status.max = end
637
                status.msg = "Cloning to radio"
638
                self.status_fn(status)
639
        self._finish()
640

    
641
    def get_features(self):
642
        rf = chirp_common.RadioFeatures()
643
        rf.has_settings = True
644
        rf.has_ctone = True
645
        rf.has_rx_dtcs = True
646
        rf.has_cross = True
647
        rf.has_tuning_step = False
648
        rf.has_bank = False
649
        rf.can_odd_split = True
650
        rf.valid_skips = ["", "S"]
651
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
652
        rf.valid_cross_modes = [
653
            "Tone->Tone",
654
            "Tone->DTCS",
655
            "DTCS->Tone",
656
            "DTCS->",
657
            "->Tone",
658
            "->DTCS",
659
            "DTCS->DTCS",
660
        ]
661
        rf.valid_modes = ["FM", "NFM"]
662
        rf.valid_power_levels = self.POWER_LEVELS
663
        rf.valid_name_length = 8
664
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
665
        rf.valid_bands = [(137000000, 175000000),  # supports 2m
666
                          (400000000, 480000000)]  # supports 70cm
667
        rf.valid_characters = chirp_common.CHARSET_ASCII
668
        rf.memory_bounds = (1, 999)  # 999 memories
669
        rf.valid_tuning_steps = STEPS
670
        return rf
671

    
672
    @classmethod
673
    def get_prompts(cls):
674
        rp = chirp_common.RadioPrompts()
675
        rp.experimental = \
676
            ('This driver is experimental.\n'
677
             '\n'
678
             'Please keep a copy of your memories with the original Wouxon CPS software '
679
             'if you treasure them, this driver is new and may contain bugs.\n'
680
             '\n'
681
             ' Upload and Download from/to the Radio are working\n'
682
             ' '
683
             ' User interface has been added. \n Most of the settings from the Wouxon Custom Programming'
684
             ' software have been mapped\n in addition to some bonus settings that were'
685
             ' found.\n'
686
             ' Changing the VHF/UHF Rx limits does appear to work - but radio performance\n'
687
             ' is not guaranteed-  and may void warranty or cause radio to malfunction.\n'
688
             ' You can also customize the bottom banner from the OEMINFO Model setting\n'
689
                          ' \n'
690
             ' There are no limits/error checking done on the settings when using the Browser tab.\n'
691
             ' \n'
692
             ' Incorrect/Illegal values for a setting may cause radio malfunctions\n'
693
             ' USE AT YOUR OWN RISK\n'
694
             '\n'
695
             )
696
        return rp
697

    
698
    def get_raw_memory(self, number):
699
        return repr(self._memobj.memory[number])
700
# MRT - corrected the Polarity decoding to match 935G implementation use 0x2000 bit mask for R
701
# MRT - 0x2000 appears to be the bit mask for Inverted DCS tones
702
# MRT - n DCS Tone will be 0x4xxx values - i DCS Tones will be 0x6xxx values.
703
# MRT - Chirp Uses N for n DCS Tones and R for i DCS Tones
704
    def _get_tone(self, _mem, mem):
705
        def _get_dcs(val):
706
            code = int("%03o" % (val & 0x07FF))
707
            pol = (val & 0x2000) and "R" or "N"
708
            return code, pol
709
# MRT - Modified the function below to bitwise AND with 0x4000 to check for 935G DCS Tone decoding
710
# MRT 0x4000 appears to be the bit mask for DCS tones
711
        tpol = False
712
# MRT Beta 1.1 - Fix the txtone compare to 0x4000 - was rxtone.
713
        if _mem.txtone != 0xFFFF and (_mem.txtone & 0x4000) == 0x4000:
714
            tcode, tpol = _get_dcs(_mem.txtone)
715
            mem.dtcs = tcode
716
            txmode = "DTCS"
717
        elif _mem.txtone != 0xFFFF and _mem.txtone != 0x0:
718
            mem.rtone = (_mem.txtone & 0x7fff) / 10.0
719
            txmode = "Tone"
720
        else:
721
            txmode = ""
722
# MRT - Modified the function below to bitwise AND with 0x4000 to check for 935G DCS Tone decoding
723
        rpol = False
724
        if _mem.rxtone != 0xFFFF and (_mem.rxtone & 0x4000) == 0x4000:
725
            rcode, rpol = _get_dcs(_mem.rxtone)
726
            mem.rx_dtcs = rcode
727
            rxmode = "DTCS"
728
        elif _mem.rxtone != 0xFFFF and _mem.rxtone != 0x0:
729
            mem.ctone = (_mem.rxtone & 0x7fff) / 10.0
730
            rxmode = "Tone"
731
        else:
732
            rxmode = ""
733

    
734
        if txmode == "Tone" and not rxmode:
735
            mem.tmode = "Tone"
736
        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
737
            mem.tmode = "TSQL"
738
        elif txmode == rxmode and txmode == "DTCS" and mem.dtcs == mem.rx_dtcs:
739
            mem.tmode = "DTCS"
740
        elif rxmode or txmode:
741
            mem.tmode = "Cross"
742
            mem.cross_mode = "%s->%s" % (txmode, rxmode)
743

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

    
747
        LOG.debug("Got TX %s (%i) RX %s (%i)" %
748
                  (txmode, _mem.txtone, rxmode, _mem.rxtone))
749

    
750
    def get_memory(self, number):
751
        _mem = self._memobj.memory[number]
752
        _nam = self._memobj.names[number]
753

    
754
        mem = chirp_common.Memory()
755
        mem.number = number
756
        _valid = self._memobj.valid[mem.number]
757
        LOG.debug("%d %s", number, _valid == MEM_VALID)
758
        if _valid != MEM_VALID:
759
            mem.empty = True
760
            return mem
761
        else:
762
            mem.empty = False
763

    
764
        mem.freq = int(_mem.rxfreq) * 10
765

    
766
        if _mem.txfreq == 0xFFFFFFFF:
767
            # TX freq not set
768
            mem.duplex = "off"
769
            mem.offset = 0
770
        elif int(_mem.rxfreq) == int(_mem.txfreq):
771
            mem.duplex = ""
772
            mem.offset = 0
773
        elif abs(int(_mem.rxfreq) * 10 - int(_mem.txfreq) * 10) > 70000000:
774
            mem.duplex = "split"
775
            mem.offset = int(_mem.txfreq) * 10
776
        else:
777
            mem.duplex = int(_mem.rxfreq) > int(_mem.txfreq) and "-" or "+"
778
            mem.offset = abs(int(_mem.rxfreq) - int(_mem.txfreq)) * 10
779

    
780
        for char in _nam.name:
781
            if char != 0:
782
                mem.name += chr(char)
783
        mem.name = mem.name.rstrip()
784

    
785
        self._get_tone(_mem, mem)
786

    
787
        mem.skip = "" if bool(_mem.scan_add) else "S"
788

    
789
        mem.power = self.POWER_LEVELS[_mem.power]
790
        mem.mode = _mem.iswide and "FM" or "NFM"
791
        return mem
792

    
793
    def _set_tone(self, mem, _mem):
794
        def _set_dcs(code, pol):
795
#MRT Change from + 0x2800 to bitwise OR with 0x4000 to set the bit for DCS
796
            val = int("%i" % code, 8) | 0x4000
797
            if pol == "R":
798
#MRT Change to 0x2000 from 0x8000 to set the bit for i/R polarity
799
               val += 0x2000
800
            return val
801

    
802
        rx_mode = tx_mode = None
803
        rxtone = txtone = 0x0000
804

    
805
        if mem.tmode == "Tone":
806
            tx_mode = "Tone"
807
            rx_mode = None
808
            txtone = int(mem.rtone * 10) + 0x8000
809
        elif mem.tmode == "TSQL":
810
            rx_mode = tx_mode = "Tone"
811
            rxtone = txtone = int(mem.ctone * 10) + 0x8000
812
        elif mem.tmode == "DTCS":
813
            tx_mode = rx_mode = "DTCS"
814
            txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
815
            rxtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[1])
816
        elif mem.tmode == "Cross":
817
            tx_mode, rx_mode = mem.cross_mode.split("->")
818
            if tx_mode == "DTCS":
819
                txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
820
            elif tx_mode == "Tone":
821
                txtone = int(mem.rtone * 10) + 0x8000
822
            if rx_mode == "DTCS":
823
                rxtone = _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[1])
824
            elif rx_mode == "Tone":
825
                rxtone = int(mem.ctone * 10) + 0x8000
826

    
827
        _mem.rxtone = rxtone
828
        _mem.txtone = txtone
829

    
830
        LOG.debug("Set TX %s (%i) RX %s (%i)" %
831
                  (tx_mode, _mem.txtone, rx_mode, _mem.rxtone))
832

    
833
    def set_memory(self, mem):
834
        number = mem.number
835

    
836
        _mem = self._memobj.memory[number]
837
        _nam = self._memobj.names[number]
838

    
839
        if mem.empty:
840
            _mem.set_raw("\x00" * (_mem.size() / 8))
841
            self._memobj.valid[number] = 0
842
            self._memobj.names[number].set_raw("\x00" * (_nam.size() / 8))
843
            return
844

    
845
        _mem.rxfreq = int(mem.freq / 10)
846
        if mem.duplex == "off":
847
            _mem.txfreq = 0xFFFFFFFF
848
        elif mem.duplex == "split":
849
            _mem.txfreq = int(mem.offset / 10)
850
        elif mem.duplex == "off":
851
            for i in range(0, 4):
852
                _mem.txfreq[i].set_raw("\xFF")
853
        elif mem.duplex == "+":
854
            _mem.txfreq = int(mem.freq / 10) + int(mem.offset / 10)
855
        elif mem.duplex == "-":
856
            _mem.txfreq = int(mem.freq / 10) - int(mem.offset / 10)
857
        else:
858
            _mem.txfreq = int(mem.freq / 10)
859
        _mem.scan_add = int(mem.skip != "S")
860
        _mem.iswide = int(mem.mode == "FM")
861
        # set the tone
862
        self._set_tone(mem, _mem)
863
        # MRT set the scrambler and compander to off by default
864
        # MRT This changes them in the channel memory
865
        _mem.scrambler = 0
866
        _mem.compander = 0
867
        # set the power
868
        if mem.power:
869
            _mem.power = self.POWER_LEVELS.index(mem.power)
870
        else:
871
            _mem.power = True
872
        # MRT set to mute mode to QT (not QT+DTMF or QT*DTMF) by default
873
        # MRT This changes them in the channel memory
874
        _mem.mute_mode = 0
875
        # MRT set unknown5 to 1 and unknown6 to 0 
876
        # MRT This changes them in the channel memory to match Wouxun CPS
877
        # MRT it is unknown what impact these values have
878
        _mem.unknown5 = 1
879
        _mem.unknown6 = 0
880

    
881

    
882
        for i in range(0, len(_nam.name)):
883
            if i < len(mem.name) and mem.name[i]:
884
                _nam.name[i] = ord(mem.name[i])
885
            else:
886
                _nam.name[i] = 0x0
887
        self._memobj.valid[mem.number] = MEM_VALID
888

    
889
    def _get_settings(self):
890
        _settings = self._memobj.settings
891
        _vfoa = self._memobj.vfoa
892
        _vfob = self._memobj.vfob
893
        _scan = self._memobj.scan_groups
894
        _call = self._memobj.call_groups
895
        _callname = self._memobj.call_names
896
        
897

    
898
        cfg_grp = RadioSettingGroup("cfg_grp", "Config Settings")
899
        vfoa_grp = RadioSettingGroup("vfoa_grp", "VFO A Settings")
900
        vfob_grp = RadioSettingGroup("vfob_grp", "VFO B Settings")
901
        key_grp = RadioSettingGroup("key_grp", "Key Settings")
902
        fmradio_grp = RadioSettingGroup("fmradio_grp", "FM Broadcast Memory")
903
        lmt_grp = RadioSettingGroup("lmt_grp", "Rx Frequency Limits")
904
        uhf_lmt_grp = RadioSettingGroup("uhf_lmt_grp", "UHF")
905
        vhf_lmt_grp = RadioSettingGroup("vhf_lmt_grp", "VHF")
906
        oem_grp = RadioSettingGroup("oem_grp", "OEM Info")
907
        scan_grp = RadioSettingGroup("scan_grp", "Scan Group")
908
        call_grp = RadioSettingGroup("call_grp", "Call Settings")
909
        extra_grp = RadioSettingGroup("extra_grp", "NOT CHANGED BY RESET OR CPS")
910
        # lmt_grp.append(vhf_lmt_grp);
911
        # lmt_grp.append(uhf_lmt_grp);
912
        extra_grp.append(lmt_grp);
913
        extra_grp.append(oem_grp);
914
        group = RadioSettings(cfg_grp, vfoa_grp, vfob_grp,
915
                             fmradio_grp, key_grp, scan_grp, call_grp, extra_grp)
916

    
917

    
918
    # Call Settings
919

    
920
        callchars = "0123456789"
921
#MRT - Handle default factory values of 0xFF in Call Name memory
922
        _msg = ""
923
        _msg1 = str(_callname.call_name1).split("\0")[0]
924
        for char in _msg1:
925
            if char < chr(0x20) or char > chr(0x7E):
926
                _msg += chr(0x20)
927
            else:
928
                _msg += str(char)
929
        val = RadioSettingValueString(0, 6, _msg)
930
        val.set_mutable(True)
931
        rs = RadioSetting("call_names.call_name1", "Call Name 1", val)
932
        call_grp.append(rs)
933

    
934
        _codeobj = _call.call_code_1
935
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
936
        val = RadioSettingValueString(3, 6, _code, False)
937
        val.set_charset(callchars)
938
        rs = RadioSetting("call_code_1", "Call Code 1", val)
939
        def apply_call_code(setting, obj):
940
            value = []
941
            for j in range(0, 6):
942
                try:
943
                    value.append(callchars.index(str(setting.value)[j]))
944
                except IndexError:
945
                    value.append(0xFF)
946
            obj.call_code = value
947
        rs.set_apply_callback(apply_call_code, _call.call_code_1)
948
        call_grp.append(rs)
949

    
950
#MRT - Handle default factory values of 0xFF in Call Name memory
951
        _msg = ""
952
        _msg1 = str(_callname.call_name2).split("\0")[0]
953
        for char in _msg1:
954
            if char < chr(0x20) or char > chr(0x7E):
955
                _msg += chr(0x20)
956
            else:
957
                _msg += str(char)
958
        val = RadioSettingValueString(0, 6, _msg)
959
        val.set_mutable(True)
960
        rs = RadioSetting("call_names.call_name2", "Call Name 2", val)
961
        call_grp.append(rs)
962

    
963
        _codeobj = _call.call_code_2
964
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
965
        val = RadioSettingValueString(3, 6, _code, False)
966
        val.set_charset(callchars)
967
        rs = RadioSetting("call_code_2", "Call Code 2", val)
968
        def apply_call_code(setting, obj):
969
            value = []
970
            for j in range(0, 6):
971
                try:
972
                    value.append(callchars.index(str(setting.value)[j]))
973
                except IndexError:
974
                    value.append(0xFF)
975
            obj.call_code = value
976
        rs.set_apply_callback(apply_call_code, _call.call_code_2)
977
        call_grp.append(rs)
978

    
979
#MRT - Handle default factory values of 0xFF in Call Name memory
980
        _msg = ""
981
        _msg1 = str(_callname.call_name3).split("\0")[0]
982
        for char in _msg1:
983
            if char < chr(0x20) or char > chr(0x7E):
984
                _msg += chr(0x20)
985
            else:
986
                _msg += str(char)
987
        val = RadioSettingValueString(0, 6, _msg)
988
        val.set_mutable(True)
989
        rs = RadioSetting("call_names.call_name3", "Call Name 3", val)
990
        call_grp.append(rs)
991

    
992

    
993
        _codeobj = _call.call_code_3
994
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
995
        val = RadioSettingValueString(3, 6, _code, False)
996
        val.set_charset(callchars)
997
        rs = RadioSetting("call_code_3", "Call Code 3", val)
998
        def apply_call_code(setting, obj):
999
            value = []
1000
            for j in range(0, 6):
1001
                try:
1002
                    value.append(callchars.index(str(setting.value)[j]))
1003
                except IndexError:
1004
                    value.append(0xFF)
1005
            obj.call_code = value
1006
        rs.set_apply_callback(apply_call_code, _call.call_code_3)
1007
        call_grp.append(rs)
1008

    
1009
#MRT - Handle default factory values of 0xFF in Call Name memory
1010
        _msg = ""
1011
        _msg1 = str(_callname.call_name4).split("\0")[0]
1012
        for char in _msg1:
1013
            if char < chr(0x20) or char > chr(0x7E):
1014
                _msg += chr(0x20)
1015
            else:
1016
                _msg += str(char)
1017
        val = RadioSettingValueString(0, 6, _msg)
1018
        val.set_mutable(True)
1019
        rs = RadioSetting("call_names.call_name4", "Call Name 4", val)
1020
        call_grp.append(rs)
1021

    
1022

    
1023
        _codeobj = _call.call_code_4
1024
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1025
        val = RadioSettingValueString(3, 6, _code, False)
1026
        val.set_charset(callchars)
1027
        rs = RadioSetting("call_code_4", "Call Code 4", val)
1028
        def apply_call_code(setting, obj):
1029
            value = []
1030
            for j in range(0, 6):
1031
                try:
1032
                    value.append(callchars.index(str(setting.value)[j]))
1033
                except IndexError:
1034
                    value.append(0xFF)
1035
            obj.call_code = value
1036
        rs.set_apply_callback(apply_call_code, _call.call_code_4)
1037
        call_grp.append(rs)
1038

    
1039
#MRT - Handle default factory values of 0xFF in Call Name memory
1040
        _msg = ""
1041
        _msg1 = str(_callname.call_name5).split("\0")[0]
1042
        for char in _msg1:
1043
            if char < chr(0x20) or char > chr(0x7E):
1044
                _msg += chr(0x20)
1045
            else:
1046
                _msg += str(char)
1047
        val = RadioSettingValueString(0, 6, _msg)
1048
        val.set_mutable(True)
1049
        rs = RadioSetting("call_names.call_name5", "Call Name 5", val)
1050
        call_grp.append(rs)
1051

    
1052

    
1053
        _codeobj = _call.call_code_5
1054
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1055
        val = RadioSettingValueString(3, 6, _code, False)
1056
        val.set_charset(callchars)
1057
        rs = RadioSetting("call_code_5", "Call Code 5", val)
1058
        def apply_call_code(setting, obj):
1059
            value = []
1060
            for j in range(0, 6):
1061
                try:
1062
                    value.append(callchars.index(str(setting.value)[j]))
1063
                except IndexError:
1064
                    value.append(0xFF)
1065
            obj.call_code = value
1066
        rs.set_apply_callback(apply_call_code, _call.call_code_5)
1067
        call_grp.append(rs)
1068

    
1069
#MRT - Handle default factory values of 0xFF in Call Name memory
1070
        _msg = ""
1071
        _msg1 = str(_callname.call_name6).split("\0")[0]
1072
        for char in _msg1:
1073
            if char < chr(0x20) or char > chr(0x7E):
1074
                _msg += chr(0x20)
1075
            else:
1076
                _msg += str(char)
1077
        val = RadioSettingValueString(0, 6, _msg)
1078
        val.set_mutable(True)
1079
        rs = RadioSetting("call_names.call_name6", "Call Name 6", val)
1080
        call_grp.append(rs)
1081

    
1082

    
1083
        _codeobj = _call.call_code_6
1084
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1085
        val = RadioSettingValueString(3, 6, _code, False)
1086
        val.set_charset(callchars)
1087
        rs = RadioSetting("call_code_6", "Call Code 6", val)
1088
        def apply_call_code(setting, obj):
1089
            value = []
1090
            for j in range(0, 6):
1091
                try:
1092
                    value.append(callchars.index(str(setting.value)[j]))
1093
                except IndexError:
1094
                    value.append(0xFF)
1095
            obj.call_code = value
1096
        rs.set_apply_callback(apply_call_code, _call.call_code_6)
1097
        call_grp.append(rs)
1098

    
1099
#MRT - Handle default factory values of 0xFF in Call Name memory
1100
        _msg = ""
1101
        _msg1 = str(_callname.call_name7).split("\0")[0]
1102
        for char in _msg1:
1103
            if char < chr(0x20) or char > chr(0x7E):
1104
                _msg += chr(0x20)
1105
            else:
1106
                _msg += str(char)
1107
        val = RadioSettingValueString(0, 6, _msg)
1108
        val.set_mutable(True)
1109
        rs = RadioSetting("call_names.call_name7", "Call Name 7", val)
1110
        call_grp.append(rs)
1111

    
1112

    
1113
        _codeobj = _call.call_code_7
1114
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1115
        val = RadioSettingValueString(3, 6, _code, False)
1116
        val.set_charset(callchars)
1117
        rs = RadioSetting("call_code_7", "Call Code 7", val)
1118
        def apply_call_code(setting, obj):
1119
            value = []
1120
            for j in range(0, 6):
1121
                try:
1122
                    value.append(callchars.index(str(setting.value)[j]))
1123
                except IndexError:
1124
                    value.append(0xFF)
1125
            obj.call_code = value
1126
        rs.set_apply_callback(apply_call_code, _call.call_code_7)
1127
        call_grp.append(rs)
1128

    
1129
#MRT - Handle default factory values of 0xFF in Call Name memory
1130
        _msg = ""
1131
        _msg1 = str(_callname.call_name8).split("\0")[0]
1132
        for char in _msg1:
1133
            if char < chr(0x20) or char > chr(0x7E):
1134
                _msg += chr(0x20)
1135
            else:
1136
                _msg += str(char)
1137
        val = RadioSettingValueString(0, 6, _msg)
1138
        val.set_mutable(True)
1139
        rs = RadioSetting("call_names.call_name8", "Call Name 8", val)
1140
        call_grp.append(rs)
1141

    
1142

    
1143
        _codeobj = _call.call_code_8
1144
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1145
        val = RadioSettingValueString(3, 6, _code, False)
1146
        val.set_charset(callchars)
1147
        rs = RadioSetting("call_code_8", "Call Code 8", val)
1148
        def apply_call_code(setting, obj):
1149
            value = []
1150
            for j in range(0, 6):
1151
                try:
1152
                    value.append(callchars.index(str(setting.value)[j]))
1153
                except IndexError:
1154
                    value.append(0xFF)
1155
            obj.call_code = value
1156
        rs.set_apply_callback(apply_call_code, _call.call_code_8)
1157
        call_grp.append(rs)
1158

    
1159
#MRT - Handle default factory values of 0xFF in Call Name memory
1160
        _msg = ""
1161
        _msg1 = str(_callname.call_name9).split("\0")[0]
1162
        for char in _msg1:
1163
            if char < chr(0x20) or char > chr(0x7E):
1164
                _msg += chr(0x20)
1165
            else:
1166
                _msg += str(char)
1167
        val = RadioSettingValueString(0, 6, _msg)
1168
        val.set_mutable(True)
1169
        rs = RadioSetting("call_names.call_name9", "Call Name 9", val)
1170
        call_grp.append(rs)
1171

    
1172

    
1173
        _codeobj = _call.call_code_9
1174
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1175
        val = RadioSettingValueString(3, 6, _code, False)
1176
        val.set_charset(callchars)
1177
        rs = RadioSetting("call_code_9", "Call Code 9", val)
1178
        def apply_call_code(setting, obj):
1179
            value = []
1180
            for j in range(0, 6):
1181
                try:
1182
                    value.append(callchars.index(str(setting.value)[j]))
1183
                except IndexError:
1184
                    value.append(0xFF)
1185
            obj.call_code = value
1186
        rs.set_apply_callback(apply_call_code, _call.call_code_9)
1187
        call_grp.append(rs)
1188

    
1189
#MRT - Handle default factory values of 0xFF in Call Name memory
1190
        _msg = ""
1191
        _msg1 = str(_callname.call_name10).split("\0")[0]
1192
        for char in _msg1:
1193
            if char < chr(0x20) or char > chr(0x7E):
1194
                _msg += chr(0x20)
1195
            else:
1196
                _msg += str(char)
1197
        val = RadioSettingValueString(0, 6, _msg)
1198
        val.set_mutable(True)
1199
        rs = RadioSetting("call_names.call_name10", "Call Name 10", val)
1200
        call_grp.append(rs)
1201

    
1202

    
1203
        _codeobj = _call.call_code_10
1204
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1205
        val = RadioSettingValueString(3, 6, _code, False)
1206
        val.set_charset(callchars)
1207
        rs = RadioSetting("call_code_10", "Call Code 10", val)
1208
        def apply_call_code(setting, obj):
1209
            value = []
1210
            for j in range(0, 6):
1211
                try:
1212
                    value.append(callchars.index(str(setting.value)[j]))
1213
                except IndexError:
1214
                    value.append(0xFF)
1215
            obj.call_code = value
1216
        rs.set_apply_callback(apply_call_code, _call.call_code_10)
1217
        call_grp.append(rs)
1218

    
1219
#MRT - Handle default factory values of 0xFF in Call Name memory
1220
        _msg = ""
1221
        _msg1 = str(_callname.call_name11).split("\0")[0]
1222
        for char in _msg1:
1223
            if char < chr(0x20) or char > chr(0x7E):
1224
                _msg += chr(0x20)
1225
            else:
1226
                _msg += str(char)
1227
        val = RadioSettingValueString(0, 6, _msg)
1228
        val.set_mutable(True)
1229
        rs = RadioSetting("call_names.call_name11", "Call Name 11", val)
1230
        call_grp.append(rs)
1231

    
1232

    
1233
        _codeobj = _call.call_code_11
1234
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1235
        val = RadioSettingValueString(3, 6, _code, False)
1236
        val.set_charset(callchars)
1237
        rs = RadioSetting("call_code_11", "Call Code 11", val)
1238
        def apply_call_code(setting, obj):
1239
            value = []
1240
            for j in range(0, 6):
1241
                try:
1242
                    value.append(callchars.index(str(setting.value)[j]))
1243
                except IndexError:
1244
                    value.append(0xFF)
1245
            obj.call_code = value
1246
        rs.set_apply_callback(apply_call_code, _call.call_code_11)
1247
        call_grp.append(rs)
1248

    
1249
#MRT - Handle default factory values of 0xFF in Call Name memory
1250
        _msg = ""
1251
        _msg1 = str(_callname.call_name12).split("\0")[0]
1252
        for char in _msg1:
1253
            if char < chr(0x20) or char > chr(0x7E):
1254
                _msg += chr(0x20)
1255
            else:
1256
                _msg += str(char)
1257
        val = RadioSettingValueString(0, 6, _msg)
1258
        val.set_mutable(True)
1259
        rs = RadioSetting("call_names.call_name12", "Call Name 12", val)
1260
        call_grp.append(rs)
1261

    
1262

    
1263
        _codeobj = _call.call_code_12
1264
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1265
        val = RadioSettingValueString(3, 6, _code, False)
1266
        val.set_charset(callchars)
1267
        rs = RadioSetting("call_code_12", "Call Code 12", val)
1268
        def apply_call_code(setting, obj):
1269
            value = []
1270
            for j in range(0, 6):
1271
                try:
1272
                    value.append(callchars.index(str(setting.value)[j]))
1273
                except IndexError:
1274
                    value.append(0xFF)
1275
            obj.call_code = value
1276
        rs.set_apply_callback(apply_call_code, _call.call_code_12)
1277
        call_grp.append(rs)
1278

    
1279
#MRT - Handle default factory values of 0xFF in Call Name memory
1280
        _msg = ""
1281
        _msg1 = str(_callname.call_name13).split("\0")[0]
1282
        for char in _msg1:
1283
            if char < chr(0x20) or char > chr(0x7E):
1284
                _msg += chr(0x20)
1285
            else:
1286
                _msg += str(char)
1287
        val = RadioSettingValueString(0, 6, _msg)
1288
        val.set_mutable(True)
1289
        rs = RadioSetting("call_names.call_name13", "Call Name 13", val)
1290
        call_grp.append(rs)
1291

    
1292

    
1293
        _codeobj = _call.call_code_13
1294
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1295
        val = RadioSettingValueString(3, 6, _code, False)
1296
        val.set_charset(callchars)
1297
        rs = RadioSetting("call_code_13", "Call Code 13", val)
1298
        def apply_call_code(setting, obj):
1299
            value = []
1300
            for j in range(0, 6):
1301
                try:
1302
                    value.append(callchars.index(str(setting.value)[j]))
1303
                except IndexError:
1304
                    value.append(0xFF)
1305
            obj.call_code = value
1306
        rs.set_apply_callback(apply_call_code, _call.call_code_13)
1307
        call_grp.append(rs)
1308

    
1309
#MRT - Handle default factory values of 0xFF in Call Name memory
1310
        _msg = ""
1311
        _msg1 = str(_callname.call_name14).split("\0")[0]
1312
        for char in _msg1:
1313
            if char < chr(0x20) or char > chr(0x7E):
1314
                _msg += chr(0x20)
1315
            else:
1316
                _msg += str(char)
1317
        val = RadioSettingValueString(0, 6, _msg)
1318
        val.set_mutable(True)
1319
        rs = RadioSetting("call_names.call_name14", "Call Name 14", val)
1320
        call_grp.append(rs)
1321

    
1322

    
1323
        _codeobj = _call.call_code_14
1324
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1325
        val = RadioSettingValueString(3, 6, _code, False)
1326
        val.set_charset(callchars)
1327
        rs = RadioSetting("call_code_14", "Call Code 14", val)
1328
        def apply_call_code(setting, obj):
1329
            value = []
1330
            for j in range(0, 6):
1331
                try:
1332
                    value.append(callchars.index(str(setting.value)[j]))
1333
                except IndexError:
1334
                    value.append(0xFF)
1335
            obj.call_code = value
1336
        rs.set_apply_callback(apply_call_code, _call.call_code_14)
1337
        call_grp.append(rs)
1338

    
1339
#MRT - Handle default factory values of 0xFF in Call Name memory
1340
        _msg = ""
1341
        _msg1 = str(_callname.call_name15).split("\0")[0]
1342
        for char in _msg1:
1343
            if char < chr(0x20) or char > chr(0x7E):
1344
                _msg += chr(0x20)
1345
            else:
1346
                _msg += str(char)
1347
        val = RadioSettingValueString(0, 6, _msg)
1348
        val.set_mutable(True)
1349
        rs = RadioSetting("call_names.call_name15", "Call Name 15", val)
1350
        call_grp.append(rs)
1351

    
1352

    
1353
        _codeobj = _call.call_code_15
1354
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1355
        val = RadioSettingValueString(3, 6, _code, False)
1356
        val.set_charset(callchars)
1357
        rs = RadioSetting("call_code_15", "Call Code 15", val)
1358
        def apply_call_code(setting, obj):
1359
            value = []
1360
            for j in range(0, 6):
1361
                try:
1362
                    value.append(callchars.index(str(setting.value)[j]))
1363
                except IndexError:
1364
                    value.append(0xFF)
1365
            obj.call_code = value
1366
        rs.set_apply_callback(apply_call_code, _call.call_code_15)
1367
        call_grp.append(rs)
1368

    
1369
#MRT - Handle default factory values of 0xFF in Call Name memory
1370
        _msg = ""
1371
        _msg1 = str(_callname.call_name16).split("\0")[0]
1372
        for char in _msg1:
1373
            if char < chr(0x20) or char > chr(0x7E):
1374
                _msg += chr(0x20)
1375
            else:
1376
                _msg += str(char)
1377
        val = RadioSettingValueString(0, 6, _msg)
1378
        val.set_mutable(True)
1379
        rs = RadioSetting("call_names.call_name16", "Call Name 16", val)
1380
        call_grp.append(rs)
1381

    
1382

    
1383
        _codeobj = _call.call_code_16
1384
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1385
        val = RadioSettingValueString(3, 6, _code, False)
1386
        val.set_charset(callchars)
1387
        rs = RadioSetting("call_code_16", "Call Code 16", val)
1388
        def apply_call_code(setting, obj):
1389
            value = []
1390
            for j in range(0, 6):
1391
                try:
1392
                    value.append(callchars.index(str(setting.value)[j]))
1393
                except IndexError:
1394
                    value.append(0xFF)
1395
            obj.call_code = value
1396
        rs.set_apply_callback(apply_call_code, _call.call_code_16)
1397
        call_grp.append(rs)
1398

    
1399
#MRT - Handle default factory values of 0xFF in Call Name memory
1400
        _msg = ""
1401
        _msg1 = str(_callname.call_name17).split("\0")[0]
1402
        for char in _msg1:
1403
            if char < chr(0x20) or char > chr(0x7E):
1404
                _msg += chr(0x20)
1405
            else:
1406
                _msg += str(char)
1407
        val = RadioSettingValueString(0, 6, _msg)
1408
        val.set_mutable(True)
1409
        rs = RadioSetting("call_names.call_name17", "Call Name 17", val)
1410
        call_grp.append(rs)
1411

    
1412

    
1413
        _codeobj = _call.call_code_17
1414
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1415
        val = RadioSettingValueString(3, 6, _code, False)
1416
        val.set_charset(callchars)
1417
        rs = RadioSetting("call_code_17", "Call Code 17", val)
1418
        def apply_call_code(setting, obj):
1419
            value = []
1420
            for j in range(0, 6):
1421
                try:
1422
                    value.append(callchars.index(str(setting.value)[j]))
1423
                except IndexError:
1424
                    value.append(0xFF)
1425
            obj.call_code = value
1426
        rs.set_apply_callback(apply_call_code, _call.call_code_17)
1427
        call_grp.append(rs)
1428

    
1429
#MRT - Handle default factory values of 0xFF in Call Name memory
1430
        _msg = ""
1431
        _msg1 = str(_callname.call_name18).split("\0")[0]
1432
        for char in _msg1:
1433
            if char < chr(0x20) or char > chr(0x7E):
1434
                _msg += chr(0x20)
1435
            else:
1436
                _msg += str(char)
1437
        val = RadioSettingValueString(0, 6, _msg)
1438
        val.set_mutable(True)
1439
        rs = RadioSetting("call_names.call_name18", "Call Name 18", val)
1440
        call_grp.append(rs)
1441

    
1442

    
1443
        _codeobj = _call.call_code_18
1444
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1445
        val = RadioSettingValueString(3, 6, _code, False)
1446
        val.set_charset(callchars)
1447
        rs = RadioSetting("call_code_18", "Call Code 18", val)
1448
        def apply_call_code(setting, obj):
1449
            value = []
1450
            for j in range(0, 6):
1451
                try:
1452
                    value.append(callchars.index(str(setting.value)[j]))
1453
                except IndexError:
1454
                    value.append(0xFF)
1455
            obj.call_code = value
1456
        rs.set_apply_callback(apply_call_code, _call.call_code_18)
1457
        call_grp.append(rs)
1458

    
1459
#MRT - Handle default factory values of 0xFF in Call Name memory
1460
        _msg = ""
1461
        _msg1 = str(_callname.call_name19).split("\0")[0]
1462
        for char in _msg1:
1463
            if char < chr(0x20) or char > chr(0x7E):
1464
                _msg += chr(0x20)
1465
            else:
1466
                _msg += str(char)
1467
        val = RadioSettingValueString(0, 6, _msg)
1468
        val.set_mutable(True)
1469
        rs = RadioSetting("call_names.call_name19", "Call Name 19", val)
1470
        call_grp.append(rs)
1471

    
1472

    
1473
        _codeobj = _call.call_code_19
1474
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1475
        val = RadioSettingValueString(3, 6, _code, False)
1476
        val.set_charset(callchars)
1477
        rs = RadioSetting("call_code_19", "Call Code 19", val)
1478
        def apply_call_code(setting, obj):
1479
            value = []
1480
            for j in range(0, 6):
1481
                try:
1482
                    value.append(callchars.index(str(setting.value)[j]))
1483
                except IndexError:
1484
                    value.append(0xFF)
1485
            obj.call_code = value
1486
        rs.set_apply_callback(apply_call_code, _call.call_code_19)
1487
        call_grp.append(rs)
1488

    
1489
#MRT - Handle default factory values of 0xFF in Call Name memory
1490
        _msg = ""
1491
        _msg1 = str(_callname.call_name20).split("\0")[0]
1492
        for char in _msg1:
1493
            if char < chr(0x20) or char > chr(0x7E):
1494
                _msg += chr(0x20)
1495
            else:
1496
                _msg += str(char)
1497
        val = RadioSettingValueString(0, 6, _msg)
1498
        val.set_mutable(True)
1499
        rs = RadioSetting("call_names.call_name20", "Call Name 20", val)
1500
        call_grp.append(rs)
1501

    
1502

    
1503
        _codeobj = _call.call_code_20
1504
        _code = "".join([callchars[x] for x in _codeobj if int(x) < 0x0A])
1505
        val = RadioSettingValueString(3, 6, _code, False)
1506
        val.set_charset(callchars)
1507
        rs = RadioSetting("call_code_20", "Call Code 20", val)
1508
        def apply_call_code(setting, obj):
1509
            value = []
1510
            for j in range(0, 6):
1511
                try:
1512
                    value.append(callchars.index(str(setting.value)[j]))
1513
                except IndexError:
1514
                    value.append(0xFF)
1515
            obj.call_code = value
1516
        rs.set_apply_callback(apply_call_code, _call.call_code_20)
1517
        call_grp.append(rs)
1518

    
1519
        # Configuration Settings
1520
        #
1521
        rs = RadioSetting("DspBrtAct", "Display Brightness ACTIVE",
1522
                          RadioSettingValueList(DSPBRTACT_LIST,
1523
                                                 DSPBRTACT_LIST[_settings.DspBrtAct]))
1524
        cfg_grp.append(rs)
1525
        rs = RadioSetting("DspBrtSby", "Display Brightness STANDBY",
1526
                          RadioSettingValueList(DSPBRTSBY_LIST,
1527
                                                 DSPBRTSBY_LIST[_settings.DspBrtSby]))
1528
        cfg_grp.append(rs)
1529
        rs = RadioSetting("wxalert", "Weather Alert",
1530
                          RadioSettingValueBoolean(_settings.wxalert))
1531
        cfg_grp.append(rs)
1532
        rs = RadioSetting("power_save", "Battery Saver",
1533
                          RadioSettingValueBoolean(_settings.power_save))
1534
        cfg_grp.append(rs)
1535
        rs = RadioSetting("theme", "Theme",
1536
                          RadioSettingValueList(
1537
                              THEME_LIST, THEME_LIST[_settings.theme]))
1538
        cfg_grp.append(rs)
1539
        rs = RadioSetting("backlight", "Backlight Active Time",
1540
                          RadioSettingValueList(BACKLIGHT_LIST,
1541
                                                BACKLIGHT_LIST[_settings.
1542
                                                               backlight]))
1543
        cfg_grp.append(rs)
1544
        rs = RadioSetting("scan_rev", "Scan Mode",
1545
                          RadioSettingValueList(SCANMODE_LIST,
1546
                                                SCANMODE_LIST[_settings.
1547
                                                              scan_rev]))
1548
        cfg_grp.append(rs)
1549
        rs = RadioSetting("prich_sw", "Priority Channel Scan",
1550
                          RadioSettingValueBoolean(_settings.prich_sw))
1551
        cfg_grp.append(rs)
1552
        rs = RadioSetting("pri_ch", "Priority Channel - Can not be empty Channel",
1553
                          RadioSettingValueInteger(1, 999, _settings.pri_ch))
1554
        cfg_grp.append(rs)
1555
        rs = RadioSetting("scan_det", "Scan Mode Tone Detect",
1556
                          RadioSettingValueBoolean(_settings.scan_det))
1557
        cfg_grp.append(rs)
1558
        rs = RadioSetting("ToneScnSave", "Tone Scan Save",
1559
                          RadioSettingValueList(TONESCANSAVELIST,
1560
                                                TONESCANSAVELIST[_settings.ToneScnSave]))
1561
        cfg_grp.append(rs)
1562
        rs = RadioSetting("roger_beep", "Roger Beep",
1563
                          RadioSettingValueList(ROGER_LIST,
1564
                                                ROGER_LIST[_settings.roger_beep]))
1565
        cfg_grp.append(rs)
1566
        rs = RadioSetting("timeout", "Timeout Timer (TOT)",
1567
                          RadioSettingValueList(
1568
                              TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout]))
1569
        cfg_grp.append(rs)
1570
        rs = RadioSetting("toalarm", "Timeout Alarm (TOA)",
1571
                          RadioSettingValueInteger(0, 10, _settings.toalarm))
1572
        cfg_grp.append(rs)
1573
        rs = RadioSetting("vox", "VOX",
1574
                          RadioSettingValueList(LIST_10,
1575
                                                LIST_10[_settings.vox]))
1576
        cfg_grp.append(rs)
1577
        rs = RadioSetting("voice", "Voice Guide",
1578
                          RadioSettingValueBoolean(_settings.voice))
1579
        cfg_grp.append(rs)
1580
        rs = RadioSetting("beep", "Keypad Beep",
1581
                          RadioSettingValueBoolean(_settings.beep))
1582
        cfg_grp.append(rs)
1583
        rs = RadioSetting("BCL_B", "Busy Channel Lock-out A",
1584
                          RadioSettingValueBoolean(_settings.BCL_A))
1585
        cfg_grp.append(rs)
1586
        rs = RadioSetting("BCL_A", "Busy Channel Lock-out B",
1587
                          RadioSettingValueBoolean(_settings.BCL_B))
1588
        cfg_grp.append(rs)
1589
        rs = RadioSetting("smuteset", "Secondary Area Mute (SMUTESET)",
1590
                          RadioSettingValueList(SMUTESET_LIST,
1591
                                                SMUTESET_LIST[_settings.
1592
                                                              smuteset]))
1593
        cfg_grp.append(rs)
1594
        rs = RadioSetting("ani_sw", "ANI-ID Switch (ANI-SW)",
1595
                          RadioSettingValueBoolean(_settings.ani_sw))
1596
        cfg_grp.append(rs)
1597
        rs = RadioSetting("dtmf_st", "DTMF Sidetone (SIDETONE)",
1598
                          RadioSettingValueList(DTMFST_LIST,
1599
                                                DTMFST_LIST[_settings.
1600
                                                            dtmf_st]))
1601
        cfg_grp.append(rs)
1602
        rs = RadioSetting("alert", "Alert Tone",
1603
                          RadioSettingValueList(ALERTS_LIST,
1604
                                                ALERTS_LIST[_settings.alert]))
1605
        cfg_grp.append(rs)
1606
        rs = RadioSetting("ptt_delay", "PTT-DLY - MIN 100ms",
1607
                          RadioSettingValueList(PTTDELAY_LIST,
1608
                                                PTTDELAY_LIST[_settings.ptt_delay]))
1609
        cfg_grp.append(rs)
1610
        rs = RadioSetting("ptt_id", "PTT-ID",
1611
                          RadioSettingValueList(PTTID_LIST,
1612
                                                PTTID_LIST[_settings.ptt_id]))
1613
        cfg_grp.append(rs)
1614
        rs = RadioSetting("ring_time", "Ring Time",
1615
                          RadioSettingValueList(LIST_10,
1616
                                                LIST_10[_settings.ring_time]))
1617
        cfg_grp.append(rs)
1618
        rs = RadioSetting("rpt_tone", "Repeater Tone",
1619
                          RadioSettingValueBoolean(_settings.rpt_tone))
1620
        cfg_grp.append(rs)
1621
        rs = RadioSetting("stopwatch", "Timer / Stopwatch",
1622
                          RadioSettingValueBoolean(_settings.stopwatch))
1623
        cfg_grp.append(rs)
1624
        rs = RadioSetting("autolock", "Autolock",
1625
                          RadioSettingValueBoolean(_settings.autolock))
1626
        cfg_grp.append(rs)
1627
        rs = RadioSetting("keylock", "Keypad Lock",
1628
                          RadioSettingValueBoolean(_settings.keylock))
1629
        cfg_grp.append(rs)
1630
        rs = RadioSetting("ponmsg", "Poweron message",
1631
                          RadioSettingValueList(
1632
                              PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
1633
        cfg_grp.append(rs)
1634
        rs = RadioSetting("dtmf_tx_time", "DTMF Transmit Time - MIN 50ms",
1635
                          RadioSettingValueList(DTMF_TIMES,
1636
                                                DTMF_TIMES[_settings.
1637
                                                           dtmf_tx_time]))
1638
        cfg_grp.append(rs)
1639
        rs = RadioSetting("dtmf_interval", "DTMF Interval Time - MIN 50ms",
1640
                          RadioSettingValueList(DTMF_TIMES,
1641
                                                DTMF_TIMES[_settings.
1642
                                                           dtmf_interval]))
1643
        cfg_grp.append(rs)
1644
        rs = RadioSetting("channel_menu", "Menu available in channel mode",
1645
                          RadioSettingValueBoolean(_settings.channel_menu))
1646
        cfg_grp.append(rs)
1647

    
1648
        pswdchars = "0123456789"
1649
        _msg = str(_settings.mode_sw_pwd).split("\0")[0]
1650
        val = RadioSettingValueString(0, 6, _msg, False)
1651
        val.set_mutable(True)
1652
        val.set_charset(pswdchars)
1653
        rs = RadioSetting("mode_sw_pwd", "Mode SW Pwd", val)
1654
        cfg_grp.append(rs)
1655

    
1656
        _msg = str(_settings.reset_pwd).split("\0")[0]
1657
        val = RadioSettingValueString(0, 6, _msg, False)
1658
        val.set_charset(pswdchars)
1659
        val.set_mutable(True)
1660
        rs = RadioSetting("reset_pwd", "Reset Pwd", val)
1661
        cfg_grp.append(rs)
1662

    
1663
        # Key Settings
1664
        #
1665
        _msg = str(_settings.dispstr).split("\0")[0]
1666
        val = RadioSettingValueString(0, 15, _msg)
1667
        val.set_mutable(True)
1668
        rs = RadioSetting("dispstr", "Display Message - Interface Display Edit", val)
1669
        key_grp.append(rs)
1670

    
1671
        dtmfchars = "0123456789"
1672
        _codeobj = _settings.ani_code
1673
        _code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x0A])
1674
        val = RadioSettingValueString(3, 6, _code, False)
1675
        val.set_charset(dtmfchars)
1676
        rs = RadioSetting("ani_code", "ANI Code", val)
1677
        def apply_ani_id(setting, obj):
1678
            value = []
1679
            for j in range(0, 6):
1680
                try:
1681
                    value.append(dtmfchars.index(str(setting.value)[j]))
1682
                except IndexError:
1683
                    value.append(0xFF)
1684
            obj.ani_code = value
1685
        rs.set_apply_callback(apply_ani_id, _settings)
1686
        key_grp.append(rs)
1687

    
1688
        rs = RadioSetting("pf1_shrt", "PF1 SHORT Key function",
1689
                          RadioSettingValueList(
1690
                              PFKEYSHORT_LIST,
1691
                              PFKEYSHORT_LIST[_settings.pf1_shrt]))
1692
        key_grp.append(rs)
1693
        rs = RadioSetting("pf1_long", "PF1 LONG Key function",
1694
                          RadioSettingValueList(
1695
                              PFKEYLONG_LIST,
1696
                              PFKEYLONG_LIST[_settings.pf1_long]))
1697
        key_grp.append(rs)
1698
        rs = RadioSetting("pf2_shrt", "PF2 SHORT Key function",
1699
                          RadioSettingValueList(
1700
                              PFKEYSHORT_LIST,
1701
                              PFKEYSHORT_LIST[_settings.pf2_shrt]))
1702
        key_grp.append(rs)
1703
        rs = RadioSetting("pf2_long", "PF2 LONG Key function",
1704
                          RadioSettingValueList(
1705
                              PFKEYLONG_LIST,
1706
                              PFKEYLONG_LIST[_settings.pf2_long]))
1707
        key_grp.append(rs)
1708

    
1709
#       SCAN GROUP settings
1710
        rs = RadioSetting("ScnGrpA_Act", "Scan Group A Active",
1711
                          RadioSettingValueList(SCANGRP_LIST, SCANGRP_LIST[_settings.ScnGrpA_Act]))
1712
        scan_grp.append(rs)
1713
        rs = RadioSetting("ScnGrpB_Act", "Scan Group B Active",
1714
                          RadioSettingValueList(SCANGRP_LIST, SCANGRP_LIST[_settings.ScnGrpB_Act]))
1715
        scan_grp.append(rs)
1716

    
1717
        for i in range(1, 11):
1718
            scgroup = str(i)
1719

    
1720
            rs = RadioSetting("scan_groups.Group_lower"+scgroup, "Scan Group "+scgroup+" Lower",
1721
                          RadioSettingValueInteger(1, 999, eval("self._memobj.scan_groups.Group_lower"+scgroup)))
1722
            scan_grp.append(rs)
1723

    
1724
            rs = RadioSetting("scan_groups.Group_upper"+scgroup, "Scan Group "+scgroup+" Upper",
1725
                          RadioSettingValueInteger(1, 999, eval("self._memobj.scan_groups.Group_upper"+scgroup)))
1726
            scan_grp.append(rs)
1727

    
1728
        # VFO A Settings
1729
        #
1730
        rs = RadioSetting("work_mode_a", "VFO A Workmode",
1731
                          RadioSettingValueList(WORKMODE_LIST, WORKMODE_LIST[_settings.work_mode_a]))
1732
        vfoa_grp.append(rs)
1733
        rs = RadioSetting("work_ch_a", "VFO A Work Channel",
1734
                          RadioSettingValueInteger(1, 999, _settings.work_ch_a))
1735
        vfoa_grp.append(rs)
1736
        rs = RadioSetting("vfoa.rxfreq", "VFO A Rx Frequency (MHz)",
1737
                          RadioSettingValueFloat(
1738
                              30.00000, 999.999999, (_vfoa.rxfreq / 100000.0), 0.000001, 6))
1739
        vfoa_grp.append(rs)
1740
        rs = RadioSetting("vfoa.power", "VFO A Power",
1741
                          RadioSettingValueList(
1742
                              POWER_LIST, POWER_LIST[_vfoa.power]))
1743
        vfoa_grp.append(rs)
1744
        rs = RadioSetting("vfoa.iswide", "VFO A Wide/Narrow",
1745
                          RadioSettingValueList(
1746
                              BANDWIDTH_LIST, BANDWIDTH_LIST[_vfoa.iswide]))
1747
        vfoa_grp.append(rs)
1748
        rs = RadioSetting("vfoa.mute_mode", "VFO A Mute (SP Mute)",
1749
                          RadioSettingValueList(
1750
                              SPMUTE_LIST, SPMUTE_LIST[_vfoa.mute_mode]))
1751
        vfoa_grp.append(rs)
1752
        rs = RadioSetting("VFO_repeater_a", "VFO A Repeater",
1753
                          RadioSettingValueBoolean(_settings.VFO_repeater_a))
1754
        vfoa_grp.append(rs)
1755

    
1756
        rs = RadioSetting("vfoa.scrambler", "VFO A Descramble",
1757
                          RadioSettingValueList(
1758
                              SCRAMBLE_LIST, SCRAMBLE_LIST[_vfoa.scrambler]))
1759
        vfoa_grp.append(rs)
1760

    
1761
        rs = RadioSetting("vfoa.cmpndr", "VFO A Compander",
1762
                         RadioSettingValueList(
1763
                             ONOFF_LIST, ONOFF_LIST[_vfoa.cmpndr]))
1764
        vfoa_grp.append(rs)
1765

    
1766
        rs = RadioSetting("vfoa.step", "VFO A Step (kHz)",
1767
                          RadioSettingValueList(
1768
                              STEP_LIST, STEP_LIST[_vfoa.step]))
1769
        vfoa_grp.append(rs)
1770
        rs = RadioSetting("vfoa.squelch", "VFO A Squelch",
1771
                          RadioSettingValueList(
1772
                              LIST_10, LIST_10[_vfoa.squelch]))
1773
        vfoa_grp.append(rs)
1774
        # VFO B Settings
1775
        #
1776
        rs = RadioSetting("work_mode_b", "VFO B Workmode",
1777
                          RadioSettingValueList(WORKMODE_LIST, WORKMODE_LIST[_settings.work_mode_b]))
1778
        vfob_grp.append(rs)
1779
        rs = RadioSetting("work_ch_b", "VFO B Work Channel",
1780
                          RadioSettingValueInteger(1, 999, _settings.work_ch_b))
1781
        vfob_grp.append(rs)
1782
        rs = RadioSetting("vfob.rxfreq", "VFO B Rx Frequency (MHz)",
1783
                          RadioSettingValueFloat(
1784
                              30.000000, 999.999999, (_vfob.rxfreq / 100000.0), 0.000001, 6))
1785
        vfob_grp.append(rs)
1786
        rs = RadioSetting("vfob.power", "VFO B Power",
1787
                          RadioSettingValueList(
1788
                              POWER_LIST, POWER_LIST[_vfob.power]))
1789
        vfob_grp.append(rs)
1790
        rs = RadioSetting("vfob.iswide", "VFO B Wide/Narrow",
1791
                          RadioSettingValueList(
1792
                              BANDWIDTH_LIST, BANDWIDTH_LIST[_vfob.iswide]))
1793
        vfob_grp.append(rs)
1794
        rs = RadioSetting("vfob.mute_mode", "VFO B Mute (SP Mute)",
1795
                          RadioSettingValueList(
1796
                              SPMUTE_LIST, SPMUTE_LIST[_vfob.mute_mode]))
1797
        vfob_grp.append(rs)
1798
        rs = RadioSetting("VFO_repeater_b", "VFO B Repeater",
1799
                          RadioSettingValueBoolean(_settings.VFO_repeater_b))
1800
        vfob_grp.append(rs)
1801

    
1802
        rs = RadioSetting("vfob.scrambler", "VFO B Descramble",
1803
                          RadioSettingValueList(
1804
                              SCRAMBLE_LIST, SCRAMBLE_LIST[_vfob.scrambler]))
1805
        vfob_grp.append(rs)
1806

    
1807
        rs = RadioSetting("vfob.cmpndr", "VFO B Compander",
1808
                         RadioSettingValueList(
1809
                             ONOFF_LIST, ONOFF_LIST[_vfob.cmpndr]))
1810
        vfob_grp.append(rs)
1811
        
1812
        rs = RadioSetting("vfob.step", "VFO B Step (kHz)",
1813
                          RadioSettingValueList(
1814
                              STEP_LIST, STEP_LIST[_vfob.step]))
1815
        vfob_grp.append(rs)
1816
        rs = RadioSetting("vfob.squelch", "VFO B Squelch",
1817
                          RadioSettingValueList(
1818
                              LIST_10, LIST_10[_vfob.squelch]))
1819
        vfob_grp.append(rs)
1820

    
1821
# FM RADIO PRESETS
1822

    
1823
        #memory stores raw integer value 760
1824
        #display shows 760 from memory , but frequency is actually 76.0 mhz
1825
        #radio will divide 760 by 10 and interpret correctly
1826
        #user inputs 760, displays 760
1827
        # rs = RadioSetting("FM_radio1", "FM1 in MHz*10", 
1828
        # rs = RadioSetting("FM_radio1", "FM1 in MHz*10", 
1829
        #                   RadioSettingValueFloat(0, 10800, (self._memobj.settings.FM_radio1), 0.1, 1))
1830
        # rs.set_apply_callback(apply_frequencyscaling4display, _settings.FM_radio1)    
1831
        # RadioSetting("FM_radio1", "FM1 in MHz*10",
1832
        #                  RadioSettingValueInteger(760, 1080, _settings.FM_radio1))
1833

    
1834
        for i in range(1, 21):
1835
            chan = str(i)
1836
            rs = RadioSetting("FM_radio"+ chan, "FM Preset"+chan,
1837
                # RadioSettingValueFloat(76.0, 108.0, _settings.FM_radio1 / 10.0, 0.01, 5))
1838
                        RadioSettingValueFloat(76.0, 108.0, eval("_settings.FM_radio"+chan)/10.0,  0.1, 1))
1839
            fmradio_grp.append(rs)
1840

    
1841
# Freq Limits settings
1842
        #
1843
        rs = RadioSetting("vhf_limits.rx_start", "VHF RX Lower Limit (MHz)",
1844
                          RadioSettingValueFloat(
1845
                              30.000000, 299.999999, (self._memobj.vhf_limits.rx_start / 100000.0), 0.000001, 6))
1846

    
1847
        lmt_grp.append(rs)
1848
        rs = RadioSetting("vhf_limits.rx_stop", "VHF RX Upper Limit (MHz)",
1849
                          RadioSettingValueFloat(
1850
                              30.000000, 299.999999, (self._memobj.vhf_limits.rx_stop / 100000.0), 0.000001, 6))
1851
        lmt_grp.append(rs)
1852
        # rs = RadioSetting("vhf_limits.tx_start", "VHF TX Lower Limit",
1853
        #                   RadioSettingValueInteger(
1854
        #                       10000000, 299999999,
1855
        #                       self._memobj.vhf_limits.tx_start * 10, 5000))
1856
        # val.set_mutable(False)
1857
        # vhf_lmt_grp.append(rs)
1858
        # rs = RadioSetting("vhf_limits.tx_stop", "VHF TX Upper Limit",
1859
        #                   RadioSettingValueInteger(
1860
        #                       10000000, 299999999,
1861
        #                       self._memobj.vhf_limits.tx_stop * 10, 5000))
1862
        # val.set_mutable(False)
1863
        # vhf_lmt_grp.append(rs)
1864

    
1865
        rs = RadioSetting("uhf_limits.rx_start", "UHF RX Lower Limit (MHz)",
1866
                          RadioSettingValueFloat(
1867
                              300.000000, 999.999999, (self._memobj.uhf_limits.rx_start / 100000.0), 0.000001, 6))
1868
        lmt_grp.append(rs)
1869
        rs = RadioSetting("uhf_limits.rx_stop", "UHF RX Upper Limit (MHz)",
1870
                          RadioSettingValueFloat(
1871
                              300.000000, 999.999999, (self._memobj.uhf_limits.rx_stop / 100000.0), 0.000001, 6))
1872
        lmt_grp.append(rs)
1873
        # rs = RadioSetting("uhf_limits.tx_start", "UHF TX Lower Limit",
1874
        #                   RadioSettingValueInteger(
1875
        #                       300000000, 999999999,
1876
        #                       self._memobj.uhf_limits.tx_start * 10, 5000))
1877
        # uhf_lmt_grp.append(rs)
1878
        # rs = RadioSetting("uhf_limits.tx_stop", "UHF TX Upper Limit",
1879
        #                   RadioSettingValueInteger(
1880
        #                       300000000, 999999999,
1881
        #                       self._memobj.uhf_limits.tx_stop * 10, 5000))
1882
        # uhf_lmt_grp.append(rs)
1883

    
1884

    
1885
# OEM info
1886
        #
1887
        def _decode(lst):
1888
            _str = ''.join([chr(c) for c in lst
1889
                            if chr(c) in chirp_common.CHARSET_ASCII])
1890
            return _str
1891

    
1892
        def do_nothing(setting, obj):
1893
            return
1894

    
1895
        _str = _decode(self._memobj.oem_info.model)
1896
        val = RadioSettingValueString(0, 8, _str)
1897
        val.set_mutable(True)
1898
        rs = RadioSetting("oem_info.model", "Model / Bottom Banner", val)
1899
        oem_grp.append(rs)
1900
        _str = _decode(self._memobj.oem_info.oem1)
1901
        val = RadioSettingValueString(0, 15, _str)
1902
        val.set_mutable(False)
1903
        rs = RadioSetting("oem_info.oem1", "OEM String 1", val)
1904
        rs.set_apply_callback(do_nothing, _settings)
1905
        oem_grp.append(rs)
1906
        _str = _decode(self._memobj.oem_info.oem2)
1907
        val = RadioSettingValueString(0, 15, _str)
1908
        val.set_mutable(False)
1909
        rs = RadioSetting("oem_info.oem2", "Firmware Version ??", val)
1910
        rs.set_apply_callback(do_nothing, _settings)
1911
        oem_grp.append(rs)
1912
        # _str = _decode(self._memobj.oem_info.version)
1913
        # val = RadioSettingValueString(0, 15, _str)
1914
        # val.set_mutable(False)
1915
        # rs = RadioSetting("oem_info.version", "Software Version", val)
1916
        # rs.set_apply_callback(do_nothing, _settings)
1917
        # oem_grp.append(rs)
1918
        _str = _decode(self._memobj.oem_info.date)
1919
        val = RadioSettingValueString(0, 15, _str)
1920
        val.set_mutable(False)
1921
        rs = RadioSetting("oem_info.date", "OEM Date", val)
1922
        rs.set_apply_callback(do_nothing, _settings)
1923
        oem_grp.append(rs)
1924

    
1925
        return group
1926

    
1927

    
1928

    
1929

    
1930
        return group
1931

    
1932
    def get_settings(self):
1933
        try:
1934
            return self._get_settings()
1935
        except:
1936
            import traceback
1937
            LOG.error("Failed to parse settings: %s", traceback.format_exc())
1938
            return None
1939

    
1940
    def set_settings(self, settings):
1941
        for element in settings:
1942
            if not isinstance(element, RadioSetting):
1943
                self.set_settings(element)
1944
                continue
1945
            else:
1946
                try:
1947
                    if "." in element.get_name():
1948
                        bits = element.get_name().split(".")
1949
                        obj = self._memobj
1950
                        for bit in bits[:-1]:
1951
                            obj = getattr(obj, bit)
1952
                        setting = bits[-1]
1953
                    else:
1954
                        obj = self._memobj.settings
1955
                        setting = element.get_name()
1956

    
1957
                    if element.has_apply_callback():
1958
                        LOG.debug("Using apply callback")
1959
                        element.run_apply_callback()
1960
                    else:
1961
                        LOG.debug("Setting %s = %s" % (setting, element.value))
1962
                        if self._is_freq(element):
1963
                            # setattr(obj, setting, int(element.value / 10))
1964
                            setattr(obj, setting, int(element.values()[0]._current *100000.0))
1965
                        elif self._is_fmradio(element):
1966
                            setattr(obj, setting, int(element.values()[0]._current *10.0))
1967
                        else:
1968
                            setattr(obj, setting, element.value)
1969
                except Exception, e:
1970
                    LOG.debug(element.get_name())
1971
                    raise
1972

    
1973
    def _is_freq(self, element):
1974
        return "rxfreq" in element.get_name() or "txoffset" in element.get_name() or "rx_start" in element.get_name() or "rx_stop" in element.get_name() or "tx_start" in element.get_name() or "tx_stop" in element.get_name()
1975

    
1976
    def _is_fmradio(self, element):
1977
        return "FM_radio" in element.get_name()
1978
        
(16-16/18)