Project

General

Profile

New Model #10498 » kguv980p with 950P b5.py

fixed syntax error issue with latest revision of Chirp - Mel Terechenok, 11/08/2023 04:05 PM

 
1
# Copyright 2022 Mel Terechenok <melvin.terechenok@gmail.com>
2
# KG-UV980P , KG-1000G, KG-1000G Plus, KG-UV950P unified driver using
3
# KG-935G, KG-UV8H, KG-UV920P, and KG-UV9D Plus drivers as resources
4
#
5
# Based on the previous work of Pavel Milanes CO7WT <pavelmc@gmail.com>
6
# and Krystian Struzik <toner_82@tlen.pl>
7
# who figured out the crypt used and made possible the
8
# Wuoxun KG-UV8D Plus driver.
9
#
10
# This program is free software: you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation, either version 3 of the License, or
13
# (at your option) any later version.
14
#
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
# GNU General Public License for more details.
19
#
20
# You should have received a copy of the GNU General Public License
21
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22

    
23
"""Wouxun KG-UV980P radio management module"""
24

    
25
import time
26
import logging
27
import struct
28

    
29
from chirp import util, chirp_common, bitwise, memmap, errors, directory
30
from chirp.settings import RadioSetting, RadioSettingGroup, \
31
    RadioSettingValueBoolean, RadioSettingValueList, \
32
    RadioSettingValueInteger, RadioSettingValueString, \
33
    RadioSettingValueFloat, RadioSettingValueMap, RadioSettings
34

    
35

    
36
LOG = logging.getLogger(__name__)
37

    
38

    
39
CMD_ID = 0x80
40
CMD_END = 0x81
41
CMD_RD = 0x82
42
CMD_WR = 0x83
43
DIR_TO = 0xFF
44
DIR_FROM = 0x00
45

    
46

    
47
# This is used to write the configuration of the radio base on info
48
# gleaned from the downloaded app. There are empty spaces and we honor
49
# them because we don't know what they are (yet) although we read the
50
# whole of memory.
51
# Max Write Size = 64 for comm protocol
52

    
53
config_map = (          # map address, write size, write count
54
    # (0x0, 64, 512),     # 0 to 8000 - full write use only
55
    (0x4c,  12, 1),    # Mode PSW --  Display name
56
    (0x60,  44, 1),    # Freq Limits
57
    (0x740,  40, 1),    # FM chan 1-20
58
    (0x830,  16, 13),    # General settings
59
    (0x900, 8, 1),       # General settings
60
    (0x940,  64, 2),    # Scan groups and names
61
    (0xa00,  64, 249),  # Memory Channels 1-996
62
    (0x4840, 48, 1),    # Memory Channels 997-999
63
    (0x4900, 64, 124),  # Memory Names    1-992
64
    (0x6800, 8, 7),    # Memory Names    997-999
65
    (0x7000, 64, 15),    # mem valid 1 -960
66
    (0x73c0, 39, 1),    # mem valid 961 - 999
67
)
68

    
69

    
70
MEM_VALID = 0x00
71
TX_BLANK = 0x40
72
RX_BLANK = 0x80
73

    
74
CHARSET_NUMERIC = "0123456789"
75
CHARSET = "0123456789" + \
76
          ":;<=>?@" + \
77
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + \
78
          "[\\]^_`" + \
79
          "abcdefghijklmnopqrstuvwxyz" + \
80
          "{|}~\x4E" + \
81
          " !\"#$%&'()*+,-./"
82

    
83
SCANNAME_CHARSET = "0123456789" + \
84
          ":;<=>?@" + \
85
          "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + \
86
          "[\\]^_`" + \
87
          "abcdefghijklmnopqrstuvwxyz" + \
88
          "{|}~\x4E" + \
89
          " !\"#$%&'()*+,-./"
90

    
91
MUTE_MODE_MAP = [('QT',      0b01),
92
                 ('QT*DTMF', 0b10),
93
                 ('QT+DTMF', 0b11)]
94
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 30.0, 50.0, 100.0]
95
STEP_LIST = [str(x) for x in STEPS]
96
SQL_LIST = [int(i) for i in range(0, 10)]
97
M_POWER_MAP = [('1 = 20W', 1),
98
               ('2 = 10W', 2)]
99
ROGER_LIST = ["Off", "BOT", "EOT", "Both"]
100
VOICE_MAP = [('Off', 0),
101
             ('Chinese', 1),
102
             ('On / English', 2)]
103
VOICE_MAP_1000GPLUS = [('Off', 0),
104
                       ('On', 2)]
105
SC_REV_MAP = [('Timeout (TO)',  1),
106
              ('Carrier (CO)',  2),
107
              ('Stop (SE)',     3)]
108
TOT_MAP = [('%d min' % i, int('%02d' % i, 10)) for i in range(1, 61)]
109
TOT_MAP_1000GPLUS = [('%d min' % i, int('%02d' % i, 16)) for i in range(1, 61)]
110
TOA_LIST = ["Off", "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", "10s"]
111
TOA_MAP = [('Off', 0)] + \
112
          [('%ds' % i, int('%02d' % i, 16)) for i in range(1, 11)]
113
RING_LIST = ["Off", "1s", "2s", "3s", "4s", "5s", "6s", "7s",
114
             "8s", "9s", "10s"]
115
RING_MAP = [('Off', 0)] + \
116
           [('%ds' % i, int('%02d' % i, 10)) for i in range(1, 11)]
117
DTMF_ST_LIST = ["Off", "DT-ST", "ANI-ST", "DT+ANI"]
118
DTMF_ST_LIST_1000GPLUS = ["Off", "DTMF", "ID", "DTMF+ID"]
119

    
120
PTT_ID_MAP = [('BOT',  1),
121
              ('EOT',  2),
122
              ('Both', 3)]
123
PTT_ID_MAP_1000GPLUS = [('Off', 0),
124
                        ('BOT',  1),
125
                        ('EOT',  2),
126
                        ('Both', 3)]
127
BACKLIGHT_LIST = ["Off", "Red", "Orange", "Green"]
128
BACKLIGHT_LIST_950 = ["Off", "White", "Blue", "Green" ]
129
SPEAKER_MAP = [('SPK_1',   1),
130
               ('SPK_2',   2),
131
               ('SPK_1+2', 3)]
132
SPEAKER_MAP_1000GPLUS = [('RADIO',   1),
133
                         ('MIC',   2),
134
                         ('BOTH', 3)]
135
RPT_MODE_LIST = ["Radio", "X-DIRPT", "X-TWRPT", "RPT-RX", "T-W RPT"]
136
RPT_MODE_MAP = [("OFF", 0),
137
                ("RPT-RX", 3),
138
                ("RPT-TX", 4)]
139
APO_TIME_LIST = ["Off", "30", "60", "90", "120", "150"]
140
ALERT_MAP = [('1750', 1),
141
             ('2100', 2),
142
             ('1000', 3),
143
             ('1450', 4)]
144
FAN_MODE_LIST = ["TX", "Hi-Temp/TX", "Always"]
145
SCAN_GROUP_LIST = ["All"] + ["%s" % x for x in range(1, 11)]
146
WORKMODE_MAP = [('VFO',             1),
147
                ('Ch. No.',         2),
148
                ('Ch. No.+Freq.',   3),
149
                ('Ch. No.+Name',    4)]
150
WORKMODE_MAP_1000GPLUS = [('Freq',   1),
151
                          ('Ch-Num',  2),
152
                          ('Ch-Freq', 3),
153
                          ('Ch-Name', 4)]
154

    
155
VFOBAND_MAP = [("150M", 0),
156
               ("450M", 1),
157
               ("20M", 2),
158
               ("50M", 3),
159
               ("350M", 4),
160
               ("850M", 5)]
161
AB_LIST = ["A", "B"]
162
POWER_MAP = [('Low', 0),
163
             ('Med', 1),
164
             ('Med2', 2),
165
             ('High', 3)]
166
BANDWIDTH_MAP = [('Narrow', 1),
167
                 ('Wide',  0)]
168
SCRAMBLER_LIST = ["Off", "1", "2", "3", "4", "5", "6", "7", "8"]
169
ANS_LIST = ["Off", "Normal", "Strong"]
170
DTMF_TIMES = [str(x) for x in range(80, 501, 20)]
171
DTMF_INTERVALS = [str(x) for x in range(60, 501, 20)]
172
ROGER_TIMES = [str(x) for x in range(20, 1001, 20)]
173
# For py3 compliance x/100 must be changed to x//100
174
PTT_ID_DELAY_MAP = [(str(x), x//100) for x in range(100, 1001, 100)]
175
ROGER_INTERVALS = ROGER_TIMES
176
TONE_MAP = [('Off', 0x0000)] + \
177
           [('%.1f' % tone, int(tone * 10)) for tone in chirp_common.TONES] + \
178
           [('DN%d' % tone, int(0x8000 + tone))
179
               for tone in chirp_common.DTCS_CODES] + \
180
           [('DI%d' % tone, int(0xC000 + tone))
181
               for tone in chirp_common.DTCS_CODES]
182
DUPLEX_LIST = ["Off", "Plus", "Minus"]
183
SC_QT_MAP = [("Decoder - Rx QT/DT MEM", 1), ("Encoder- Tx QT/DT MEM", 2),
184
             ("All- RxTx QT/DT MEM", 3)]
185
SC_QT_MAP_1000GPLUS = [("Rx", 1), ("Tx", 2),
186
                       ("Tx/Rx", 3)]
187

    
188
HOLD_TIMES = ["Off"] + ["%s" % x for x in range(100, 5001, 100)]
189
PF1_SETTINGS = ["Off", "Stun", "Kill", "Monitor", "Inspection"]
190
PF1_SETTINGS_1000GPLUS = ["OFF", "Reverse", "Pri-Sel", "Pri-Scan", "Squelch",
191
                          "TX PWR", "Scan", "Scan CTCSS",
192
                          "Scan DCS", "FM Radio", "Weather", "Ch-Add", "W-N",
193
                          "TDR", "WORKMODE", "Band", "Repeater", "Lock",
194
                          "Monitor"]
195

    
196
ABR_LIST = ["Always", "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s",
197
            "9s", "10s", "11s", "12s", "13s", "14s", "15s", "16s", "17s",
198
            "18s", "19s", "20s", "Off"]
199
KEY_LIST = ["Off", "B/SW", "MENCH", "H-M-L", "VFO/MR", "SET-D", "TDR",
200
            "SQL", "SCAN", "FM-Radio", "Scan CTCSS", "Scan DCS"]
201
KEY_LIST_1000GPLUS = ["OFF", "Reverse", "Pri-Sel", "Pri-Scan", "Squelch",
202
                      "TX PWR", "Scan", "Scan CTCSS", "Scan DCS",
203
                      "FM Radio", "Weather", "Ch-Add", "W-N", "TDR",
204
                      "WORKMODE", "Band", "Repeater", "Lock", "Monitor"]
205

    
206
RC_POWER_LIST = ["RC Stop", "RC Open"]
207
ACTIVE_AREA_LIST = ["Area A - Left", "Area B - Right"]
208
TDR_LIST = ["TDR ON", "TDR OFF"]
209
PRI_CH_SCAN_LIST = ["Off", "ON-Stby", "On-Always"]
210

    
211
# memory slot 0 is not used, start at 1 (so need 1000 slots, not 999)
212
# structure elements whose name starts with x are currently unidentified
213

    
214
_MEM_FORMAT = """
215
    #seekto 0x004c;
216
    struct {
217
        u24 mode_psw;
218
        u8  xunk04F;
219
        u8  display_name[8];
220
    } oem;
221

    
222
    #seekto 0x0060;
223
    struct {
224
        bbcd    limit_144M_ChA_rx_start[2];
225
        bbcd    limit_144M_ChA_rx_stop[2];
226
        bbcd    limit_70cm_rx_start[2];
227
        bbcd    limit_70cm_rx_stop[2];
228
        bbcd    limit_10m_rx_start[2];
229
        bbcd    limit_10m_rx_stop[2];
230
        bbcd    limit_6m_rx_start[2];
231
        bbcd    limit_6m_rx_stop[2];
232
        bbcd    limit_350M_rx_start[2];
233
        bbcd    limit_350M_rx_stop[2];
234
        bbcd    limit_850M_rx_start[2];
235
        bbcd    limit_850M_rx_stop[2];
236
        bbcd    limit_144M_tx_start[2];
237
        bbcd    limit_144M_tx_stop[2];
238
        bbcd    limit_70cm_tx_start[2];
239
        bbcd    limit_70cm_tx_stop[2];
240
        bbcd    limit_10m_tx_start[2];
241
        bbcd    limit_10m_tx_stop[2];
242
        bbcd    limit_6m_tx_start[2];
243
        bbcd    limit_6m_tx_stop[2];
244
        bbcd    limit_144M_ChB_rx_start[2];
245
        bbcd    limit_144M_ChB_rx_stop[2];
246
    } bandlimits;
247

    
248

    
249
    #seekto 0x0740;
250
    struct {
251
        u16    FM_radio1;
252
        u16    FM_radio2;
253
        u16    FM_radio3;
254
        u16    FM_radio4;
255
        u16    FM_radio5;
256
        u16    FM_radio6;
257
        u16    FM_radio7;
258
        u16    FM_radio8;
259
        u16    FM_radio9;
260
        u16    FM_radio10;
261
        u16    FM_radio11;
262
        u16    FM_radio12;
263
        u16    FM_radio13;
264
        u16    FM_radio14;
265
        u16    FM_radio15;
266
        u16    FM_radio16;
267
        u16    FM_radio17;
268
        u16    FM_radio18;
269
        u16    FM_radio19;
270
        u16    FM_radio20;
271
        u16    FM_radio21;
272
        u16    FM_radio22;
273
        u8  x76c_pad[196];
274
        u32  vfofreq1;     // 0x0830
275
        u32  vfoofst1;
276
        u16  txtone1;
277
        u16  rxtone1;
278
        u8  xunk83C_1:3,
279
            mute1:2,
280
            xunk83C_2:3;
281
        u8  xunk83d_1:1,
282
            xunk83d_2:1,
283
            xunk83d_3:1,
284
            power1:2,
285
            am_mode1:1,
286
            xunk83d_7:1,
287
            narrow1:1;
288
        u8  xunk83e:6,
289
            shft_dir1:2;
290
        u8  xunk83F:3,
291
            compander1:1,
292
            scrambler1:4;
293
        u32  vfofreq2;
294
        u32  vfoofst2;
295
        u16  txtone2;
296
        u16  rxtone2;
297
        u8  xunk84C_1:3,
298
            mute2:2,
299
            xunk84C_2:3;
300
        u8  xunk84d_1:1,
301
            xunk84d_2:1,
302
            xunk84d_3:1,
303
            power2:2,
304
            am_mode2:1,
305
            xunk84d_7:1,
306
            narrow2:1;
307
        u8  xunk84e:6,
308
            shft_dir2:2;
309
        u8  xunk84F:3,
310
            compander2:1,
311
            scrambler2:4;
312
        u32  vfofreq3;
313
        u32  vfoofst3;
314
        u16  txtone3;
315
        u16  rxtone3;
316
        u8  xunk85C_1:3,
317
            mute3:2,
318
            xunk85C_2:3;
319
        u8  xunk85d_1:1,
320
            xunk85d_2:1,
321
            xunk85d_3:1,
322
            power3:2,
323
            am_mode3:1,
324
            xunk85d_7:1,
325
            narrow3:1;
326
        u8  xunk85e:6,
327
            shft_dir3:2;
328
        u8  xunk85F:3,
329
            compander3:1,
330
            scrambler3:4;
331
        u32  vfofreq4;
332
        u32  vfoofst4;
333
        u16  txtone4;
334
        u16  rxtone4;
335
        u8  xunk86C_1:3,
336
            mute4:2,
337
            xunk86C_2:3;
338
        u8  xunk86d_1:1,
339
            xunk86d_2:1,
340
            xunk86d_3:1,
341
            power4:2,
342
            am_mode4:1,
343
            xunk86d_7:1,
344
            narrow4:1;
345
        u8  xunk86e:6,
346
            shft_dir4:2;
347
        u8  xunk86F:3,
348
            compander4:1,
349
            scrambler4:4;
350
        u32  vfofreq5;
351
        u32  vfoofst5;
352
        u16  txtone5;
353
        u16  rxtone5;
354
        u8  xunk87C_1:3,
355
            mute5:2,
356
            xunk87C_2:3;
357
        u8  xunk87d_1:1,
358
            xunk87d_2:1,
359
            xunk87d_3:1,
360
            power5:2,
361
            am_mode5:1,
362
            xunk87d_7:1,
363
            narrow5:1;
364
        u8  xunk87e:6,
365
            shft_dir5:2;
366
        u8  xunk87F:3,
367
            compander5:1,
368
            scrambler5:4;
369
        u32  vfofreq6;
370
        u32  vfoofst6;
371
        u16  txtone6;
372
        u16  rxtone6;
373
        u8  xunk88C_1:3,
374
            mute6:2,
375
            xunk88C_2:3;
376
        u8  xunk88d_1:1,
377
            xunk88d_2:1,
378
            xunk88d_3:1,
379
            power6:2,
380
            am_mode6:1,
381
            xunk88d_7:1,
382
            narrow6:1;
383
        u8  xunk88e:6,
384
            shft_dir6:2;
385
        u8  xunk8F:3,
386
            compander6:1,
387
            scrambler6:4;
388
        u32  vfofreq7;
389
        u32  vfoofst7;
390
        u16  txtone7;
391
        u16  rxtone7;
392
        u8  xunk89C_1:3,
393
            mute7:2,
394
            xunk89C_2:3;
395
        u8  xunk89d_1:1,
396
            xunk89d_2:1,
397
            xunk89d_3:1,
398
            power7:2,
399
            am_mode7:1,
400
            xunk89d_7:1,
401
            narrow7:1;
402
        u8  xunk89e:6,
403
            shft_dir7:2;
404
        u8  xunk89F:3,
405
            compander7:1,
406
            scrambler7:4;
407
        u8  x8a0;
408
        u16  vfochan_a;
409
        u8  x8a3;
410
        u16  vfochan_b;
411
        u16  pri_ch;
412
        u8  x8a8;
413
        u8  x8a9;
414
        u8  scan_a_act;
415
        u8  scan_b_act;
416
        u8  m_pwr;
417
        u8  hold_time_rpt;
418
        u8  spk_cont;
419
        u8  x8af;
420
        u8  rc_power;
421
        u8  voice;
422
        u8  tot;
423
        u8  toa;
424
        u8  roger;
425
        u8  sc_rev;
426
        u8  dtmfsf;
427
        u8  ptt_id;
428
        u8  ring;
429
        u8  ani_sw;
430
        u8  rc_sw;
431
        u8  alert;
432
        u8  bcl_a;
433
        u8  prich_sw;
434
        u8  x8bE;
435
        u8  ptt_id_dly;
436
        u8  menu;
437
        u8  x8c1;
438
        u8  beep;
439
        u8  key_lock;
440
        u8  x8c4;
441
        u8  tx_led;
442
        u8  wt_led;
443
        u8  rx_led;
444
        u8  act_area;
445
        u8  vfomode_a;
446
        u8  vfomode_b;
447
        u8  vfosquelch_a;
448
        u8  vfosquelch_b;
449
        u8  vfostep_a;
450
        u8  vfostep_b;
451
        u8  tdr_off;
452
        u8  rpt_spk;       //x8d0
453
        u8  rpt_ptt;
454
        u8  autolock;
455
        u8  apo_time;
456
        u8  low_v;
457
        u8  fan;
458
        u8  rpt_set_model;
459
        u8  pf1_set;
460
        u8  auto_am;
461
        u8  dtmf_time;
462
        u8  dtmf_int;
463
        u8  bcl_b;
464
        u8  rpt_tone;
465
        u8  sc_qt;
466
        u8  vfoband_a;
467
        u8  x8dF;
468
        u24  ani_edit;
469
        u8  x8e3;
470
        u24  mcc_edit;
471
        u8  x8e7;
472
        u24  scc_edit;
473
        u8  x8eB;
474
        u24  ctrl_edit;
475
        u8  x8eF;
476
        u8  KeyA;
477
        u8  KeyB;
478
        u8  KeyC;
479
        u8  ABR;
480
        u8  x8f4;
481
        u8  x8f5;
482
        u8  KeyD;       // KG-1000G Plus ONLY
483
        u8  x8f7;
484
        u8  x8f8;
485
        u8  x8f9;
486
        u8  x8fA;
487
        u8  x8fB;
488
        u8  x8fC;
489
        u8  x8fD;
490
        u8  x8fE;
491
        u8  x8fF;
492
        u16  FM_radio_cur_freq;
493
        u8  x902;
494
        u8  scan_det;
495
        u8  x904;
496
        u8  thr_vol_tx;
497
        u16  thr_vol_lvl;
498
        u8  x908;
499
        u8  x909;
500
        u8  x90A;
501
        u8  x90B;
502
        u8  x90C;
503
        u8  x90D;
504
        u8  x90E;
505
        u8  x90F;
506
        u8  x910pad[48];
507
        u16  scanlower1; // x940
508
        u16  scanupper1;
509
        u16  scanlower2;
510
        u16  scanupper2;
511
        u16  scanlower3;
512
        u16  scanupper3;
513
        u16  scanlower4;
514
        u16  scanupper4;
515
        u16  scanlower5;
516
        u16  scanupper5;
517
        u16  scanlower6;
518
        u16  scanupper6;
519
        u16  scanlower7;
520
        u16  scanupper7;
521
        u16  scanlower8;
522
        u16  scanupper8;
523
        u16  scanlower9;
524
        u16  scanupper9;
525
        u16  scanlower10;
526
        u16  scanupper10;
527
        u8  scanname0[8]; // x968
528
        u8  scanname1[8];
529
        u8  scanname2[8];
530
        u8  scanname3[8];
531
        u8  scanname4[8];
532
        u8  scanname5[8];
533
        u8  scanname6[8];
534
        u8  scanname7[8];
535
        u8  scanname8[8];
536
        u8  scanname9[8];
537
        u8  scanname10[8];
538
     } settings;
539

    
540

    
541
    #seekto 0x09f0;
542
    struct {
543
        u32     rxfreq;
544
        u32     txfreq;
545
        u16     txtone;
546
        u16     rxtone;
547
        u8      unknown1:3,
548
                mute_mode:2,
549
                unknown2:3;
550
        u8      named:1,
551
                scan_add:1,
552
                extra_power_bit:1,
553
                power:2,
554
                am_mode:1,
555
                unknownbit2:1,
556
                isnarrow:1;
557
        u8      unknown3:6,
558
                Unknown4_shft_dir:2;
559
        u8      unknown5:3,
560
                compander:1,
561
                scrambler:4;
562
    } memory[1000];
563

    
564
    #seekto 0x48f8;
565
    struct {
566
        u8    name[8];
567
    } names[1000];
568

    
569
    #seekto 0x6fff;
570
    u8          valid[1000];
571
    """
572

    
573

    
574
def _freq_decode(in_freq, bytes=4):
575
    out_freq = 0
576
    for i in range(bytes*2):
577
        out_freq += (in_freq & 0xF) * (10 ** i)
578
        in_freq = in_freq >> 4
579
    if bytes == 4:
580
        return out_freq * 10
581
    elif bytes == 2:
582
        return out_freq * 100000
583

    
584

    
585
def _freq_encode(in_freq, bytes=4):
586
    if bytes == 4:
587
        return int('%08d' % (in_freq / 10), 16)
588
    elif bytes == 2:
589
        return int('%04d' % (in_freq / 100000), 16)
590

    
591

    
592
def _oem_str_decode(in_str):
593
    out_str = ''
594
    stopchar = False
595
    for c in in_str:
596
        if c != 0x50 and stopchar is False:
597
            if chr(c+48) in chirp_common.CHARSET_ASCII:
598
                out_str += chr(c+48)
599
        else:
600
            out_str += ''
601
            stopchar = True
602
    return out_str
603

    
604

    
605
def _oem_str_encode(in_str):
606
    out_str = ''
607
    LOG.debug("OEM Input String = %s", in_str)
608
    for c in in_str:
609
        try:
610
            out_str += chr(int(ord(c))-48)
611
        except ValueError:
612
            pass
613
    while len(out_str) < 8:
614
        out_str += chr(0x50)
615
    LOG.debug("OEM Ouput String = %s", out_str)
616
    return out_str
617

    
618

    
619
# OEM String Encode for KG-1000G Plus
620
def _oem_str_decode_1000GPLUS(in_str):
621
    LOG.debug("decode OEM Input String = %s", in_str)
622
    out_str = ''
623
    for c in in_str:
624
        # 1000G+ character mapping starts with P = 32 and O = 127
625
        if 127 >= c >= 80:
626
            out_str += chr(c - 48)
627
        elif 32 <= c < 80:
628
            out_str += chr(c+48)
629
        else:
630
            out_str += ''
631
    LOG.debug("decode OEM Ouput String = %s", out_str)
632
    return out_str
633

    
634

    
635
# OEM String Encode for KG-1000G Plus
636
def _oem_str_encode_1000GPLUS(in_str):
637
    out_str = ''
638
    LOG.debug("encode OEM Input String = %s", in_str)
639
    for c in in_str:
640
        if 32 <= ord(c) < 80:
641
            out_str += chr(int(ord(c)) + 48)
642
        elif 127 >= ord(c) >= 80:
643
            out_str += chr(int(ord(c)) - 48)
644
    while len(out_str) < 8:
645
        out_str += chr(0x50)
646
    LOG.debug("encode OEM Ouput String = %s", out_str)
647
    return out_str
648

    
649

    
650
def _str_decode_950(in_str):
651
    out_str = ''
652
    for c in in_str:
653
        if c > 95:
654
            out_str += ''
655
        elif c >= 80:
656
            out_str += chr(c-48)
657
        else:
658
            out_str += chr(c+48)
659

    
660
    return out_str
661

    
662

    
663
def _str_decode(in_str):
664
    out_str = ''
665
    stopchar = False
666
    for c in in_str:
667
        if c != 0x00 and stopchar is False:
668
            if chr(c) in chirp_common.CHARSET_ASCII:
669
                out_str += chr(c)
670
        else:
671
            out_str += ''
672
            stopchar = True
673
    return out_str
674

    
675

    
676
def _str_encode(in_str):
677
    out_str = ''
678
    for c in in_str:
679
        try:
680
            out_str += chr(ord(c))
681
        except ValueError:
682
            pass
683
    while len(out_str) < 8:
684
        out_str += chr(0x00)
685
    if out_str == "        " or out_str == "":
686
        out_str = "\x00\x00\x00\x00\x00\x00\x00\x00"
687
    return out_str
688

    
689

    
690
def _str_encode_950(in_str):
691
    out_str = ''
692
    for c in in_str:
693
        if ord(c) < 32 or ord(c)> 126:
694
            out_str += ''
695
        elif ord(c) >= 32 and ord(c)<= 47:
696
            out_str += chr(int(ord(c)+48))
697
        else:
698
            out_str += chr(int(ord(c)-48))         
699
    while len(out_str) < 8:
700
        out_str += chr(0x50)
701
    if out_str == "        " or out_str == "":
702
        out_str = "\x00\x00\x00\x00\x00\x00\x00\x00"
703
    return out_str
704

    
705

    
706
def _chnum_decode(in_ch):
707
    return int(('%04x' % in_ch)[0:3])
708

    
709

    
710
def _chnum_encode(in_ch):
711
    return int('%03d0' % in_ch, 16)
712

    
713
# Support for the Wouxun KG-UV980P radio
714
# Serial coms are at 19200 baud
715
# The data is passed in variable length records
716
# Record structure:
717
#  Offset   Usage
718
#    0      start of record (\x7c)
719
#    1      Command (\x80 Identify \x81 End/Reboot \x82 Read \x83 Write)
720
#    2      direction (\xff PC-> Radio, \x00 Radio -> PC)
721
#    3      length of payload (excluding header/checksum) (n)
722
#    4      payload (n bytes)
723
#    4+n+1  checksum - only lower 4 bits of byte sum (% 256) of bytes 1 -> 4+n
724
#
725
# Memory Read Records:
726
# the payload is 3 bytes, first 2 are offset (big endian),
727
# 3rd is number of bytes to read
728
# Memory Write Records:
729
# the maximum payload size (from the Wouxun software) seems to be 66 bytes
730
#  (2 bytes location + 64 bytes data).
731

    
732

    
733
@directory.register
734
class KG980PRadio(chirp_common.CloneModeRadio,
735
                  chirp_common.ExperimentalRadio):
736

    
737
    """Wouxun KG-UV980P"""
738
    VENDOR = "Wouxun"
739
    MODEL = "KG-UV980P"
740
    _model = b"KG-UV950R2"
741
    _file_ident = b"980P"
742
    BAUD_RATE = 19200
743
    NEEDS_COMPAT_SERIAL = False
744
    # Start Byte for Communication messages
745
    _record_start = 0xDA
746
    # _cs_size = 0x0F for 4-bit checksum, 0xFF for 8-Bit checksum
747
    _cs_size = 0x0F
748
    # _valxor = value needed to encrypt/decrypt the bytes to/from the radio
749
    _valxor = 0x57
750
    POWER_LEVELS = [chirp_common.PowerLevel("L", watts=1.0),
751
                    chirp_common.PowerLevel("M", watts=20.0),
752
                    chirp_common.PowerLevel("H", watts=50.0)]
753

    
754
    def _checksum(self, data):
755
        cs = 0
756
        for byte in data:
757
            cs += byte
758
        return ((cs % 256) & self._cs_size)
759

    
760
    def _write_record(self, cmd, payload=b''):
761
        _packet = struct.pack('BBBB', self._record_start, cmd, 0xFF,
762
                              len(payload))
763
        checksum = bytes([self._checksum(_packet[1:] + payload)])
764
        _packet += self.encrypt(payload + checksum)
765
        LOG.debug("Sent:\n%s" % util.hexprint(_packet))
766
        self.pipe.write(_packet)
767

    
768
    def _read_record(self):
769
        # read 4 chars for the header
770
        _header = self.pipe.read(4)
771
        if len(_header) != 4:
772
            raise errors.RadioError('Radio did not respond')
773
        _length = struct.unpack('xxxB', _header)[0]
774
        _packet = self.pipe.read(_length)
775
        _rcs_xor = _packet[-1]
776
        _packet = self.decrypt(_packet)
777
        _cs = self._checksum(_header[1:])
778
        _cs += self._checksum(_packet)
779
        _cs %= 256
780
        _cs = _cs & self._cs_size
781
        _rcs = self.strxor(self.pipe.read(1)[0], _rcs_xor)[0]
782
        return (_rcs != _cs, _packet)
783

    
784
    def decrypt(self, data):
785
        result = b''
786
        for i in range(len(data)-1, 0, -1):
787
            result += self.strxor(data[i], data[i - 1])
788
        result += self.strxor(data[0], self._valxor)
789
        return result[::-1]
790

    
791
    def encrypt(self, data):
792
        result = self.strxor(self._valxor, data[0])
793
        for i in range(1, len(data), 1):
794
            result += self.strxor(result[i - 1], data[i])
795
        return result
796

    
797
    def strxor(self, xora, xorb):
798
        return bytes([xora ^ xorb])
799

    
800
    # Identify the radio
801
    #
802
    # A Gotcha: the first identify packet returns a bad checksum, subsequent
803
    # attempts return the correct checksum... (well it does on my radio!)
804
    #
805
    # The ID record returned by the radio also includes the
806
    # current frequency range
807
    # as 4 bytes big-endian in 10Hz increments
808
    #
809
    # Offset
810
    #  0:10     Model, zero padded
811

    
812
    def _identify(self):
813
        """Do the identification dance"""
814
        for _i in range(0, 3):
815
            LOG.debug("ID try #"+str(_i))
816
            self._write_record(CMD_ID)
817
            _chksum_err, _resp = self._read_record()
818
            if len(_resp) == 0:
819
                raise errors.RadioError("Radio not responding")
820
            else:
821
                LOG.debug("Got:\n%s" % util.hexprint(_resp))
822
                LOG.debug("Model received is %s" % _resp[0:10])
823
                LOG.debug("Model expected is %s" % self._model)
824
                if _chksum_err:
825
                    LOG.error("Checksum error: retrying ident...")
826
                    time.sleep(0.100)
827
                    continue
828
                else:
829
                    LOG.debug("checksum passed")
830
                    if _resp[0:8] == self._model[0:8]:
831
                        LOG.debug("Passed identify")
832
                        break
833
                    else:
834
                        LOG.debug("FAILED to identify")
835
                        raise errors.RadioError("Failed Identification")
836

    
837
    def _finish(self):
838
        self._write_record(CMD_END)
839

    
840
    def process_mmap(self):
841
        self._memobj = bitwise.parse(_MEM_FORMAT, self._mmap)
842

    
843
    def sync_in(self):
844
        try:
845
            self._mmap = self._download()
846
        except errors.RadioError:
847
            raise
848
        except Exception:
849
            raise errors.RadioError("Failed to communicate with radio: %s")
850
        self.process_mmap()
851

    
852
    def sync_out(self):
853
        self._upload()
854

    
855
    # TODO: Load all memory.
856
    # It would be smarter to only load the active areas and none of
857
    # the padding/unused areas. Padding still need to be investigated.
858
    def _download(self):
859
        """Talk to a wouxun KG-UV980P and do a download"""
860
        try:
861
            self._identify()
862
            return self._do_download(0, 32768, 64)
863
        except errors.RadioError:
864
            raise
865
        except Exception:
866
            LOG.exception('Unknown error during download process')
867
            raise errors.RadioError("Failed to communicate with radio: %s")
868

    
869
    def _do_download(self, start, end, size):
870
        # allocate & fill memory
871
        LOG.debug("Start Download")
872
        image = b""
873
        for i in range(start, end, size):
874
            req = struct.pack("BBB", int(i / 256), int(i % 256), int(size))
875
            self._write_record(CMD_RD, req)
876
            cs_error, resp = self._read_record()
877
            if cs_error:
878
                LOG.debug(util.hexprint(resp))
879
                raise errors.RadioError("Checksum error on read")
880
            image += resp[2:]
881
            if self.status_fn:
882
                status = chirp_common.Status()
883
                status.cur = i
884
                status.max = end
885
                status.msg = "Cloning from radio"
886
                self.status_fn(status)
887
        self._finish()
888
        LOG.debug("Download Completed")
889
        return memmap.MemoryMapBytes(image)
890

    
891
    def _upload(self):
892
        """Talk to a wouxun KG-UV980P and do a upload"""
893
        try:
894
            self._identify()
895
            LOG.debug("Done with Upload Identify")
896
            self._do_upload()
897
            LOG.debug("Done with Mem and Settings Upload")
898
            self._finish()
899
        except errors.RadioError:
900
            raise
901
        except Exception:
902
            raise errors.RadioError("Failed to communicate with radio: %s")
903
        return
904

    
905
    def _do_upload(self):
906
        LOG.debug("Start of _do_upload")
907
        cfg_map = config_map
908
        endwrite = 0x73E7
909
        for start, blocksize, count in cfg_map:
910
            end = start + (blocksize * count)
911
            LOG.debug("start = " + str(start))
912
            LOG.debug("end = " + str(end))
913
            LOG.debug("blksize = " + str(blocksize))
914

    
915
            for addr in range(start, end, blocksize):
916
                ptr = addr
917
                req = struct.pack('>H', addr)
918
                chunk = self.get_mmap()[ptr:ptr + blocksize]
919
                self._write_record(CMD_WR, req + chunk)
920
                LOG.debug(util.hexprint(req + chunk))
921
                cserr, ack = self._read_record()
922
                LOG.debug(util.hexprint(ack))
923
                j = struct.unpack('>H', ack)[0]
924
                if cserr or j != ptr:
925
                    raise errors.RadioError("Radio did not ack block %i" % ptr)
926
                ptr += blocksize
927
                if self.status_fn:
928
                    status = chirp_common.Status()
929
                    status.cur = ptr
930
                    status.max = endwrite
931
                    status.msg = "Cloning to radio"
932
                    self.status_fn(status)
933

    
934
    def get_features(self):
935
        rf = chirp_common.RadioFeatures()
936
        rf.has_settings = True
937
        rf.has_ctone = True
938
        rf.has_rx_dtcs = True
939
        rf.has_cross = True
940
        rf.has_tuning_step = False
941
        rf.has_bank = False
942
        rf.can_odd_split = True
943
        rf.valid_skips = ["", "S"]
944
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
945
        rf.valid_cross_modes = [
946
            "Tone->Tone",
947
            "Tone->DTCS",
948
            "DTCS->Tone",
949
            "DTCS->",
950
            "->Tone",
951
            "->DTCS",
952
            "DTCS->DTCS",
953
        ]
954
        rf.valid_modes = ["FM", "NFM", "AM", "NAM"]
955
        rf.valid_power_levels = self.POWER_LEVELS
956
        rf.valid_name_length = 8
957
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
958
        rf.valid_bands = [(26000000, 299999990),  # supports VHF
959
                          (300000000, 999999990)]  # supports UHF
960

    
961
        rf.valid_characters = chirp_common.CHARSET_ASCII
962
        rf.memory_bounds = (1, 999)  # 999 memories
963
        rf.valid_tuning_steps = STEPS
964
        return rf
965

    
966
    @classmethod
967
    def get_prompts(cls):
968
        rp = chirp_common.RadioPrompts()
969
        rp.experimental = \
970
            ('This driver is experimental and may contain bugs. \n'
971
             'USE AT YOUR OWN RISK  - '
972
             'SAVE A COPY OF DOWNLOAD FROM YOUR RADIO BEFORE MAKING CHANGES\n'
973
             'Modification of Freq Limit Interfaces is done '
974
             'AT YOUR OWN RISK and may affect performance or certification'
975
             )
976
        return rp
977

    
978
    def get_raw_memory(self, number):
979
        return repr(self._memobj.memory[number])
980

    
981
    def _get_tone(self, _mem, mem):
982
        #  - corrected the Polarity decoding to match 980P implementation
983
        # use 0x4000 bit mask for R
984
        #  - 0x4000 appears to be the bit mask for Inverted DCS tones
985
        #  - n DCS Tone will be 0x8xxx values - i DCS Tones will
986
        # be 0xCxxx values.
987
        #  - Chirp Uses N for n DCS Tones and R for i DCS Tones
988
        #  - 980P encodes DCS tone # in decimal -  NOT OCTAL
989
        def _get_dcs(val):
990
            code = int("%03d" % (val & 0x07FF))
991
            pol = (val & 0x4000) and "R" or "N"
992
            return code, pol
993
        #  - Modified the function below to bitwise AND with 0x4000
994
        # to check for 980P DCS Tone decoding
995
        #  0x8000 appears to be the bit mask for DCS tones
996
        tpol = False
997
        #  Beta 1.1 - Fix the txtone compare to 0x8000 - was rxtone.
998
        if _mem.txtone != 0xFFFF and (_mem.txtone & 0x8000) == 0x8000:
999
            tcode, tpol = _get_dcs(_mem.txtone)
1000
            mem.dtcs = tcode
1001
            txmode = "DTCS"
1002
        elif _mem.txtone != 0xFFFF and _mem.txtone != 0x0:
1003
            mem.rtone = (_mem.txtone & 0x7fff) / 10.0
1004
            txmode = "Tone"
1005
        else:
1006
            txmode = ""
1007
        #  - Modified the function below to bitwise AND with 0x4000
1008
        # to check for 980P DCS Tone decoding
1009
        rpol = False
1010
        if _mem.rxtone != 0xFFFF and (_mem.rxtone & 0x8000) == 0x8000:
1011
            rcode, rpol = _get_dcs(_mem.rxtone)
1012
            mem.rx_dtcs = rcode
1013
            rxmode = "DTCS"
1014
        elif _mem.rxtone != 0xFFFF and _mem.rxtone != 0x0:
1015
            mem.ctone = (_mem.rxtone & 0x7fff) / 10.0
1016
            rxmode = "Tone"
1017
        else:
1018
            rxmode = ""
1019

    
1020
        if txmode == "Tone" and not rxmode:
1021
            mem.tmode = "Tone"
1022
        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
1023
            mem.tmode = "TSQL"
1024
        elif txmode == rxmode and txmode == "DTCS" and mem.dtcs == mem.rx_dtcs:
1025
            mem.tmode = "DTCS"
1026
        elif rxmode or txmode:
1027
            mem.tmode = "Cross"
1028
            mem.cross_mode = "%s->%s" % (txmode, rxmode)
1029

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

    
1033
    def get_memory(self, number):
1034
        _mem = self._memobj.memory[number]
1035

    
1036
        mem = chirp_common.Memory()
1037
        mem.number = number
1038
        _valid = self._memobj.valid[mem.number]
1039

    
1040
        if (_valid != MEM_VALID) & ((_mem.rxfreq == 0xFFFFFFFF) or
1041
                                    _mem.rxfreq == 0x00000000):
1042
            mem.empty = True
1043
            return mem
1044
        elif (_valid != MEM_VALID) & ((_mem.rxfreq != 0xFFFFFFFF) and
1045
                                      (_mem.rxfreq != 0x00000000)):
1046
            mem.empty = False
1047
        else:
1048
            mem.empty = False
1049
        mem.freq = int(_mem.rxfreq) * 10
1050
        _rxfreq = _freq_decode(_mem.rxfreq)
1051
        _txfreq = _freq_decode(_mem.txfreq)
1052
        mem.freq = _rxfreq
1053

    
1054
        if _mem.txfreq == 0xFFFFFFFF:
1055
            # TX freq not set
1056
            mem.duplex = "off"
1057
            mem.offset = 0
1058
        elif int(_rxfreq) == int(_txfreq):
1059
            mem.duplex = ""
1060
            mem.offset = 0
1061
        elif abs(_rxfreq - _txfreq) > 70000000:
1062
            mem.duplex = "split"
1063
            mem.offset = _txfreq
1064
        else:
1065
            mem.duplex = _rxfreq > _txfreq and "-" or "+"
1066
            mem.offset = abs(_rxfreq - _txfreq)
1067

    
1068
        if _mem.named:
1069
            if self.MODEL != "KG-UV950P":
1070
                mem.name = _str_decode(self._memobj.names[number].name)
1071
                # Remove trailing whitespaces from the name
1072
                mem.name = mem.name.rstrip()
1073
            else:
1074
                mem.name = _str_decode_950(self._memobj.names[number].name)
1075
                # Remove trailing whitespaces from the name
1076
                mem.name = mem.name.rstrip()               
1077
        else:
1078
            mem.name = ''
1079

    
1080
        self._get_tone(_mem, mem)
1081

    
1082
        mem.skip = "" if bool(_mem.scan_add) else "S"
1083

    
1084
        pwr_index = _mem.power
1085
        if _mem.power == 3:
1086
            pwr_index = 2
1087
        if _mem.power:
1088
            mem.power = self.POWER_LEVELS[pwr_index]
1089
        else:
1090
            mem.power = self.POWER_LEVELS[0]
1091

    
1092
        if _mem.am_mode:
1093
            if _mem.isnarrow:
1094
                mem.mode = "NAM"
1095
            else:
1096
                mem.mode = "AM"
1097
        else:
1098
            mem.mode = _mem.isnarrow and "NFM" or "FM"
1099

    
1100
        mem.extra = RadioSettingGroup("Extra", "extra")
1101

    
1102
        # Scrambler >8 is invalid default to off
1103
        if _mem.scrambler > 8:
1104
            _mem.scrambler = 0
1105

    
1106
        return mem
1107

    
1108
    def _set_tone(self, mem, _mem):
1109
        def _set_dcs(code, pol):
1110
            #  Change to 0x8000 to
1111
            # set the bit for DCS- code is a decimal version
1112
            # of the code # - NOT OCTAL
1113
            val = code | 0x8000
1114
            if pol == "R":
1115
                #  Change to 0x4000 to set the bit for
1116
                # i/R polarity
1117
                val += 0x4000
1118
            return val
1119

    
1120
        rx_mode = tx_mode = None
1121
        rxtone = txtone = 0x0000
1122

    
1123
        if mem.tmode == "Tone":
1124
            tx_mode = "Tone"
1125
            rx_mode = None
1126
            txtone = int(mem.rtone * 10)
1127
        elif mem.tmode == "TSQL":
1128
            rx_mode = tx_mode = "Tone"
1129
            rxtone = txtone = int(mem.ctone * 10)
1130
        elif mem.tmode == "DTCS":
1131
            tx_mode = rx_mode = "DTCS"
1132
            txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
1133
            rxtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[1])
1134
        elif mem.tmode == "Cross":
1135
            tx_mode, rx_mode = mem.cross_mode.split("->")
1136
            if tx_mode == "DTCS":
1137
                txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
1138
            elif tx_mode == "Tone":
1139
                txtone = int(mem.rtone * 10)
1140
            if rx_mode == "DTCS":
1141
                rxtone = _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[1])
1142
            elif rx_mode == "Tone":
1143
                rxtone = int(mem.ctone * 10)
1144

    
1145
        _mem.rxtone = rxtone
1146
        _mem.txtone = txtone
1147

    
1148
        LOG.debug("Set TX %s (%i) RX %s (%i)" %
1149
                  (tx_mode, _mem.txtone, rx_mode, _mem.rxtone))
1150

    
1151
    def set_memory(self, mem):
1152
        # _mem = Stored Memory value
1153
        # mem = New value from user entry
1154
        number = mem.number
1155
        _mem = self._memobj.memory[number]
1156
        _nam = self._memobj.names[number]
1157

    
1158
        if mem.empty:
1159
            self._memobj.valid[number] = 0xFF
1160
            _mem.rxfreq = 0xFFFFFFFF
1161
            _mem.txfreq = 0xFFFFFFFF
1162
            self._memobj.names[number].set_raw("\xFF" * (_nam.size() // 8))
1163
        else:
1164
            if len(mem.name) > 0:
1165
                _mem.named = True
1166
                if self.MODEL != "KG-UV950P":
1167
                    name_encoded = _str_encode(mem.name)
1168
                else:
1169
                    name_encoded = _str_encode_950(mem.name)
1170
                for i in range(0, 8):
1171
                    _nam.name[i] = ord(name_encoded[i])
1172
            else:
1173
                _mem.named = False
1174

    
1175
            _mem.rxfreq = _freq_encode(mem.freq)
1176
            if mem.duplex == "off":
1177
                _mem.txfreq = 0xFFFFFFFF
1178
            elif mem.duplex == "split":
1179
                _mem.txfreq = _freq_encode(mem.offset)
1180
            elif mem.duplex == "+":
1181
                _mem.txfreq = _freq_encode(mem.freq + mem.offset)
1182
            elif mem.duplex == "-":
1183
                _mem.txfreq = _freq_encode(mem.freq - mem.offset)
1184
            else:
1185
                _mem.txfreq = _freq_encode(mem.freq)
1186

    
1187
            _mem.scan_add = int(mem.skip != "S")
1188

    
1189
            if mem.mode == "AM":
1190
                _mem.am_mode = True
1191
                _mem.isnarrow = False
1192
            elif mem.mode == "NAM":
1193
                _mem.am_mode = True
1194
                _mem.isnarrow = True
1195
            else:
1196
                _mem.am_mode = False
1197
                if mem.mode == "NFM":
1198
                    _mem.isnarrow = True
1199
                else:
1200
                    _mem.isnarrow = False
1201

    
1202
            # set the tone
1203
            self._set_tone(mem, _mem)
1204
            #  set the scrambler and compander to off by default
1205
            #  This changes them in the channel memory
1206
            _mem.scrambler = 0
1207
            _mem.compander = 0
1208
            # set the power
1209
            if mem.power:
1210
                _mem.power = self.POWER_LEVELS.index(mem.power)
1211
            else:
1212
                _mem.power = True
1213
            # pwr_index= mem.power
1214
            if str(mem.power) == "None":
1215
                mem.power = self.POWER_LEVELS[1]
1216
            index = self.POWER_LEVELS.index(mem.power)
1217
            if index == 2:
1218
                _mem.power = 0b11
1219
            else:
1220
                _mem.power = self.POWER_LEVELS.index(mem.power)
1221
            # Not sure what this bit does yet but it causes
1222
            # the radio to display
1223
            # MED power when the CPS shows Low Power.
1224
            # Forcing it to 0 to keep them
1225
            # consistent
1226
            _mem.extra_power_bit = 0
1227
            # Set other unknowns to 0 to match default CPS values
1228
            _mem.unknown1 = 0
1229
            _mem.unknown2 = 0
1230
            _mem.unknownbit2 = 0
1231
            _mem.unknown3 = 0
1232
            _mem.Unknown4_shft_dir = 0
1233
            _mem.unknown5 = 0
1234

    
1235
            #  set to mute mode to QT (not QT+DTMF or QT*DTMF) by default
1236
            #  This changes them in the channel memory
1237
            _mem.mute_mode = 1
1238

    
1239
    def _get_settings(self):
1240
        _settings = self._memobj.settings
1241
        _oem = self._memobj.oem
1242

    
1243
        cfg_grp = RadioSettingGroup("cfg_grp", "Config Settings")
1244
        cfg1_grp = RadioSettingGroup("cfg1_grp", "Config Settings 1")
1245
        cfg2_grp = RadioSettingGroup("cfg2_grp", "Config Settings 2")
1246
        if self.MODEL == "KG-1000G Plus":
1247
            vfoaname = "Area A Settings"
1248
        else:
1249
            vfoaname = "VFO A Settings"
1250
        vfoa_grp = RadioSettingGroup("vfoa_grp", vfoaname)
1251
        vfo150_grp = RadioSettingGroup("vfo150_grp", "150M Settings")
1252
        vfo450_grp = RadioSettingGroup("vfo450_grp", "450M Settings")
1253
        vfo20_grp = RadioSettingGroup("vfo20_grp", "20M Settings")
1254
        vfo50_grp = RadioSettingGroup("vfo50_grp", "50M Settings")
1255
        vfo350_grp = RadioSettingGroup("vfo350_grp", "350M Settings")
1256
        vfo850_grp = RadioSettingGroup("vfo850_grp", "850M Settings")
1257
        if self.MODEL == "KG-1000G Plus":
1258
            vfobname = "Area B Settings"
1259
        else:
1260
            vfobname = "VFO B Settings"
1261
        vfob_grp = RadioSettingGroup("vfob_grp", vfobname)
1262

    
1263
        fmradio_grp = RadioSettingGroup("fmradio_grp", "FM Broadcast Memory")
1264
        lmt_grp = RadioSettingGroup("lmt_grp", "Frequency Limits")
1265
        lmwrn_grp = RadioSettingGroup("lmwrn_grp", "USE AT YOUR OWN RISK")
1266
        rxlim_grp = RadioSettingGroup("rxlim_grp", "Rx Limits")
1267
        txlim_grp = RadioSettingGroup("txlim_grp", "Tx Limits")
1268
        oem_grp = RadioSettingGroup("oem_grp", "OEM Info")
1269
        scan_grp = RadioSettingGroup("scan_grp", "Scan Group")
1270
        scanname_grp = RadioSettingGroup("scanname_grp", "Scan Names")
1271
        remote_grp = RadioSettingGroup("remote_grp", "Remote Settings")
1272
        extra_grp = RadioSettingGroup("extra_grp",
1273
                                      "Extra Settings")
1274
        if self.MODEL == "KG-1000G Plus":
1275
            vfoname = "Freq Mode Settings"
1276
        else:
1277
            vfoname = "VFO Settings"
1278
        vfo_grp = RadioSettingGroup("vfo_grp",
1279
                                    vfoname)
1280
        extra_grp.append(oem_grp)
1281
        cfg_grp.append(cfg1_grp)
1282
        cfg_grp.append(cfg2_grp)
1283
        lmt_grp.append(lmwrn_grp)
1284
        lmt_grp.append(rxlim_grp)
1285
        extra_grp.append(lmt_grp)
1286
        vfo_grp.append(vfoa_grp)
1287
        vfo_grp.append(vfob_grp)
1288
        vfoa_grp.append(vfo150_grp)
1289
        vfoa_grp.append(vfo450_grp)
1290
        if (self.MODEL == "KG-UV980P" or
1291
            self.MODEL == "KG-UV950P"):
1292
            vfoa_grp.append(vfo20_grp)
1293
            lmt_grp.append(txlim_grp)
1294
        vfoa_grp.append(vfo50_grp)
1295
        vfoa_grp.append(vfo350_grp)
1296
        vfoa_grp.append(vfo850_grp)
1297
        scan_grp.append(scanname_grp)
1298

    
1299
        group = RadioSettings(cfg_grp, vfo_grp, fmradio_grp,
1300
                              remote_grp, scan_grp, extra_grp)
1301

    
1302
        # Configuration Settings
1303
        if self.MODEL == "KG-1000G Plus":
1304
            backlight = BACKLIGHT_LIST
1305
            voicemap = VOICE_MAP_1000GPLUS
1306
            pttidmap = PTT_ID_MAP_1000GPLUS
1307
            pttidlabel = "DTMF ID"
1308
            dtmflist = DTMF_ST_LIST_1000GPLUS
1309
            dtmflabel = "Sidetone"
1310
            spkmap = SPEAKER_MAP_1000GPLUS
1311
            key_l = KEY_LIST_1000GPLUS
1312
            scqt = SC_QT_MAP_1000GPLUS
1313
            pf1set = PF1_SETTINGS_1000GPLUS
1314
            wmmap = WORKMODE_MAP_1000GPLUS
1315
            totmap = TOT_MAP_1000GPLUS
1316
            lowvlabel = "Voltage Alert"
1317
            fanlabel = "Fan Setting"
1318
            alerttonelabel = "Alert Tone(Hz)"
1319
            tonescanlabel = "Tone Save"
1320
            pttdelaylabel = "DTMF ID Delay (ms)"
1321
            scandetlabel = "Tone Scan"
1322
            txvoltlabel = "Tx Voltage Limit"
1323
            holdtimrptlabel = "Repeater Hold Time (ms)"
1324
            thrvollvllabel = "Tx Voltage Min"
1325
            modepswlabel = "Freq Mode Password"
1326
            narrow1label = "150M W/N"
1327
            mute1label = "150M SP Mute"
1328
            scram1label = "150M Descrambler"
1329
            narrow2label = "450M W/N"
1330
            mute2label = "450M SP Mute"
1331
            scram2label = "450M Descrambler"
1332
            narrow3label = "20M W/N"
1333
            mute3label = "20M SP Mute"
1334
            scram3label = "20M Descrambler"
1335
            narrow4label = "50M W/N"
1336
            mute4label = "50M SP Mute"
1337
            scram4label = "50M Descrambler"
1338
            narrow5label = "350M W/N"
1339
            mute5label = "350M SP Mute"
1340
            scram5label = "350M Descrambler"
1341
            narrow6label = "850M W/N"
1342
            mute6label = "850M SP Mute"
1343
            scram6label = "850M Descrambler"
1344
            narrow7label = "W/N"
1345
            mute7label = "SP Mute"
1346
            scram7label = "Descrambler"
1347
        elif self.MODEL == "KG-UV950P":
1348
            backlight = BACKLIGHT_LIST_950
1349
            voicemap = VOICE_MAP
1350
            pttidmap = PTT_ID_MAP
1351
            pttidlabel = "Caller ID Tx Mode (PTT_ID)"
1352
            dtmflist = DTMF_ST_LIST
1353
            dtmflabel = "DTMF Sidetone"
1354
            spkmap = SPEAKER_MAP
1355
            key_l = KEY_LIST
1356
            scqt = SC_QT_MAP
1357
            pf1set = PF1_SETTINGS
1358
            wmmap = WORKMODE_MAP
1359
            totmap = TOT_MAP_1000GPLUS
1360
            lowvlabel = "Low Voltage Shutoff"
1361
            fanlabel = "Fan Mode"
1362
            alerttonelabel = "Alert Pulse (Hz)"
1363
            tonescanlabel = "CTCSS/DCS Scan"
1364
            pttdelaylabel = "Caller ID Tx Delay PTT-ID-DLY (ms)"
1365
            scandetlabel = "Scan DET"
1366
            txvoltlabel = "Threshold Voltage Tx"
1367
            holdtimrptlabel = "Hold Time of Repeat (ms)"
1368
            thrvollvllabel = "Threshold Voltage Level"
1369
            modepswlabel = "MODE PSW"
1370
            narrow1label = "150M Bandwidth"
1371
            mute1label = "150M Mute Mode"
1372
            scram1label = "150M Scrambler"
1373
            narrow2label = "450M Bandwidth"
1374
            mute2label = "450M Mute Mode"
1375
            scram2label = "450M Scrambler"
1376
            narrow3label = "20M Bandwidth"
1377
            mute3label = "20M Mute Mode"
1378
            scram3label = "20M Scrambler"
1379
            narrow4label = "50M Bandwidth"
1380
            mute4label = "50M Mute Mode"
1381
            scram4label = "50M Scrambler"
1382
            narrow5label = "350M Bandwidth"
1383
            mute5label = "350M Mute Mode"
1384
            scram5label = "350M Scrambler"
1385
            narrow6label = "850M Bandwidth"
1386
            mute6label = "850M Mute Mode"
1387
            scram6label = "850M Scrambler"
1388
            narrow7label = "Bandwidth"
1389
            mute7label = "Mute Mode"
1390
            scram7label = "Scrambler"
1391
        else:   # 980P or 1000G radios
1392
            backlight = BACKLIGHT_LIST
1393
            voicemap = VOICE_MAP
1394
            pttidmap = PTT_ID_MAP
1395
            pttidlabel = "Caller ID Tx Mode (PTT_ID)"
1396
            dtmflist = DTMF_ST_LIST
1397
            dtmflabel = "DTMF Sidetone"
1398
            spkmap = SPEAKER_MAP
1399
            key_l = KEY_LIST
1400
            scqt = SC_QT_MAP
1401
            pf1set = PF1_SETTINGS
1402
            wmmap = WORKMODE_MAP
1403
            totmap = TOT_MAP
1404
            lowvlabel = "Low Voltage Shutoff"
1405
            fanlabel = "Fan Mode"
1406
            alerttonelabel = "Alert Pulse (Hz)"
1407
            tonescanlabel = "CTCSS/DCS Scan"
1408
            pttdelaylabel = "Caller ID Tx Delay PTT-ID-DLY (ms)"
1409
            scandetlabel = "Scan DET"
1410
            txvoltlabel = "Threshold Voltage Tx"
1411
            holdtimrptlabel = "Hold Time of Repeat (ms)"
1412
            thrvollvllabel = "Threshold Voltage Level"
1413
            modepswlabel = "MODE PSW"
1414
            narrow1label = "150M Bandwidth"
1415
            mute1label = "150M Mute Mode"
1416
            scram1label = "150M Scrambler"
1417
            narrow2label = "450M Bandwidth"
1418
            mute2label = "450M Mute Mode"
1419
            scram2label = "450M Scrambler"
1420
            narrow3label = "20M Bandwidth"
1421
            mute3label = "20M Mute Mode"
1422
            scram3label = "20M Scrambler"
1423
            narrow4label = "50M Bandwidth"
1424
            mute4label = "50M Mute Mode"
1425
            scram4label = "50M Scrambler"
1426
            narrow5label = "350M Bandwidth"
1427
            mute5label = "350M Mute Mode"
1428
            scram5label = "350M Scrambler"
1429
            narrow6label = "850M Bandwidth"
1430
            mute6label = "850M Mute Mode"
1431
            scram6label = "850M Scrambler"
1432
            narrow7label = "Bandwidth"
1433
            mute7label = "Mute Mode"
1434
            scram7label = "Scrambler"
1435

    
1436
        rs = RadioSetting("roger", "Roger Beep",
1437
                          RadioSettingValueList(ROGER_LIST,
1438
                                                ROGER_LIST[_settings.
1439
                                                           roger]))
1440
        cfg1_grp.append(rs)
1441

    
1442
        rs = RadioSetting("beep", "Keypad Beep",
1443
                          RadioSettingValueBoolean(_settings.beep))
1444
        cfg1_grp.append(rs)
1445

    
1446
        rs = RadioSetting("voice", "Voice Guide",
1447
                          RadioSettingValueMap(voicemap,
1448
                                               _settings.voice))
1449
        cfg1_grp.append(rs)
1450

    
1451
        rs = RadioSetting("bcl_a", "Busy Channel Lock-out A",
1452
                          RadioSettingValueBoolean(_settings.bcl_a))
1453
        cfg1_grp.append(rs)
1454

    
1455
        rs = RadioSetting("bcl_b", "Busy Channel Lock-out B",
1456
                          RadioSettingValueBoolean(_settings.bcl_b))
1457
        cfg1_grp.append(rs)
1458

    
1459
        rs = RadioSetting("sc_rev", "Scan Mode",
1460
                          RadioSettingValueMap(SC_REV_MAP, _settings.sc_rev))
1461
        cfg1_grp.append(rs)
1462
        rs = RadioSetting("tot", "Timeout Timer (TOT)",
1463
                          RadioSettingValueMap(
1464
                              totmap, _settings.tot))
1465
        cfg1_grp.append(rs)
1466

    
1467
        if self.MODEL != "KG-1000G Plus":
1468
            rs = RadioSetting("toa", "Timeout Alarm (TOA)",
1469
                              RadioSettingValueList(
1470
                                  TOA_LIST, TOA_LIST[_settings.toa]))
1471
        else:
1472
            rs = RadioSetting("toa", "Overtime Alarm (TOA)",
1473
                              RadioSettingValueMap(
1474
                                  TOA_MAP, _settings.toa))
1475
        cfg1_grp.append(rs)
1476

    
1477
        if self.MODEL != "KG-1000G Plus":
1478
            rs = RadioSetting("ani_sw", "Caller ID Tx - ANI-SW",
1479
                              RadioSettingValueBoolean(_settings.ani_sw))
1480
            cfg1_grp.append(rs)
1481

    
1482
        if self.MODEL != "KG-1000G Plus":
1483
            rs = RadioSetting("ring", "Ring Time (Sec)",
1484
                              RadioSettingValueList(
1485
                                  RING_LIST,
1486
                                  RING_LIST[_settings.ring]))
1487
        else:
1488
            rs = RadioSetting("ring", "Ring Time (Sec)",
1489
                              RadioSettingValueMap(
1490
                                  RING_MAP, _settings.ring))
1491
        cfg1_grp.append(rs)
1492

    
1493
        rs = RadioSetting("dtmfsf", dtmflabel,
1494
                          RadioSettingValueList(
1495
                              dtmflist,
1496
                              dtmflist[_settings.dtmfsf]))
1497
        cfg1_grp.append(rs)
1498

    
1499
        rs = RadioSetting("ptt_id", pttidlabel,
1500
                          RadioSettingValueMap(pttidmap, _settings.ptt_id))
1501
        cfg1_grp.append(rs)
1502

    
1503
        rs = RadioSetting("wt_led", "Standby / WT LED",
1504
                          RadioSettingValueList(
1505
                              backlight,
1506
                              backlight[_settings.wt_led]))
1507
        cfg1_grp.append(rs)
1508

    
1509
        rs = RadioSetting("tx_led", "TX LED",
1510
                          RadioSettingValueList(
1511
                              backlight,
1512
                              backlight[_settings.tx_led]))
1513
        cfg1_grp.append(rs)
1514

    
1515
        rs = RadioSetting("rx_led", "Rx LED",
1516
                          RadioSettingValueList(
1517
                              backlight,
1518
                              backlight[_settings.rx_led]))
1519
        cfg1_grp.append(rs)
1520

    
1521
        if self.MODEL == "KG-1000G Plus":
1522
            rs = RadioSetting("prich_sw", "Priority Scan",
1523
                              RadioSettingValueList(
1524
                                  PRI_CH_SCAN_LIST,
1525
                                  PRI_CH_SCAN_LIST[_settings.prich_sw]))
1526
            cfg1_grp.append(rs)
1527
        else:
1528
            rs = RadioSetting("prich_sw", "Priority Channel Scan",
1529
                              RadioSettingValueBoolean(_settings.prich_sw))
1530
            cfg1_grp.append(rs)
1531

    
1532
        rs = RadioSetting("spk_cont", "Speaker Control",
1533
                          RadioSettingValueMap(
1534
                              spkmap,
1535
                              _settings.spk_cont))
1536
        cfg1_grp.append(rs)
1537

    
1538
        rs = RadioSetting("autolock", "Autolock",
1539
                          RadioSettingValueBoolean(_settings.autolock))
1540
        cfg1_grp.append(rs)
1541

    
1542
        rs = RadioSetting("low_v", lowvlabel,
1543
                          RadioSettingValueBoolean(_settings.low_v))
1544
        cfg1_grp.append(rs)
1545

    
1546
        rs = RadioSetting("fan", fanlabel,
1547
                          RadioSettingValueList(
1548
                              FAN_MODE_LIST,
1549
                              FAN_MODE_LIST[_settings.fan]))
1550
        cfg1_grp.append(rs)
1551

    
1552
        rs = RadioSetting("apo_time", "Auto Power-Off (Min)",
1553
                          RadioSettingValueList(
1554
                              APO_TIME_LIST,
1555
                              APO_TIME_LIST[_settings.apo_time]))
1556
        cfg1_grp.append(rs)
1557

    
1558
        rs = RadioSetting("alert", alerttonelabel,
1559
                          RadioSettingValueMap(ALERT_MAP, _settings.alert))
1560
        cfg1_grp.append(rs)
1561
        rs = RadioSetting("m_pwr", "Medium Power Level (W)",
1562
                          RadioSettingValueMap(M_POWER_MAP,
1563
                                               _settings.m_pwr))
1564
        cfg1_grp.append(rs)
1565

    
1566
        if self.MODEL != "KG-1000G Plus":
1567
            rs = RadioSetting("rpt_set_model", "Model (RPT-SET)",
1568
                              RadioSettingValueList(
1569
                                  RPT_MODE_LIST,
1570
                                  RPT_MODE_LIST[_settings.rpt_set_model]))
1571
        else:
1572
            rs = RadioSetting("rpt_set_model", "Repeater Mode",
1573
                              RadioSettingValueMap(
1574
                                  RPT_MODE_MAP,
1575
                                  _settings.rpt_set_model))
1576
        cfg2_grp.append(rs)
1577

    
1578
        rs = RadioSetting("rpt_spk", "Repeater Speaker Switch (RPT-SPK)",
1579
                          RadioSettingValueBoolean(_settings.rpt_spk))
1580
        cfg2_grp.append(rs)
1581

    
1582
        rs = RadioSetting("rpt_ptt", "Repeater PTT (RPT-PTT)",
1583
                          RadioSettingValueBoolean(_settings.rpt_ptt))
1584
        cfg2_grp.append(rs)
1585

    
1586
        rs = RadioSetting("dtmf_time", "DTMF Tx Duration (ms)",
1587
                          RadioSettingValueList(
1588
                              DTMF_TIMES,
1589
                              DTMF_TIMES[_settings.dtmf_time]))
1590
        cfg2_grp.append(rs)
1591
        rs = RadioSetting("dtmf_int", "DTMF Interval (ms)",
1592
                          RadioSettingValueList(
1593
                              DTMF_INTERVALS,
1594
                              DTMF_INTERVALS[_settings.dtmf_int]))
1595
        cfg2_grp.append(rs)
1596

    
1597
        rs = RadioSetting("sc_qt", tonescanlabel,
1598
                          RadioSettingValueMap(
1599
                              scqt, _settings.sc_qt))
1600
        cfg2_grp.append(rs)
1601

    
1602
        rs = RadioSetting("pri_ch", "Priority Channel",
1603
                          RadioSettingValueInteger(
1604
                              1, 999, _chnum_decode(_settings.pri_ch)))
1605
        cfg2_grp.append(rs)
1606

    
1607
        rs = RadioSetting("ptt_id_dly", pttdelaylabel,
1608
                          RadioSettingValueMap(PTT_ID_DELAY_MAP,
1609
                                               _settings.ptt_id_dly))
1610
        cfg2_grp.append(rs)
1611

    
1612
        rs = RadioSetting("rc_sw", "Remote Control RC-SW",
1613
                          RadioSettingValueBoolean(_settings.rc_sw))
1614
        cfg2_grp.append(rs)
1615

    
1616
        rs = RadioSetting("scan_det", scandetlabel,
1617
                          RadioSettingValueBoolean(_settings.scan_det))
1618
        cfg2_grp.append(rs)
1619

    
1620
        rs = RadioSetting("menu", "Menu Available",
1621
                          RadioSettingValueBoolean(_settings.menu))
1622
        cfg2_grp.append(rs)
1623

    
1624
        rs = RadioSetting("thr_vol_tx", txvoltlabel,
1625
                          RadioSettingValueBoolean(_settings.thr_vol_tx))
1626
        cfg2_grp.append(rs)
1627

    
1628
        rs = RadioSetting("hold_time_rpt", holdtimrptlabel,
1629
                          RadioSettingValueList(
1630
                              HOLD_TIMES,
1631
                              HOLD_TIMES[_settings.hold_time_rpt]))
1632
        cfg2_grp.append(rs)
1633

    
1634
        rs = RadioSetting("auto_am", "Auto AM",
1635
                          RadioSettingValueBoolean(_settings.auto_am))
1636
        cfg2_grp.append(rs)
1637

    
1638
        rs = RadioSetting("rpt_tone", "Repeat Tone",
1639
                          RadioSettingValueBoolean(_settings.rpt_tone))
1640
        cfg2_grp.append(rs)
1641

    
1642
        rs = RadioSetting("pf1_set", "PF1 setting",
1643
                          RadioSettingValueList(
1644
                              pf1set,
1645
                              pf1set[_settings.pf1_set]))
1646
        cfg2_grp.append(rs)
1647

    
1648
        rs = RadioSetting("settings.thr_vol_lvl", thrvollvllabel,
1649
                          RadioSettingValueFloat(
1650
                           9.5, 10.5, _settings.thr_vol_lvl / 100.0, 0.1, 1))
1651
        cfg2_grp.append(rs)
1652

    
1653
        dtmfchars = "0123456789"
1654
        _code = ''
1655
        test = int(_oem.mode_psw)
1656
        _codeobj = '0x{0:0{1}X}'.format(test, 6)
1657
        LOG.debug("codeobj = %s" % _codeobj)
1658
        _psw = str(_codeobj)
1659
        for i in range(2, 8):
1660
            LOG.debug("psw[i] = %s" % _psw[i])
1661
            if _psw[i] in dtmfchars:
1662
                _code += _psw[i]
1663
        val_psw = RadioSettingValueString(6, 6, _code, False)
1664
        val_psw.set_charset(dtmfchars)
1665
        rs = RadioSetting("oem.mode_psw", modepswlabel, val_psw)
1666

    
1667
        def apply_psw_id(setting, obj):
1668
            val2 = hex(int(str(val_psw), 16))
1669
            if (int(val2, 16) != 0):
1670
                while len(val2) < 8:
1671
                    val2 += '0'
1672
            psw = int(str(val2), 16)
1673
            obj.mode_psw = psw
1674
        rs.set_apply_callback(apply_psw_id, _oem)
1675
        cfg2_grp.append(rs)
1676

    
1677
        if self.MODEL != "KG-UV950P":
1678
            rs = RadioSetting("ABR", "Backlight On Time (ABR)",
1679
                            RadioSettingValueList(
1680
                                ABR_LIST,
1681
                                ABR_LIST[_settings.ABR]))
1682
            cfg2_grp.append(rs)
1683

    
1684
            rs = RadioSetting("KeyA", "Key A",
1685
                            RadioSettingValueList(
1686
                                key_l,
1687
                                key_l[_settings.KeyA]))
1688
            cfg2_grp.append(rs)
1689
            rs = RadioSetting("KeyB", "Key B",
1690
                            RadioSettingValueList(
1691
                                key_l,
1692
                                key_l[_settings.KeyB]))
1693
            cfg2_grp.append(rs)
1694
            rs = RadioSetting("KeyC", "Key C",
1695
                            RadioSettingValueList(
1696
                                key_l,
1697
                                key_l[_settings.KeyC]))
1698
            cfg2_grp.append(rs)
1699

    
1700
        if self.MODEL == "KG-1000G Plus":
1701
            rs = RadioSetting("KeyD", "Key D",
1702
                              RadioSettingValueList(
1703
                                  KEY_LIST_1000GPLUS,
1704
                                  KEY_LIST_1000GPLUS[_settings.KeyD]))
1705
            cfg2_grp.append(rs)
1706

    
1707
        if self.MODEL != "KG-UV950P":
1708
            rs = RadioSetting("key_lock", "Key Lock Active",
1709
                            RadioSettingValueBoolean(_settings.key_lock))
1710
            cfg2_grp.append(rs)
1711

    
1712
            rs = RadioSetting("act_area", "Active Area (BAND)",
1713
                            RadioSettingValueList(
1714
                                ACTIVE_AREA_LIST,
1715
                                ACTIVE_AREA_LIST[_settings.act_area]))
1716
            cfg2_grp.append(rs)
1717
            rs = RadioSetting("tdr_off", "TDR",
1718
                            RadioSettingValueList(
1719
                                TDR_LIST,
1720
                                TDR_LIST[_settings.tdr_off]))
1721
            cfg2_grp.append(rs)
1722

    
1723
        # Freq Limits settings
1724

    
1725
        # Convert Integer back to correct limit HEX value:
1726
        s = self._memobj
1727

    
1728
        _temp = int(s.bandlimits.limit_144M_ChA_rx_start) // 10
1729
        val = RadioSettingValueInteger(0, 999, _temp)
1730
        rs = RadioSetting("bandlimits.limit_144M_ChA_rx_start",
1731
                          "144M Area A Rx Lower Limit (MHz)",
1732
                          RadioSettingValueInteger(0, 999,
1733
                                                   val))
1734
        rxlim_grp.append(rs)
1735

    
1736
        _temp = int(s.bandlimits.limit_144M_ChA_rx_stop) // 10
1737
        val = RadioSettingValueInteger(0, 999, _temp)
1738
        rs = RadioSetting("bandlimits.limit_144M_ChA_rx_stop",
1739
                          "144M Area A Rx Upper Limit (+ .9975 MHz)",
1740
                          RadioSettingValueInteger(0, 999,
1741
                                                   val))
1742
        rxlim_grp.append(rs)
1743

    
1744
        _temp = int(s.bandlimits.limit_144M_ChB_rx_start) // 10
1745
        val = RadioSettingValueInteger(0, 999, _temp)
1746
        rs = RadioSetting("bandlimits.limit_144M_ChB_rx_start",
1747
                          "144M Area B Rx Lower Limit (MHz)",
1748
                          RadioSettingValueInteger(0, 999,
1749
                                                   val))
1750
        rxlim_grp.append(rs)
1751

    
1752
        _temp = int(s.bandlimits.limit_144M_ChB_rx_stop) // 10
1753
        val = RadioSettingValueInteger(0, 999, _temp)
1754
        rs = RadioSetting("bandlimits.limit_144M_ChB_rx_stop",
1755
                          "144M Area B Rx Upper Limit (+ .9975 MHz)",
1756
                          RadioSettingValueInteger(0, 999,
1757
                                                   val))
1758
        rxlim_grp.append(rs)
1759

    
1760
        _temp = int(s.bandlimits.limit_70cm_rx_start) // 10
1761
        val = RadioSettingValueInteger(0, 999, _temp)
1762
        rs = RadioSetting("bandlimits.limit_70cm_rx_start",
1763
                          "450M Rx Lower Limit (MHz)",
1764
                          RadioSettingValueInteger(0, 999,
1765
                                                   val))
1766
        rxlim_grp.append(rs)
1767

    
1768
        _temp = int(s.bandlimits.limit_70cm_rx_stop) // 10
1769
        val = RadioSettingValueInteger(0, 999, _temp)
1770
        rs = RadioSetting("bandlimits.limit_70cm_rx_stop",
1771
                          "450M Rx Upper Limit (+ .9975 MHz)",
1772
                          RadioSettingValueInteger(0, 999,
1773
                                                   val))
1774
        rxlim_grp.append(rs)
1775

    
1776
        if (self.MODEL == "KG-UV980P" or
1777
            self.MODEL == "KG-UV950P"):
1778
            _temp = int(s.bandlimits.limit_10m_rx_start) // 10
1779
            val = RadioSettingValueInteger(0, 999, _temp)
1780
            rs = RadioSetting("bandlimits.limit_10m_rx_start",
1781
                              "20M Rx Lower Limit (MHz)",
1782
                              RadioSettingValueInteger(0, 999,
1783
                                                       val))
1784
            rxlim_grp.append(rs)
1785

    
1786
            _temp = int(s.bandlimits.limit_10m_rx_stop) // 10
1787
            val = RadioSettingValueInteger(0, 999, _temp)
1788
            rs = RadioSetting("bandlimits.limit_10m_rx_stop",
1789
                              "20M Rx Upper Limit (+ .9975 MHz)",
1790
                              RadioSettingValueInteger(0, 999,
1791
                                                       val))
1792
            rxlim_grp.append(rs)
1793

    
1794
        _temp = int(s.bandlimits.limit_6m_rx_start) // 10
1795
        val = RadioSettingValueInteger(0, 999, _temp)
1796
        rs = RadioSetting("bandlimits.limit_6m_rx_start",
1797
                          "50M Rx Lower Limit (MHz)",
1798
                          RadioSettingValueInteger(0, 999,
1799
                                                   val))
1800
        rxlim_grp.append(rs)
1801

    
1802
        _temp = int(s.bandlimits.limit_6m_rx_stop) // 10
1803
        val = RadioSettingValueInteger(0, 999, _temp)
1804
        rs = RadioSetting("bandlimits.limit_6m_rx_stop",
1805
                          "50M Rx Upper Limit (+ .9975 MHz)",
1806
                          RadioSettingValueInteger(0, 999,
1807
                                                   val))
1808
        rxlim_grp.append(rs)
1809

    
1810
        _temp = int(s.bandlimits.limit_350M_rx_start) // 10
1811
        val = RadioSettingValueInteger(0, 999, _temp)
1812
        rs = RadioSetting("bandlimits.limit_350M_rx_start",
1813
                          "350M Rx Lower Limit (MHz)",
1814
                          RadioSettingValueInteger(0, 999,
1815
                                                   val))
1816
        rxlim_grp.append(rs)
1817

    
1818
        _temp = int(s.bandlimits.limit_350M_rx_stop) // 10
1819
        val = RadioSettingValueInteger(0, 999, _temp)
1820
        rs = RadioSetting("bandlimits.limit_350M_rx_stop",
1821
                          "350M Rx Upper Limit (+ .9975 MHz)",
1822
                          RadioSettingValueInteger(0, 999,
1823
                                                   val))
1824
        rxlim_grp.append(rs)
1825

    
1826
        _temp = int(s.bandlimits.limit_850M_rx_start) // 10
1827
        val = RadioSettingValueInteger(0, 999, _temp)
1828
        rs = RadioSetting("bandlimits.limit_850M_rx_start",
1829
                          "850M Rx Lower Limit (MHz)",
1830
                          RadioSettingValueInteger(0, 999,
1831
                                                   val))
1832
        rxlim_grp.append(rs)
1833

    
1834
        _temp = int(s.bandlimits.limit_850M_rx_stop) // 10
1835
        val = RadioSettingValueInteger(0, 999, _temp)
1836
        rs = RadioSetting("bandlimits.limit_850M_rx_stop",
1837
                          "850M Rx Upper Limit (+ .9975 MHz)",
1838
                          RadioSettingValueInteger(0, 999,
1839
                                                   val))
1840
        rxlim_grp.append(rs)
1841

    
1842
        if (self.MODEL == "KG-UV980P" or
1843
            self.MODEL == "KG-UV950P"):
1844
            _temp = int(s.bandlimits.limit_144M_tx_start) // 10
1845
            val = RadioSettingValueInteger(0, 999, _temp)
1846
            rs = RadioSetting("bandlimits.limit_144M_tx_start",
1847
                              "144M Tx Lower Limit (MHz)",
1848
                              RadioSettingValueInteger(0, 999,
1849
                                                       val))
1850
            txlim_grp.append(rs)
1851

    
1852
            _temp = int(s.bandlimits.limit_144M_tx_stop) // 10
1853
            val = RadioSettingValueInteger(0, 999, _temp)
1854
            rs = RadioSetting("bandlimits.limit_144M_tx_stop",
1855
                              "144M Tx Upper Limit (+ .9975 MHz)",
1856
                              RadioSettingValueInteger(0, 999,
1857
                                                       val))
1858
            txlim_grp.append(rs)
1859

    
1860
            _temp = int(s.bandlimits.limit_70cm_tx_start) // 10
1861
            val = RadioSettingValueInteger(0, 999, _temp)
1862
            rs = RadioSetting("bandlimits.limit_70cm_tx_start",
1863
                              "450M Tx Lower Limit (MHz)",
1864
                              RadioSettingValueInteger(0, 999,
1865
                                                       val))
1866
            txlim_grp.append(rs)
1867

    
1868
            _temp = int(s.bandlimits.limit_70cm_tx_stop) // 10
1869
            val = RadioSettingValueInteger(0, 999, _temp)
1870
            rs = RadioSetting("bandlimits.limit_70cm_tx_stop",
1871
                              "450M tx Upper Limit (+ .9975 MHz)",
1872
                              RadioSettingValueInteger(0, 999,
1873
                                                       val))
1874
            txlim_grp.append(rs)
1875

    
1876
            _temp = int(s.bandlimits.limit_10m_tx_start) // 10
1877
            val = RadioSettingValueInteger(0, 999, _temp)
1878
            rs = RadioSetting("bandlimits.limit_10m_tx_start",
1879
                                "20M tx Lower Limit (MHz)",
1880
                                RadioSettingValueInteger(0, 999,
1881
                                                        val))
1882
            txlim_grp.append(rs)
1883

    
1884
            _temp = int(s.bandlimits.limit_10m_tx_stop) // 10
1885
            val = RadioSettingValueInteger(0, 999, _temp)
1886
            rs = RadioSetting("bandlimits.limit_10m_tx_stop",
1887
                                "20M tx Upper Limit (+ .9975 MHz)",
1888
                                RadioSettingValueInteger(0, 999,
1889
                                                        val))
1890
            txlim_grp.append(rs)
1891

    
1892
            _temp = int(s.bandlimits.limit_6m_tx_start) // 10
1893
            val = RadioSettingValueInteger(0, 999, _temp)
1894
            rs = RadioSetting("bandlimits.limit_6m_tx_start",
1895
                              "50M tx Lower Limit (MHz)",
1896
                              RadioSettingValueInteger(0, 999,
1897
                                                       val))
1898
            txlim_grp.append(rs)
1899

    
1900
            _temp = int(s.bandlimits.limit_6m_tx_stop) // 10
1901
            val = RadioSettingValueInteger(0, 999, _temp)
1902
            rs = RadioSetting("bandlimits.limit_6m_tx_stop",
1903
                              "50M tx Upper Limit (+ .9975 MHz)",
1904
                              RadioSettingValueInteger(0, 999,
1905
                                                       val))
1906
            txlim_grp.append(rs)
1907

    
1908
        # VFO Settings
1909
        rs = RadioSetting("vfomode_a", "Working Mode",
1910
                          RadioSettingValueMap(wmmap,
1911
                                               _settings.vfomode_a))
1912
        vfoa_grp.append(rs)
1913

    
1914
        rs = RadioSetting("vfoband_a", "Current Band",
1915
                          RadioSettingValueMap(VFOBAND_MAP,
1916
                                               _settings.vfoband_a))
1917
        vfoa_grp.append(rs)
1918

    
1919
        rs = RadioSetting("vfochan_a", "Active/Work Channel",
1920
                          RadioSettingValueInteger(1, 999,
1921
                                                   _chnum_decode(
1922
                                                    _settings.vfochan_a)))
1923
        vfoa_grp.append(rs)
1924

    
1925
        rs = RadioSetting("vfosquelch_a", "Squelch",
1926
                          RadioSettingValueInteger(0, 9,
1927
                                                   _settings.vfosquelch_a))
1928
        vfoa_grp.append(rs)
1929
        rs = RadioSetting("vfostep_a", "Step",
1930
                          RadioSettingValueList(
1931
                            STEP_LIST,
1932
                            STEP_LIST[_settings.vfostep_a]))
1933
        vfoa_grp.append(rs)
1934

    
1935
        # #####################
1936

    
1937
        rs = RadioSetting("vfofreq1", "150M Freq",
1938
                          RadioSettingValueFloat(
1939
                             0, 999.999999, (_freq_decode
1940
                                             (_settings.vfofreq1) /
1941
                                             1000000.0), 0.000001, 6))
1942
        vfo150_grp.append(rs)
1943

    
1944
        if self.MODEL != "KG-1000G Plus":
1945
            rs = RadioSetting("vfoofst1", "150M Offset",
1946
                              RadioSettingValueFloat(
1947
                                0, 999.999999, (_freq_decode
1948
                                                (_settings.vfoofst1) /
1949
                                                1000000.0), 0.000001, 6))
1950
            vfo150_grp.append(rs)
1951

    
1952
        rs = RadioSetting("rxtone1", "150M Rx tone",
1953
                          RadioSettingValueMap(
1954
                            TONE_MAP, _settings.rxtone1))
1955
        vfo150_grp.append(rs)
1956

    
1957
        rs = RadioSetting("txtone1", "150M Tx tone",
1958
                          RadioSettingValueMap(
1959
                            TONE_MAP, _settings.txtone1))
1960
        vfo150_grp.append(rs)
1961

    
1962
        rs = RadioSetting("power1", "150M Power",
1963
                          RadioSettingValueMap(
1964
                            POWER_MAP, _settings.power1))
1965
        vfo150_grp.append(rs)
1966

    
1967
        rs = RadioSetting("narrow1", narrow1label,
1968
                          RadioSettingValueMap(
1969
                            BANDWIDTH_MAP, _settings.narrow1))
1970
        vfo150_grp.append(rs)
1971

    
1972
        rs = RadioSetting("mute1", mute1label,
1973
                          RadioSettingValueMap(
1974
                            MUTE_MODE_MAP, _settings.mute1))
1975
        vfo150_grp.append(rs)
1976

    
1977
        if self.MODEL == "KG-1000G Plus":
1978
            rs = RadioSetting("shft_dir1", "150M Repeater",
1979
                              RadioSettingValueBoolean(
1980
                                _settings.shft_dir1))
1981
        else:
1982
            rs = RadioSetting("shft_dir1", "150M Shift Direction",
1983
                              RadioSettingValueList(
1984
                                DUPLEX_LIST,
1985
                                DUPLEX_LIST[_settings.shft_dir1]))
1986
        vfo150_grp.append(rs)
1987

    
1988
        rs = RadioSetting("compander1", "150M Compander",
1989
                          RadioSettingValueBoolean(
1990
                            _settings.compander1))
1991
        vfo150_grp.append(rs)
1992

    
1993
        rs = RadioSetting("scrambler1", scram1label,
1994
                          RadioSettingValueList(
1995
                            SCRAMBLER_LIST,
1996
                            SCRAMBLER_LIST[_settings.scrambler1]))
1997
        vfo150_grp.append(rs)
1998
        rs = RadioSetting("am_mode1", "150M AM Mode",
1999
                          RadioSettingValueBoolean(
2000
                            _settings.am_mode1))
2001
        vfo150_grp.append(rs)
2002

    
2003
        # ###########################
2004

    
2005
        rs = RadioSetting("vfofreq2", "450M Freq",
2006
                          RadioSettingValueFloat(
2007
                             0, 999.999999, (_freq_decode(
2008
                                             _settings.vfofreq2) /
2009
                                             1000000.0), 0.000001, 6))
2010
        vfo450_grp.append(rs)
2011

    
2012
        if self.MODEL != "KG-1000G Plus":
2013
            rs = RadioSetting("vfoofst2", "450M Offset",
2014
                              RadioSettingValueFloat(
2015
                                0, 999.999999, (_freq_decode(
2016
                                                _settings.vfoofst2) /
2017
                                                1000000.0), 0.000001, 6))
2018
            vfo450_grp.append(rs)
2019

    
2020
        rs = RadioSetting("rxtone2", "450M Rx tone",
2021
                          RadioSettingValueMap(
2022
                            TONE_MAP, _settings.rxtone2))
2023
        vfo450_grp.append(rs)
2024

    
2025
        rs = RadioSetting("txtone2", "450M Tx tone",
2026
                          RadioSettingValueMap(
2027
                            TONE_MAP, _settings.txtone2))
2028
        vfo450_grp.append(rs)
2029

    
2030
        rs = RadioSetting("power2", "450M Power",
2031
                          RadioSettingValueMap(
2032
                            POWER_MAP, _settings.power2))
2033
        vfo450_grp.append(rs)
2034

    
2035
        rs = RadioSetting("narrow2", narrow2label,
2036
                          RadioSettingValueMap(
2037
                            BANDWIDTH_MAP, _settings.narrow2))
2038
        vfo450_grp.append(rs)
2039

    
2040
        rs = RadioSetting("mute2", mute2label,
2041
                          RadioSettingValueMap(
2042
                            MUTE_MODE_MAP, _settings.mute2))
2043
        vfo450_grp.append(rs)
2044

    
2045
        if self.MODEL == "KG-1000G Plus":
2046
            rs = RadioSetting("shft_dir2", "450M Repeater",
2047
                              RadioSettingValueBoolean(
2048
                                _settings.shft_dir2))
2049
        else:
2050
            rs = RadioSetting("shft_dir2", "450M Shift Direction",
2051
                              RadioSettingValueList(
2052
                                DUPLEX_LIST,
2053
                                DUPLEX_LIST[_settings.shft_dir2]))
2054
        vfo450_grp.append(rs)
2055

    
2056
        rs = RadioSetting("compander2", "450M Compander",
2057
                          RadioSettingValueBoolean(
2058
                            _settings.compander2))
2059
        vfo450_grp.append(rs)
2060

    
2061
        rs = RadioSetting("scrambler2", scram2label,
2062
                          RadioSettingValueList(
2063
                            SCRAMBLER_LIST,
2064
                            SCRAMBLER_LIST[_settings.scrambler2]))
2065
        vfo450_grp.append(rs)
2066

    
2067
        rs = RadioSetting("am_mode2", "450M AM Mode",
2068
                          RadioSettingValueBoolean(
2069
                            _settings.am_mode2))
2070
        vfo450_grp.append(rs)
2071

    
2072
        # ###########################
2073
        if (self.MODEL == "KG-UV980P" or
2074
            self.MODEL == "KG-UV950P"):
2075
            rs = RadioSetting("vfofreq3", "20M Freq",
2076
                              RadioSettingValueFloat(
2077
                                0, 999.999999, (_freq_decode(
2078
                                                _settings.vfofreq3) /
2079
                                                1000000.0), 0.000001, 6))
2080
            vfo20_grp.append(rs)
2081

    
2082
            if (self.MODEL == "KG-UV980P"or
2083
               self.MODEL == "KG-UV950P"):
2084
                rs = RadioSetting("vfoofst3", "20M Offset",
2085
                                  RadioSettingValueFloat(
2086
                                    0, 999.999999, (_freq_decode(
2087
                                                    _settings.vfoofst3) /
2088
                                                    1000000.0), 0.000001, 6))
2089
                vfo20_grp.append(rs)
2090

    
2091
            rs = RadioSetting("rxtone3", "20M Rx tone",
2092
                              RadioSettingValueMap(
2093
                                TONE_MAP, _settings.rxtone3))
2094
            vfo20_grp.append(rs)
2095

    
2096
            rs = RadioSetting("txtone3", "20M Tx tone",
2097
                              RadioSettingValueMap(
2098
                                TONE_MAP, _settings.txtone3))
2099
            vfo20_grp.append(rs)
2100

    
2101
            rs = RadioSetting("power3", "20M Power",
2102
                              RadioSettingValueMap(
2103
                                POWER_MAP, _settings.power3))
2104
            vfo20_grp.append(rs)
2105

    
2106
            rs = RadioSetting("narrow3", narrow3label,
2107
                              RadioSettingValueMap(
2108
                                BANDWIDTH_MAP, _settings.narrow3))
2109
            vfo20_grp.append(rs)
2110

    
2111
            rs = RadioSetting("mute3", mute3label,
2112
                              RadioSettingValueMap(
2113
                                MUTE_MODE_MAP, _settings.mute3))
2114
            vfo20_grp.append(rs)
2115

    
2116
            if self.MODEL == "KG-1000G Plus":
2117
                rs = RadioSetting("shft_dir3", "20M Repeater",
2118
                                  RadioSettingValueBoolean(
2119
                                    _settings.shft_dir3))
2120
            else:
2121
                rs = RadioSetting("shft_dir3", "20M Shift Direction",
2122
                                  RadioSettingValueList(
2123
                                    DUPLEX_LIST,
2124
                                    DUPLEX_LIST[_settings.shft_dir3]))
2125
            vfo20_grp.append(rs)
2126

    
2127
            rs = RadioSetting("compander3", "20M Compander",
2128
                              RadioSettingValueBoolean(
2129
                                _settings.compander3))
2130
            vfo20_grp.append(rs)
2131

    
2132
            rs = RadioSetting("scrambler3", scram3label,
2133
                              RadioSettingValueList(
2134
                                SCRAMBLER_LIST,
2135
                                SCRAMBLER_LIST[_settings.scrambler3]))
2136
            vfo20_grp.append(rs)
2137

    
2138
            rs = RadioSetting("am_mode3", "20M AM Mode",
2139
                              RadioSettingValueBoolean(
2140
                                _settings.am_mode3))
2141
            vfo20_grp.append(rs)
2142

    
2143
        # ###########################
2144

    
2145
        rs = RadioSetting("vfofreq4", "50M Freq",
2146
                          RadioSettingValueFloat(
2147
                             0, 999.999999, (_freq_decode(
2148
                                             _settings.vfofreq4) /
2149
                                             1000000.0), 0.000001, 6))
2150
        vfo50_grp.append(rs)
2151

    
2152
        if self.MODEL != "KG-1000G Plus":
2153
            rs = RadioSetting("vfoofst4", "50M Offset",
2154
                              RadioSettingValueFloat(
2155
                                0, 999.999999, (_freq_decode(
2156
                                                _settings.vfoofst4) /
2157
                                                1000000.0), 0.000001, 6))
2158
            vfo50_grp.append(rs)
2159

    
2160
        rs = RadioSetting("rxtone4", "50M Rx tone",
2161
                          RadioSettingValueMap(
2162
                            TONE_MAP, _settings.rxtone4))
2163
        vfo50_grp.append(rs)
2164

    
2165
        rs = RadioSetting("txtone4", "50M Tx tone",
2166
                          RadioSettingValueMap(
2167
                            TONE_MAP, _settings.txtone4))
2168
        vfo50_grp.append(rs)
2169

    
2170
        rs = RadioSetting("power4", "50M Power",
2171
                          RadioSettingValueMap(
2172
                            POWER_MAP, _settings.power4))
2173
        vfo50_grp.append(rs)
2174

    
2175
        rs = RadioSetting("narrow4", narrow4label,
2176
                          RadioSettingValueMap(
2177
                            BANDWIDTH_MAP, _settings.narrow4))
2178
        vfo50_grp.append(rs)
2179

    
2180
        rs = RadioSetting("mute4", mute4label,
2181
                          RadioSettingValueMap(
2182
                            MUTE_MODE_MAP, _settings.mute4))
2183
        vfo50_grp.append(rs)
2184

    
2185
        if self.MODEL == "KG-1000G Plus":
2186
            rs = RadioSetting("shft_dir4", "50M Repeater",
2187
                              RadioSettingValueBoolean(
2188
                                _settings.shft_dir4))
2189
        else:
2190
            rs = RadioSetting("shft_dir4", "50M Shift Direction",
2191
                              RadioSettingValueList(
2192
                                DUPLEX_LIST,
2193
                                DUPLEX_LIST[_settings.shft_dir4]))
2194
        vfo50_grp.append(rs)
2195

    
2196
        rs = RadioSetting("compander4", "50M Compander",
2197
                          RadioSettingValueBoolean(
2198
                            _settings.compander4))
2199
        vfo50_grp.append(rs)
2200

    
2201
        rs = RadioSetting("scrambler4", scram4label,
2202
                          RadioSettingValueList(
2203
                            SCRAMBLER_LIST,
2204
                            SCRAMBLER_LIST[_settings.scrambler4]))
2205
        vfo50_grp.append(rs)
2206

    
2207
        rs = RadioSetting("am_mode4", "50M AM Mode",
2208
                          RadioSettingValueBoolean(
2209
                            _settings.am_mode4))
2210
        vfo50_grp.append(rs)
2211
        # ###########################
2212
        rs = RadioSetting("vfofreq5", "350M Freq",
2213
                          RadioSettingValueFloat(
2214
                             0, 999.999999, (_freq_decode(
2215
                                             _settings.vfofreq5) /
2216
                                             1000000.0), 0.000001, 6))
2217
        vfo350_grp.append(rs)
2218

    
2219
        if self.MODEL != "KG-1000G Plus":
2220
            rs = RadioSetting("vfoofst5", "350M Offset",
2221
                              RadioSettingValueFloat(
2222
                                0, 999.999999, (_freq_decode(
2223
                                                _settings.vfoofst5) /
2224
                                                1000000.0), 0.000001, 6))
2225
            vfo350_grp.append(rs)
2226

    
2227
        rs = RadioSetting("rxtone5", "350M Rx tone",
2228
                          RadioSettingValueMap(
2229
                            TONE_MAP, _settings.rxtone5))
2230
        vfo350_grp.append(rs)
2231

    
2232
        rs = RadioSetting("txtone5", "350M Tx tone",
2233
                          RadioSettingValueMap(
2234
                            TONE_MAP, _settings.txtone5))
2235
        vfo350_grp.append(rs)
2236

    
2237
        rs = RadioSetting("power5", "350M Power",
2238
                          RadioSettingValueMap(
2239
                            POWER_MAP, _settings.power5))
2240
        vfo350_grp.append(rs)
2241

    
2242
        rs = RadioSetting("narrow5", narrow5label,
2243
                          RadioSettingValueMap(
2244
                            BANDWIDTH_MAP, _settings.narrow5))
2245
        vfo350_grp.append(rs)
2246

    
2247
        rs = RadioSetting("mute5", mute5label,
2248
                          RadioSettingValueMap(
2249
                            MUTE_MODE_MAP, _settings.mute5))
2250
        vfo350_grp.append(rs)
2251

    
2252
        if self.MODEL == "KG-1000G Plus":
2253
            rs = RadioSetting("shft_dir5", "350M Repeater",
2254
                              RadioSettingValueBoolean(
2255
                                _settings.shft_dir5))
2256
        else:
2257
            rs = RadioSetting("shft_dir5", "350M Shift Direction",
2258
                              RadioSettingValueList(
2259
                                DUPLEX_LIST,
2260
                                DUPLEX_LIST[_settings.shft_dir5]))
2261
        vfo350_grp.append(rs)
2262

    
2263
        rs = RadioSetting("compander5", "350M Compander",
2264
                          RadioSettingValueBoolean(
2265
                            _settings.compander5))
2266
        vfo350_grp.append(rs)
2267

    
2268
        rs = RadioSetting("scrambler5", scram5label,
2269
                          RadioSettingValueList(
2270
                            SCRAMBLER_LIST,
2271
                            SCRAMBLER_LIST[_settings.scrambler5]))
2272
        vfo350_grp.append(rs)
2273

    
2274
        rs = RadioSetting("am_mode5", "350M AM Mode",
2275
                          RadioSettingValueBoolean(
2276
                            _settings.am_mode5))
2277
        vfo350_grp.append(rs)
2278

    
2279
        # ############################
2280
        rs = RadioSetting("vfofreq6", "850M Freq",
2281
                          RadioSettingValueFloat(
2282
                             0, 999.999999, (_freq_decode(
2283
                                             _settings.vfofreq6) /
2284
                                             1000000.0), 0.000001, 6))
2285
        vfo850_grp.append(rs)
2286
        if self.MODEL != "KG-1000G Plus":
2287
            rs = RadioSetting("vfoofst6", "850M Offset",
2288
                              RadioSettingValueFloat(
2289
                                0, 999.999999, (_freq_decode(
2290
                                                _settings.vfoofst6) /
2291
                                                1000000.0), 0.000001, 6))
2292
            vfo850_grp.append(rs)
2293

    
2294
        rs = RadioSetting("rxtone6", "850M Rx tone",
2295
                          RadioSettingValueMap(
2296
                            TONE_MAP, _settings.rxtone6))
2297
        vfo850_grp.append(rs)
2298

    
2299
        rs = RadioSetting("txtone6", "850M Tx tone",
2300
                          RadioSettingValueMap(
2301
                            TONE_MAP, _settings.txtone6))
2302
        vfo850_grp.append(rs)
2303

    
2304
        rs = RadioSetting("power6", "850M Power",
2305
                          RadioSettingValueMap(
2306
                            POWER_MAP, _settings.power6))
2307
        vfo850_grp.append(rs)
2308

    
2309
        rs = RadioSetting("narrow6", narrow6label,
2310
                          RadioSettingValueMap(
2311
                            BANDWIDTH_MAP, _settings.narrow6))
2312
        vfo850_grp.append(rs)
2313

    
2314
        rs = RadioSetting("mute6", mute6label,
2315
                          RadioSettingValueMap(
2316
                            MUTE_MODE_MAP, _settings.mute6))
2317
        vfo850_grp.append(rs)
2318

    
2319
        if self.MODEL == "KG-1000G Plus":
2320
            rs = RadioSetting("shft_dir6", "850M Repeater",
2321
                              RadioSettingValueBoolean(
2322
                                _settings.shft_dir6))
2323
        else:
2324
            rs = RadioSetting("shft_dir6", "850M Shift Direction",
2325
                              RadioSettingValueList(
2326
                                DUPLEX_LIST,
2327
                                DUPLEX_LIST[_settings.shft_dir6]))
2328
        vfo850_grp.append(rs)
2329

    
2330
        rs = RadioSetting("compander6", "850M Compander",
2331
                          RadioSettingValueBoolean(
2332
                            _settings.compander6))
2333
        vfo850_grp.append(rs)
2334

    
2335
        rs = RadioSetting("scrambler6", scram6label,
2336
                          RadioSettingValueList(
2337
                            SCRAMBLER_LIST,
2338
                            SCRAMBLER_LIST[_settings.scrambler6]))
2339
        vfo850_grp.append(rs)
2340

    
2341
        rs = RadioSetting("am_mode6", "850M AM Mode",
2342
                          RadioSettingValueBoolean(
2343
                            _settings.am_mode6))
2344
        vfo850_grp.append(rs)
2345

    
2346
        # ###########################
2347

    
2348
        rs = RadioSetting("vfomode_b", "Working Mode",
2349
                          RadioSettingValueMap(wmmap,
2350
                                               _settings.vfomode_b))
2351
        vfob_grp.append(rs)
2352

    
2353
        rs = RadioSetting("vfochan_b", "Active/Work Channel",
2354
                          RadioSettingValueInteger(1, 999,
2355
                                                   _chnum_decode(
2356
                                                    _settings.vfochan_b)))
2357
        vfob_grp.append(rs)
2358

    
2359
        rs = RadioSetting("vfofreq7", "Freq",
2360
                          RadioSettingValueFloat(
2361
                             0, 999.999999, (_freq_decode(
2362
                                             _settings.vfofreq7) /
2363
                                             1000000.0), 0.000001, 6))
2364
        vfob_grp.append(rs)
2365

    
2366
        if self.MODEL != "KG-1000G Plus":
2367
            rs = RadioSetting("vfoofst7", "Offset",
2368
                              RadioSettingValueFloat(
2369
                                0, 999.999999, (_freq_decode(
2370
                                                _settings.vfoofst7) /
2371
                                                1000000.0), 0.000001, 6))
2372
            vfob_grp.append(rs)
2373

    
2374
        rs = RadioSetting("rxtone7", "Rx tone",
2375
                          RadioSettingValueMap(
2376
                            TONE_MAP, _settings.rxtone7))
2377
        vfob_grp.append(rs)
2378

    
2379
        rs = RadioSetting("txtone7", "Tx tone",
2380
                          RadioSettingValueMap(
2381
                            TONE_MAP, _settings.txtone7))
2382
        vfob_grp.append(rs)
2383
        rs = RadioSetting("power7", "Power",
2384
                          RadioSettingValueMap(
2385
                            POWER_MAP, _settings.power7))
2386
        vfob_grp.append(rs)
2387
        rs = RadioSetting("narrow7", narrow7label,
2388
                          RadioSettingValueMap(
2389
                            BANDWIDTH_MAP, _settings.narrow7))
2390
        vfob_grp.append(rs)
2391
        rs = RadioSetting("mute7", mute7label,
2392
                          RadioSettingValueMap(
2393
                            MUTE_MODE_MAP, _settings.mute7))
2394
        vfob_grp.append(rs)
2395
        if self.MODEL == "KG-1000G Plus":
2396
            rs = RadioSetting("shft_dir7", "Repeater",
2397
                              RadioSettingValueBoolean(
2398
                                _settings.shft_dir7))
2399
        else:
2400
            rs = RadioSetting("shft_dir7", "Shift Direction",
2401
                              RadioSettingValueList(
2402
                                DUPLEX_LIST,
2403
                                DUPLEX_LIST[_settings.shft_dir7]))
2404
        vfob_grp.append(rs)
2405
        rs = RadioSetting("compander7", "Compander",
2406
                          RadioSettingValueBoolean(
2407
                            _settings.compander7))
2408
        vfob_grp.append(rs)
2409

    
2410
        rs = RadioSetting("scrambler7", scram7label,
2411
                          RadioSettingValueList(
2412
                            SCRAMBLER_LIST,
2413
                            SCRAMBLER_LIST[_settings.scrambler7]))
2414
        vfob_grp.append(rs)
2415

    
2416
        rs = RadioSetting("vfosquelch_b", "Squelch",
2417
                          RadioSettingValueInteger(0, 9,
2418
                                                   _settings.vfosquelch_b))
2419
        vfob_grp.append(rs)
2420
        rs = RadioSetting("vfostep_b", "Step",
2421
                          RadioSettingValueList(
2422
                            STEP_LIST,
2423
                            STEP_LIST[_settings.vfostep_b]))
2424
        vfob_grp.append(rs)
2425

    
2426
        # Scan Group Settings
2427
        def _decode(lst):
2428
            LOG.debug("lst %s", lst)
2429
            _str = ''.join([chr(c) for c in lst
2430
                            if chr(c) in chirp_common.CHARSET_ASCII])
2431
            return _str
2432

    
2433
        def _decode_950(lst):
2434
            _str =''
2435
            LOG.debug("lst %s", lst)
2436
            for c in lst:
2437
                if c >78:
2438
                    temp = c - 48
2439
                else:
2440
                    temp = c + 48
2441
                if chr(temp) in chirp_common.CHARSET_ASCII:
2442
                    _str += chr(temp)
2443
            return _str
2444

    
2445
        rs = RadioSetting("scan_a_act", "Scan A Active Group",
2446
                          RadioSettingValueList(
2447
                            SCAN_GROUP_LIST,
2448
                            SCAN_GROUP_LIST[_settings.scan_a_act]))
2449
        scan_grp.append(rs)
2450
        rs = RadioSetting("scan_b_act", "Scan B Active Group",
2451
                          RadioSettingValueList(
2452
                             SCAN_GROUP_LIST,
2453
                             SCAN_GROUP_LIST[_settings.scan_b_act]))
2454
        scan_grp.append(rs)
2455

    
2456
        for i in range(1, 11):
2457
            x = str(i)
2458
            if self.MODEL != "KG-UV950P":
2459
                _str = _decode(eval("_settings.scanname"+x))
2460
            else:
2461
                _str = _decode_950(eval("_settings.scanname"+x))
2462
            LOG.debug("ScanName %s", i)
2463
            LOG.debug("is %s", _str)
2464
            # CPS treats PPPPPP as a blank name as it is the factory reset
2465
            # value"
2466
            # The Radio treats PPPPPPP as a blank name and can display 8
2467
            # chars"
2468
            # Force Chirp to blank out the scan name if value is PPPPPPP
2469
            # to match the radio"
2470
            # Blank out the name if first 6 are spaces or null and the
2471
            # 7th is a P to handle
2472
            # firmware peculiarities in handling all 8 characters.
2473
            if _str[0:7] == "PPPPPPP":
2474
                _str = ""
2475
            if self.MODEL != "KG-1000G Plus":
2476
                if _str[0:7] == "\x00\x00\x00\x00\x00\x00P":
2477
                    _str = ""
2478
                elif _str[0:7] == "\x20\x20\x20\x20\x20\x20P":
2479
                    _str = ""
2480
            else:
2481
                if _str[0:6] == "\x00\x00\x00\x00\x00\x00":
2482
                    _str = ""
2483
                elif _str[0:6] == "\x20\x20\x20\x20\x20\x20":
2484
                    _str = ""
2485
                elif (len(_str) == 2) & (_str[0:2] == "PP"):
2486
                    _str = ""
2487
            rs = RadioSetting("scanname" + x, "Scan Name " + x,
2488
                              RadioSettingValueString(0, 8, _str))
2489
            scanname_grp.append(rs)
2490

    
2491
            scngrp = str(i)
2492
            rs = RadioSetting("scanlower"+scngrp, "Scan Lower "+scngrp,
2493
                              RadioSettingValueInteger(1, 999,
2494
                                                       eval(
2495
                                                        "_settings.\
2496
                                                        scanlower" +
2497
                                                        scngrp)))
2498
            scan_grp.append(rs)
2499
            rs = RadioSetting("scanupper"+scngrp, "Scan Upper "+scngrp,
2500
                              RadioSettingValueInteger(1, 999,
2501
                                                       eval(
2502
                                                        "_settings.scanupper" +
2503
                                                        scngrp)))
2504
            scan_grp.append(rs)
2505
# remote settings
2506
        rs = RadioSetting("rc_power", "RC Power",
2507
                          RadioSettingValueList(
2508
                           RC_POWER_LIST,
2509
                           RC_POWER_LIST[_settings.rc_power]))
2510
        remote_grp.append(rs)
2511

    
2512
        def decode_remote_vals(self, setting):
2513
            # parse the id value and replace all C with empty
2514
            # C indicates the end of the id value
2515
            code = ('%06X' % int(setting)).replace('C', '')
2516
            return int(code), code
2517

    
2518
        def apply_remote_id(setting, obj, val):
2519
            val = str(val)
2520
            value = int(val.ljust(3, '0').ljust(6, 'C'), 16)
2521
            setattr(obj, setting.get_name(), value)
2522

    
2523
        val_ani, code_val = decode_remote_vals(self, _settings.ani_edit)
2524
        LOG.debug("ani = %s" % val_ani)
2525
        val_ani = RadioSettingValueString(3, 6, code_val, False)
2526
        val_ani.set_charset(dtmfchars)
2527
        rs = RadioSetting("settings.ani_edit", "ANI Edit", val_ani)
2528

    
2529
        def apply_ani_id(setting, obj):
2530
            LOG.debug("val= %s" % val_ani)
2531
            if str(val_ani)[0] == "0":
2532
                raise errors.RadioError("ANI EDIT must start with \
2533
                                        Non-Zero Digit")
2534
            val = str(val_ani)
2535
            value = int(val.ljust(3, '0').ljust(6, 'C'), 16)
2536
            obj.ani_edit = value
2537
        rs.set_apply_callback(apply_ani_id, _settings)
2538
        remote_grp.append(rs)
2539

    
2540
        val_mcc, code_val = decode_remote_vals(self, _settings.mcc_edit)
2541
        LOG.debug("mcc = %s" % val_mcc)
2542
        val_mcc = RadioSettingValueString(3, 6, code_val, False)
2543
        val_mcc.set_charset(dtmfchars)
2544
        rs = RadioSetting("mcc_edit", "MCC Edit", val_mcc)
2545

    
2546
        rs.set_apply_callback(apply_remote_id, _settings, val_mcc)
2547
        remote_grp.append(rs)
2548

    
2549
        val_scc, code_val = decode_remote_vals(self, _settings.scc_edit)
2550
        LOG.debug("scc = %s" % val_scc)
2551
        val_scc = RadioSettingValueString(3, 6, code_val, False)
2552
        val_scc.set_charset(dtmfchars)
2553
        rs = RadioSetting("scc_edit", "SCC Edit", val_scc)
2554

    
2555
        rs.set_apply_callback(apply_remote_id, _settings, val_scc)
2556
        remote_grp.append(rs)
2557

    
2558
        val_ctrl, code_val = decode_remote_vals(self, _settings.ctrl_edit)
2559
        LOG.debug("ctrl = %s" % val_ctrl)
2560
        val_ctrl = RadioSettingValueString(3, 6, code_val, False)
2561
        val_ctrl.set_charset(dtmfchars)
2562
        rs = RadioSetting("ctrl_edit", "CTRL Edit", val_ctrl)
2563

    
2564
        rs.set_apply_callback(apply_remote_id, _settings, val_ctrl)
2565
        remote_grp.append(rs)
2566

    
2567
        # OEM Settings
2568
        if self.MODEL != "KG-1000G Plus":
2569
            _oem_name = _oem_str_decode(self._memobj.oem.display_name)
2570
        else:
2571
            displayname = self._memobj.oem.display_name
2572
            _oem_name = _oem_str_decode_1000GPLUS(displayname)
2573

    
2574
        rs = RadioSetting("oem.display_name", "Area Message",
2575
                          RadioSettingValueString(1, 8, _oem_name))
2576
        oem_grp.append(rs)
2577

    
2578
        # FM RADIO PRESETS
2579

    
2580
        # memory stores raw integer value like 7600
2581
        # radio will divide 7600 by 100 and interpret correctly at 76.0Mhz
2582

    
2583
        for i in range(1, 21):
2584
            # chan = str(i)
2585
            fmname = "FM_radio%i" % i
2586
            fmlabel = "FM Preset %i" % i
2587
            fmvalue = getattr(_settings, fmname)
2588
            # some CPS versions store values with .01 Mhz in error
2589
            # eg 99.5 Mhz is stored as 0x26df = 9951 dec = 99.51 Mhz
2590
            # even though the radio properly displays 99.5
2591
            # this will drop the 0.01 Mhz for Chirp Displayed values
2592
            fmvalue = fmvalue // 10 / 10
2593
            rs = RadioSetting(fmname, fmlabel,
2594
                              RadioSettingValueFloat(76.0, 108.0,
2595
                                                     fmvalue,
2596
                                                     0.1, 1))
2597
            fmradio_grp.append(rs)
2598

    
2599
        return group
2600

    
2601
    def get_settings(self):
2602
        try:
2603
            return self._get_settings()
2604
        except Exception:
2605
            LOG.exception("Failed to parse settings: %s")
2606
            return None
2607

    
2608
    def set_settings(self, settings):
2609
        for element in settings:
2610
            if not isinstance(element, RadioSetting):
2611
                self.set_settings(element)
2612
                continue
2613
            else:
2614
                try:
2615
                    if "." in element.get_name():
2616
                        bits = element.get_name().split(".")
2617
                        obj = self._memobj
2618
                        for bit in bits[:-1]:
2619
                            obj = getattr(obj, bit)
2620
                        setting = bits[-1]
2621
                    else:
2622
                        obj = self._memobj.settings
2623
                        setting = element.get_name()
2624

    
2625
                    if element.has_apply_callback():
2626
                        LOG.debug("Using apply callback")
2627
                        element.run_apply_callback()
2628
                    else:
2629
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2630
                        if self._is_freq(element):
2631
                            #  rescale freq values to match radio
2632
                            # expected values
2633
                            value = _freq_encode(
2634
                                element[0].get_value()*1000000.0)
2635
                            setattr(obj, setting, value)
2636

    
2637
                        elif self._is_fmradio_or_voltage(element):
2638
                            #  rescale FM Radio values to match radio
2639
                            # expected values
2640
                            setattr(obj, setting,
2641
                                    int(element.values()[0]._current * 100.0))
2642

    
2643
                        elif self._is_limit(element):
2644
                            setattr(obj, setting,
2645
                                    int(element[0].get_value()) * 10)
2646

    
2647
                        # Special VFO A Settings
2648
                        #
2649
                        elif self._is_chan(element):
2650
                            value = _chnum_encode(element[0].get_value())
2651
                            setattr(obj, setting, value)
2652
                            continue
2653
                        elif self._is_display_name(element):
2654
                            string = element[0].get_value()
2655
                            if self.MODEL != "KG-1000G Plus":
2656
                                nameenc = _oem_str_encode(string)
2657
                            else:
2658
                                nameenc = _oem_str_encode_1000GPLUS(string)
2659
                            for i in range(0, 8):
2660
                                LOG.debug("nameenc %s" % (nameenc[i]))
2661
                                self._memobj.oem.display_name[i] = \
2662
                                    ord(nameenc[i])
2663

    
2664
                        elif self._is_scan_name(element):
2665
                            string = element[0].get_value()
2666
                            LOG.debug("string %s" % (string))
2667
                            if self.MODEL != "KG-UV950P":
2668
                                value = _str_encode(string)
2669
                            else:
2670
                                value = _str_encode_950(string)
2671
                            LOG.debug("scaname %s" % (value))
2672
                            setattr(obj, setting, value.encode())
2673
                        else:
2674
                            setattr(obj, setting, element.value)
2675
                except Exception:
2676
                    LOG.debug(element.get_name())
2677
                    raise
2678

    
2679
    def _is_freq(self, element):
2680
        return ("rxfreq" in element.get_name() or
2681
                "txoffset" in element.get_name() or
2682
                "vfofreq" in element.get_name() or
2683
                "vfoofst" in element.get_name())
2684

    
2685
    def _is_limit(self, element):
2686
        return "limit" in element.get_name()
2687

    
2688
    def _is_fmradio_or_voltage(self, element):
2689
        return ("FM_radio" in element.get_name() or
2690
                "thr_vol_lvl" in element.get_name())
2691

    
2692
    def _is_chan(self, element):
2693
        return ("vfochan" in element.get_name() or
2694
                "pri_ch" in element.get_name())
2695

    
2696
    def _is_display_name(self, element):
2697
        return "display_name" in element.get_name()
2698

    
2699
    def _is_scan_name(self, element):
2700
        return "scanname" in element.get_name()
2701

    
2702

    
2703
@directory.register
2704
class KG1000GRadio(KG980PRadio):
2705

    
2706
    # """Wouxun KG-1000G"""
2707
    VENDOR = "Wouxun"
2708
    MODEL = "KG-1000G"
2709

    
2710

    
2711
@directory.register
2712
class KG1000GPlusRadio(KG980PRadio):
2713

    
2714
    # """Wouxun KG-1000G Plus"""
2715
    VENDOR = "Wouxun"
2716
    MODEL = "KG-1000G Plus"
2717

    
2718

    
2719
@directory.register
2720
class KG950PGRadio(KG980PRadio):
2721

    
2722
    # """Wouxun KG-950P"""
2723
    VENDOR = "Wouxun"
2724
    MODEL = "KG-UV950P"
(58-58/58)