Project

General

Profile

Bug #11159 ยป kguv980p.py

Mel Terechenok, 02/12/2024 12:35 PM

 
1
# Copyright 2022 Mel Terechenok <melvin.terechenok@gmail.com>
2
# KG-UV980P , KG-1000G, KG-1000G Plus 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,  16, 3),    # 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
MEM_INVALID = [0x80]
72
TX_BLANK = 0x40
73
RX_BLANK = 0x80
74

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

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

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

    
121
PTT_ID_MAP = [('BOT',  1),
122
              ('EOT',  2),
123
              ('Both', 3)]
124
PTT_ID_MAP_1000GPLUS = [('Off', 0),
125
                        ('BOT',  1),
126
                        ('EOT',  2),
127
                        ('Both', 3)]
128
BACKLIGHT_LIST = ["Off", "Red", "Orange", "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 Output 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 Output 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 Output String = %s", out_str)
647
    return out_str
648

    
649

    
650
def _str_decode(in_str):
651
    out_str = ''
652
    stopchar = False
653
    for c in in_str:
654
        if c != 0x00 and stopchar is False:
655
            if chr(c) in chirp_common.CHARSET_ASCII:
656
                out_str += chr(c)
657
        else:
658
            out_str += ''
659
            stopchar = True
660
    return out_str
661

    
662

    
663
def _str_encode(in_str):
664
    out_str = ''
665
    for c in in_str:
666
        try:
667
            out_str += chr(ord(c))
668
        except ValueError:
669
            pass
670
    while len(out_str) < 8:
671
        out_str += chr(0x00)
672
    if out_str == "        " or out_str == "":
673
        out_str = "\x00\x00\x00\x00\x00\x00\x00\x00"
674
    return out_str
675

    
676

    
677
def _chnum_decode(in_ch):
678
    return int(('%04x' % in_ch)[0:3])
679

    
680

    
681
def _chnum_encode(in_ch):
682
    return int('%03d0' % in_ch, 16)
683

    
684
# Support for the Wouxun KG-UV980P radio
685
# Serial coms are at 19200 baud
686
# The data is passed in variable length records
687
# Record structure:
688
#  Offset   Usage
689
#    0      start of record (\x7c)
690
#    1      Command (\x80 Identify \x81 End/Reboot \x82 Read \x83 Write)
691
#    2      direction (\xff PC-> Radio, \x00 Radio -> PC)
692
#    3      length of payload (excluding header/checksum) (n)
693
#    4      payload (n bytes)
694
#    4+n+1  checksum - only lower 4 bits of byte sum (% 256) of bytes 1 -> 4+n
695
#
696
# Memory Read Records:
697
# the payload is 3 bytes, first 2 are offset (big endian),
698
# 3rd is number of bytes to read
699
# Memory Write Records:
700
# the maximum payload size (from the Wouxun software) seems to be 66 bytes
701
#  (2 bytes location + 64 bytes data).
702

    
703

    
704
@directory.register
705
class KG980PRadio(chirp_common.CloneModeRadio,
706
                  chirp_common.ExperimentalRadio):
707

    
708
    """Wouxun KG-UV980P"""
709
    VENDOR = "Wouxun"
710
    MODEL = "KG-UV980P"
711
    _model = b"KG-UV950R2"
712
    _file_ident = b"980P"
713
    BAUD_RATE = 19200
714
    NEEDS_COMPAT_SERIAL = False
715
    # Start Byte for Communication messages
716
    _record_start = 0xDA
717
    # _cs_size = 0x0F for 4-bit checksum, 0xFF for 8-Bit checksum
718
    _cs_size = 0x0F
719
    # _valxor = value needed to encrypt/decrypt the bytes to/from the radio
720
    _valxor = 0x57
721
    POWER_LEVELS = [chirp_common.PowerLevel("L", watts=1.0),
722
                    chirp_common.PowerLevel("M", watts=20.0),
723
                    chirp_common.PowerLevel("H", watts=50.0)]
724

    
725
    def _checksum(self, data):
726
        cs = 0
727
        for byte in data:
728
            cs += byte
729
        return ((cs % 256) & self._cs_size)
730

    
731
    def _write_record(self, cmd, payload=b''):
732
        _packet = struct.pack('BBBB', self._record_start, cmd, 0xFF,
733
                              len(payload))
734
        checksum = bytes([self._checksum(_packet[1:] + payload)])
735
        _packet += self.encrypt(payload + checksum)
736
        LOG.debug("Sent:\n%s" % util.hexprint(_packet))
737
        self.pipe.write(_packet)
738

    
739
    def _read_record(self):
740
        # read 4 chars for the header
741
        _header = self.pipe.read(4)
742
        if len(_header) != 4:
743
            raise errors.RadioError('Radio did not respond')
744
        _length = struct.unpack('xxxB', _header)[0]
745
        _packet = self.pipe.read(_length)
746
        _rcs_xor = _packet[-1]
747
        _packet = self.decrypt(_packet)
748
        _cs = self._checksum(_header[1:])
749
        _cs += self._checksum(_packet)
750
        _cs %= 256
751
        _cs = _cs & self._cs_size
752
        _rcs = self.strxor(self.pipe.read(1)[0], _rcs_xor)[0]
753
        return (_rcs != _cs, _packet)
754

    
755
    def decrypt(self, data):
756
        result = b''
757
        for i in range(len(data)-1, 0, -1):
758
            result += self.strxor(data[i], data[i - 1])
759
        result += self.strxor(data[0], self._valxor)
760
        return result[::-1]
761

    
762
    def encrypt(self, data):
763
        result = self.strxor(self._valxor, data[0])
764
        for i in range(1, len(data), 1):
765
            result += self.strxor(result[i - 1], data[i])
766
        return result
767

    
768
    def strxor(self, xora, xorb):
769
        return bytes([xora ^ xorb])
770

    
771
    # Identify the radio
772
    #
773
    # A Gotcha: the first identify packet returns a bad checksum, subsequent
774
    # attempts return the correct checksum... (well it does on my radio!)
775
    #
776
    # The ID record returned by the radio also includes the
777
    # current frequency range
778
    # as 4 bytes big-endian in 10 Hz increments
779
    #
780
    # Offset
781
    #  0:10     Model, zero padded
782

    
783
    def _identify(self):
784
        """Do the identification dance"""
785
        for _i in range(0, 3):
786
            LOG.debug("ID try #"+str(_i))
787
            self._write_record(CMD_ID)
788
            _chksum_err, _resp = self._read_record()
789
            if len(_resp) == 0:
790
                raise errors.RadioError("Radio not responding")
791
            else:
792
                LOG.debug("Got:\n%s" % util.hexprint(_resp))
793
                LOG.debug("Model received is %s" % _resp[0:10])
794
                LOG.debug("Model expected is %s" % self._model)
795
                if _chksum_err:
796
                    LOG.error("Checksum error: retrying ident...")
797
                    time.sleep(0.100)
798
                    continue
799
                else:
800
                    LOG.debug("checksum passed")
801
                    if _resp[0:8] == self._model[0:8]:
802
                        LOG.debug("Passed identify")
803
                        break
804
                    else:
805
                        LOG.debug("FAILED to identify")
806
                        raise errors.RadioError("Failed Identification")
807

    
808
    def _finish(self):
809
        self._write_record(CMD_END)
810

    
811
    def process_mmap(self):
812
        self._memobj = bitwise.parse(_MEM_FORMAT, self._mmap)
813

    
814
    def sync_in(self):
815
        try:
816
            self._mmap = self._download()
817
        except errors.RadioError:
818
            raise
819
        except Exception:
820
            raise errors.RadioError("Failed to communicate with radio: %s")
821
        self.process_mmap()
822

    
823
    def sync_out(self):
824
        self._upload()
825

    
826
    # TODO: Load all memory.
827
    # It would be smarter to only load the active areas and none of
828
    # the padding/unused areas. Padding still need to be investigated.
829
    def _download(self):
830
        """Talk to a Wouxun KG-UV980P and do a download"""
831
        try:
832
            self._identify()
833
            return self._do_download(0, 32768, 64)
834
        except errors.RadioError:
835
            raise
836
        except Exception:
837
            LOG.exception('Unknown error during download process')
838
            raise errors.RadioError("Failed to communicate with radio: %s")
839

    
840
    def _do_download(self, start, end, size):
841
        # allocate & fill memory
842
        LOG.debug("Start Download")
843
        image = b""
844
        for i in range(start, end, size):
845
            req = struct.pack("BBB", int(i / 256), int(i % 256), int(size))
846
            self._write_record(CMD_RD, req)
847
            cs_error, resp = self._read_record()
848
            if cs_error:
849
                LOG.debug(util.hexprint(resp))
850
                raise errors.RadioError("Checksum error on read")
851
            image += resp[2:]
852
            if self.status_fn:
853
                status = chirp_common.Status()
854
                status.cur = i
855
                status.max = end
856
                status.msg = "Cloning from radio"
857
                self.status_fn(status)
858
        self._finish()
859
        LOG.debug("Download Completed")
860
        return memmap.MemoryMapBytes(image)
861

    
862
    def _upload(self):
863
        """Talk to a Wouxun KG-UV980P and do a upload"""
864
        try:
865
            self._identify()
866
            LOG.debug("Done with Upload Identify")
867
            self._do_upload()
868
            LOG.debug("Done with Mem and Settings Upload")
869
            self._finish()
870
        except errors.RadioError:
871
            raise
872
        except Exception:
873
            raise errors.RadioError("Failed to communicate with radio: %s")
874
        return
875

    
876
    def _do_upload(self):
877
        LOG.debug("Start of _do_upload")
878
        cfg_map = config_map
879
        endwrite = 0x73E7
880
        for start, blocksize, count in cfg_map:
881
            end = start + (blocksize * count)
882
            LOG.debug("start = " + str(start))
883
            LOG.debug("end = " + str(end))
884
            LOG.debug("blksize = " + str(blocksize))
885

    
886
            for addr in range(start, end, blocksize):
887
                ptr = addr
888
                req = struct.pack('>H', addr)
889
                chunk = self.get_mmap()[ptr:ptr + blocksize]
890
                self._write_record(CMD_WR, req + chunk)
891
                LOG.debug(util.hexprint(req + chunk))
892
                cserr, ack = self._read_record()
893
                LOG.debug(util.hexprint(ack))
894
                j = struct.unpack('>H', ack)[0]
895
                if cserr or j != ptr:
896
                    raise errors.RadioError("Radio did not ack block %i" % ptr)
897
                ptr += blocksize
898
                if self.status_fn:
899
                    status = chirp_common.Status()
900
                    status.cur = ptr
901
                    status.max = endwrite
902
                    status.msg = "Cloning to radio"
903
                    self.status_fn(status)
904

    
905
    def get_features(self):
906
        rf = chirp_common.RadioFeatures()
907
        rf.has_settings = True
908
        rf.has_ctone = True
909
        rf.has_rx_dtcs = True
910
        rf.has_cross = True
911
        rf.has_tuning_step = False
912
        rf.has_bank = False
913
        rf.can_odd_split = True
914
        rf.valid_skips = ["", "S"]
915
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
916
        rf.valid_cross_modes = [
917
            "Tone->Tone",
918
            "Tone->DTCS",
919
            "DTCS->Tone",
920
            "DTCS->",
921
            "->Tone",
922
            "->DTCS",
923
            "DTCS->DTCS",
924
        ]
925
        rf.valid_modes = ["FM", "NFM", "AM", "NAM"]
926
        rf.valid_power_levels = self.POWER_LEVELS
927
        rf.valid_name_length = 8
928
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
929
        rf.valid_bands = [(26000000, 299999990),  # supports VHF
930
                          (300000000, 999999990)]  # supports UHF
931

    
932
        rf.valid_characters = chirp_common.CHARSET_ASCII
933
        rf.memory_bounds = (1, 999)  # 999 memories
934
        rf.valid_tuning_steps = STEPS
935
        return rf
936

    
937
    @classmethod
938
    def get_prompts(cls):
939
        rp = chirp_common.RadioPrompts()
940
        rp.experimental = \
941
            ('This driver is experimental and may contain bugs. \n'
942
             'USE AT YOUR OWN RISK  - '
943
             'SAVE A COPY OF DOWNLOAD FROM YOUR RADIO BEFORE MAKING CHANGES\n'
944
             'Modification of Freq Limit Interfaces is done '
945
             'AT YOUR OWN RISK and may affect performance or certification'
946
             )
947
        return rp
948

    
949
    def get_raw_memory(self, number):
950
        return repr(self._memobj.memory[number])
951

    
952
    def _get_tone(self, _mem, mem):
953
        #  - corrected the Polarity decoding to match 980P implementation
954
        # use 0x4000 bit mask for R
955
        #  - 0x4000 appears to be the bit mask for Inverted DCS tones
956
        #  - n DCS Tone will be 0x8xxx values - i DCS Tones will
957
        # be 0xCxxx values.
958
        #  - Chirp Uses N for n DCS Tones and R for i DCS Tones
959
        #  - 980P encodes DCS tone # in decimal -  NOT OCTAL
960
        def _get_dcs(val):
961
            code = int("%03d" % (val & 0x07FF))
962
            pol = (val & 0x4000) and "R" or "N"
963
            return code, pol
964
        #  - Modified the function below to bitwise AND with 0x4000
965
        # to check for 980P DCS Tone decoding
966
        #  0x8000 appears to be the bit mask for DCS tones
967
        tpol = False
968
        #  Beta 1.1 - Fix the txtone compare to 0x8000 - was rxtone.
969
        if _mem.txtone != 0xFFFF and (_mem.txtone & 0x8000) == 0x8000:
970
            tcode, tpol = _get_dcs(_mem.txtone)
971
            mem.dtcs = tcode
972
            txmode = "DTCS"
973
        elif _mem.txtone != 0xFFFF and _mem.txtone != 0x0:
974
            mem.rtone = (_mem.txtone & 0x7fff) / 10.0
975
            txmode = "Tone"
976
        else:
977
            txmode = ""
978
        #  - Modified the function below to bitwise AND with 0x4000
979
        # to check for 980P DCS Tone decoding
980
        rpol = False
981
        if _mem.rxtone != 0xFFFF and (_mem.rxtone & 0x8000) == 0x8000:
982
            rcode, rpol = _get_dcs(_mem.rxtone)
983
            mem.rx_dtcs = rcode
984
            rxmode = "DTCS"
985
        elif _mem.rxtone != 0xFFFF and _mem.rxtone != 0x0:
986
            mem.ctone = (_mem.rxtone & 0x7fff) / 10.0
987
            rxmode = "Tone"
988
        else:
989
            rxmode = ""
990

    
991
        if txmode == "Tone" and not rxmode:
992
            mem.tmode = "Tone"
993
        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
994
            mem.tmode = "TSQL"
995
        elif txmode == rxmode and txmode == "DTCS" and mem.dtcs == mem.rx_dtcs:
996
            mem.tmode = "DTCS"
997
        elif rxmode or txmode:
998
            mem.tmode = "Cross"
999
            mem.cross_mode = "%s->%s" % (txmode, rxmode)
1000

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

    
1004
    def get_memory(self, number):
1005
        _mem = self._memobj.memory[number]
1006

    
1007
        mem = chirp_common.Memory()
1008
        mem.number = number
1009
        _valid = self._memobj.valid[mem.number]
1010
        _nam = self._memobj.names[number]
1011

    
1012
        # Handle deleted channel quirks due to radio firmware
1013
        # not always clearing the valid channel indicators
1014
        # _valid == 0x80 indicates channel was deleted thru radio menu and
1015
        # only part of the channel structure data is cleared by firmware
1016
        # _valid == 0x00 indicates the channel is Valid
1017
        # all other values the firmware uses for _valid: look for valid
1018
        # Rx and Tx freq values to determine validity
1019
        if (_valid in MEM_INVALID or
1020
           ((_valid != MEM_VALID) & ((_mem.rxfreq == 0xFFFFFFFF) or
1021
                                     _mem.rxfreq == 0x00000000))):
1022
            mem.empty = True
1023
            if _valid == 0x80:
1024
                LOG.debug("Ch %s was deleted by using radio menu" % number)
1025
                LOG.debug("Current Memory: \n%s" % _mem)
1026
                LOG.debug("Clearing Ch %s memory" % number)
1027
                _mem.set_raw(b"\xFF" * (_mem.size() // 8))
1028
                _nam.set_raw(b"\xFF" * (_nam.size() // 8))
1029
            self._memobj.valid[mem.number] = 0xFF
1030
            return mem
1031
        elif (_valid != MEM_VALID) & ((_mem.rxfreq != 0xFFFFFFFF) and
1032
                                      (_mem.rxfreq != 0x00000000)):
1033
            mem.empty = False
1034
            self._memobj.valid[mem.number] = MEM_VALID
1035
        else:
1036
            mem.empty = False
1037
        mem.freq = int(_mem.rxfreq) * 10
1038
        _rxfreq = _freq_decode(_mem.rxfreq)
1039
        _txfreq = _freq_decode(_mem.txfreq)
1040
        mem.freq = _rxfreq
1041

    
1042
        if _mem.txfreq == 0xFFFFFFFF:
1043
            # TX freq not set
1044
            mem.duplex = "off"
1045
            mem.offset = 0
1046
        elif int(_rxfreq) == int(_txfreq):
1047
            mem.duplex = ""
1048
            mem.offset = 0
1049
        elif abs(_rxfreq - _txfreq) > 70000000:
1050
            mem.duplex = "split"
1051
            mem.offset = _txfreq
1052
        else:
1053
            mem.duplex = _rxfreq > _txfreq and "-" or "+"
1054
            mem.offset = abs(_rxfreq - _txfreq)
1055

    
1056
        if _mem.named:
1057
            mem.name = _str_decode(self._memobj.names[number].name)
1058
            # Remove trailing whitespaces from the name
1059
            mem.name = mem.name.rstrip()
1060
        else:
1061
            mem.name = ''
1062

    
1063
        self._get_tone(_mem, mem)
1064

    
1065
        mem.skip = "" if bool(_mem.scan_add) else "S"
1066

    
1067
        pwr_index = _mem.power
1068
        if _mem.power == 3:
1069
            pwr_index = 2
1070
        if _mem.power:
1071
            mem.power = self.POWER_LEVELS[pwr_index]
1072
        else:
1073
            mem.power = self.POWER_LEVELS[0]
1074

    
1075
        if _mem.am_mode:
1076
            if _mem.isnarrow:
1077
                mem.mode = "NAM"
1078
            else:
1079
                mem.mode = "AM"
1080
        else:
1081
            mem.mode = _mem.isnarrow and "NFM" or "FM"
1082

    
1083
        mem.extra = RadioSettingGroup("Extra", "extra")
1084

    
1085
        # Scrambler >8 is invalid default to off
1086
        if _mem.scrambler > 8:
1087
            _mem.scrambler = 0
1088

    
1089
        return mem
1090

    
1091
    def _set_tone(self, mem, _mem):
1092
        def _set_dcs(code, pol):
1093
            #  Change to 0x8000 to
1094
            # set the bit for DCS- code is a decimal version
1095
            # of the code # - NOT OCTAL
1096
            val = code | 0x8000
1097
            if pol == "R":
1098
                #  Change to 0x4000 to set the bit for
1099
                # i/R polarity
1100
                val += 0x4000
1101
            return val
1102

    
1103
        rx_mode = tx_mode = None
1104
        rxtone = txtone = 0x0000
1105

    
1106
        if mem.tmode == "Tone":
1107
            tx_mode = "Tone"
1108
            rx_mode = None
1109
            txtone = int(mem.rtone * 10)
1110
        elif mem.tmode == "TSQL":
1111
            rx_mode = tx_mode = "Tone"
1112
            rxtone = txtone = int(mem.ctone * 10)
1113
        elif mem.tmode == "DTCS":
1114
            tx_mode = rx_mode = "DTCS"
1115
            txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
1116
            rxtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[1])
1117
        elif mem.tmode == "Cross":
1118
            tx_mode, rx_mode = mem.cross_mode.split("->")
1119
            if tx_mode == "DTCS":
1120
                txtone = _set_dcs(mem.dtcs, mem.dtcs_polarity[0])
1121
            elif tx_mode == "Tone":
1122
                txtone = int(mem.rtone * 10)
1123
            if rx_mode == "DTCS":
1124
                rxtone = _set_dcs(mem.rx_dtcs, mem.dtcs_polarity[1])
1125
            elif rx_mode == "Tone":
1126
                rxtone = int(mem.ctone * 10)
1127

    
1128
        _mem.rxtone = rxtone
1129
        _mem.txtone = txtone
1130

    
1131
        LOG.debug("Set TX %s (%i) RX %s (%i)" %
1132
                  (tx_mode, _mem.txtone, rx_mode, _mem.rxtone))
1133

    
1134
    def set_memory(self, mem):
1135
        # _mem = Stored Memory value
1136
        # mem = New value from user entry
1137
        number = mem.number
1138
        _mem = self._memobj.memory[number]
1139
        _nam = self._memobj.names[number]
1140

    
1141
        if mem.empty:
1142
            self._memobj.valid[number] = 0xFF
1143
            _mem.set_raw(b"\xFF" * (_mem.size() // 8))
1144
            self._memobj.names[number].set_raw(b"\xFF" * (_nam.size() // 8))
1145
        else:
1146
            if len(mem.name) > 0:
1147
                _mem.named = True
1148
                name_encoded = _str_encode(mem.name)
1149
                for i in range(0, 8):
1150
                    _nam.name[i] = ord(name_encoded[i])
1151
            else:
1152
                _mem.named = False
1153

    
1154
            _mem.rxfreq = _freq_encode(mem.freq)
1155
            if mem.duplex == "off":
1156
                _mem.txfreq = 0xFFFFFFFF
1157
            elif mem.duplex == "split":
1158
                _mem.txfreq = _freq_encode(mem.offset)
1159
            elif mem.duplex == "+":
1160
                _mem.txfreq = _freq_encode(mem.freq + mem.offset)
1161
            elif mem.duplex == "-":
1162
                _mem.txfreq = _freq_encode(mem.freq - mem.offset)
1163
            else:
1164
                _mem.txfreq = _freq_encode(mem.freq)
1165

    
1166
            _mem.scan_add = int(mem.skip != "S")
1167

    
1168
            if mem.mode == "AM":
1169
                _mem.am_mode = True
1170
                _mem.isnarrow = False
1171
            elif mem.mode == "NAM":
1172
                _mem.am_mode = True
1173
                _mem.isnarrow = True
1174
            else:
1175
                _mem.am_mode = False
1176
                if mem.mode == "NFM":
1177
                    _mem.isnarrow = True
1178
                else:
1179
                    _mem.isnarrow = False
1180

    
1181
            # set the tone
1182
            self._set_tone(mem, _mem)
1183
            #  set the scrambler and compander to off by default
1184
            #  This changes them in the channel memory
1185
            _mem.scrambler = 0
1186
            _mem.compander = 0
1187
            # set the power
1188
            # Updated to resolve "Illegal set on attribute power" Warning
1189
            if str(mem.power) == "None":
1190
                _mem.power = 0  # Default to Low power
1191
            else:
1192
                index = self.POWER_LEVELS.index(mem.power)
1193
                if index == 2:
1194
                    _mem.power = 0b11  # Force H to value of binary 3
1195
                else:
1196
                    _mem.power = index
1197
            # Not sure what this bit does yet but it causes
1198
            # the radio to display
1199
            # MED power when the CPS shows Low Power.
1200
            # Forcing it to 0 to keep them
1201
            # consistent
1202
            _mem.extra_power_bit = 0
1203
            # Set other unknowns to 0 to match default CPS values
1204
            _mem.unknown1 = 0
1205
            _mem.unknown2 = 0
1206
            _mem.unknownbit2 = 0
1207
            _mem.unknown3 = 0
1208
            _mem.Unknown4_shft_dir = 0
1209
            _mem.unknown5 = 0
1210

    
1211
            #  set to mute mode to QT (not QT+DTMF or QT*DTMF) by default
1212
            #  This changes them in the channel memory
1213
            _mem.mute_mode = 1
1214
            self._memobj.valid[number] = MEM_VALID
1215

    
1216
    def _get_settings(self):
1217
        _settings = self._memobj.settings
1218
        _oem = self._memobj.oem
1219

    
1220
        cfg_grp = RadioSettingGroup("cfg_grp", "Config Settings")
1221
        cfg1_grp = RadioSettingGroup("cfg1_grp", "Config Settings 1")
1222
        cfg2_grp = RadioSettingGroup("cfg2_grp", "Config Settings 2")
1223
        if self.MODEL == "KG-1000G Plus":
1224
            vfoaname = "Area A Settings"
1225
        else:
1226
            vfoaname = "VFO A Settings"
1227
        vfoa_grp = RadioSettingGroup("vfoa_grp", vfoaname)
1228
        vfo150_grp = RadioSettingGroup("vfo150_grp", "150M Settings")
1229
        vfo450_grp = RadioSettingGroup("vfo450_grp", "450M Settings")
1230
        vfo20_grp = RadioSettingGroup("vfo20_grp", "20M Settings")
1231
        vfo50_grp = RadioSettingGroup("vfo50_grp", "50M Settings")
1232
        vfo350_grp = RadioSettingGroup("vfo350_grp", "350M Settings")
1233
        vfo850_grp = RadioSettingGroup("vfo850_grp", "850M Settings")
1234
        if self.MODEL == "KG-1000G Plus":
1235
            vfobname = "Area B Settings"
1236
        else:
1237
            vfobname = "VFO B Settings"
1238
        vfob_grp = RadioSettingGroup("vfob_grp", vfobname)
1239

    
1240
        fmradio_grp = RadioSettingGroup("fmradio_grp", "FM Broadcast Memory")
1241
        lmt_grp = RadioSettingGroup("lmt_grp", "Frequency Limits")
1242
        lmwrn_grp = RadioSettingGroup("lmwrn_grp", "USE AT YOUR OWN RISK")
1243
        rxlim_grp = RadioSettingGroup("rxlim_grp", "Rx Limits")
1244
        txlim_grp = RadioSettingGroup("txlim_grp", "Tx Limits")
1245
        oem_grp = RadioSettingGroup("oem_grp", "OEM Info")
1246
        scan_grp = RadioSettingGroup("scan_grp", "Scan Group")
1247
        scanname_grp = RadioSettingGroup("scanname_grp", "Scan Names")
1248
        remote_grp = RadioSettingGroup("remote_grp", "Remote Settings")
1249
        extra_grp = RadioSettingGroup("extra_grp",
1250
                                      "Extra Settings")
1251
        if self.MODEL == "KG-1000G Plus":
1252
            vfoname = "Freq Mode Settings"
1253
        else:
1254
            vfoname = "VFO Settings"
1255
        vfo_grp = RadioSettingGroup("vfo_grp",
1256
                                    vfoname)
1257
        extra_grp.append(oem_grp)
1258
        cfg_grp.append(cfg1_grp)
1259
        cfg_grp.append(cfg2_grp)
1260
        lmt_grp.append(lmwrn_grp)
1261
        lmt_grp.append(rxlim_grp)
1262
        extra_grp.append(lmt_grp)
1263
        vfo_grp.append(vfoa_grp)
1264
        vfo_grp.append(vfob_grp)
1265
        vfoa_grp.append(vfo150_grp)
1266
        vfoa_grp.append(vfo450_grp)
1267
        if self.MODEL == "KG-UV980P":
1268
            vfoa_grp.append(vfo20_grp)
1269
            lmt_grp.append(txlim_grp)
1270
        vfoa_grp.append(vfo50_grp)
1271
        vfoa_grp.append(vfo350_grp)
1272
        vfoa_grp.append(vfo850_grp)
1273
        scan_grp.append(scanname_grp)
1274

    
1275
        group = RadioSettings(cfg_grp, vfo_grp, fmradio_grp,
1276
                              remote_grp, scan_grp, extra_grp)
1277

    
1278
        # Configuration Settings
1279
        if self.MODEL == "KG-1000G Plus":
1280
            voicemap = VOICE_MAP_1000GPLUS
1281
            pttidmap = PTT_ID_MAP_1000GPLUS
1282
            pttidlabel = "DTMF ID"
1283
            dtmflist = DTMF_ST_LIST_1000GPLUS
1284
            dtmflabel = "Sidetone"
1285
            spkmap = SPEAKER_MAP_1000GPLUS
1286
            key_l = KEY_LIST_1000GPLUS
1287
            scqt = SC_QT_MAP_1000GPLUS
1288
            pf1set = PF1_SETTINGS_1000GPLUS
1289
            wmmap = WORKMODE_MAP_1000GPLUS
1290
            totmap = TOT_MAP_1000GPLUS
1291
            lowvlabel = "Voltage Alert"
1292
            fanlabel = "Fan Setting"
1293
            alerttonelabel = "Alert Tone(Hz)"
1294
            tonescanlabel = "Tone Save"
1295
            pttdelaylabel = "DTMF ID Delay (ms)"
1296
            scandetlabel = "Tone Scan"
1297
            txvoltlabel = "Tx Voltage Limit"
1298
            holdtimrptlabel = "Repeater Hold Time (ms)"
1299
            thrvollvllabel = "Tx Voltage Min"
1300
            modepswlabel = "Freq Mode Password"
1301
            narrow1label = "150M W/N"
1302
            mute1label = "150M SP Mute"
1303
            scram1label = "150M Descrambler"
1304
            narrow2label = "450M W/N"
1305
            mute2label = "450M SP Mute"
1306
            scram2label = "450M Descrambler"
1307
            narrow3label = "20M W/N"
1308
            mute3label = "20M SP Mute"
1309
            scram3label = "20M Descrambler"
1310
            narrow4label = "50M W/N"
1311
            mute4label = "50M SP Mute"
1312
            scram4label = "50M Descrambler"
1313
            narrow5label = "350M W/N"
1314
            mute5label = "350M SP Mute"
1315
            scram5label = "350M Descrambler"
1316
            narrow6label = "850M W/N"
1317
            mute6label = "850M SP Mute"
1318
            scram6label = "850M Descrambler"
1319
            narrow7label = "W/N"
1320
            mute7label = "SP Mute"
1321
            scram7label = "Descrambler"
1322
        else:   # 980P or 1000G radios
1323
            voicemap = VOICE_MAP
1324
            pttidmap = PTT_ID_MAP
1325
            pttidlabel = "Caller ID Tx Mode (PTT_ID)"
1326
            dtmflist = DTMF_ST_LIST
1327
            dtmflabel = "DTMF Sidetone"
1328
            spkmap = SPEAKER_MAP
1329
            key_l = KEY_LIST
1330
            scqt = SC_QT_MAP
1331
            pf1set = PF1_SETTINGS
1332
            wmmap = WORKMODE_MAP
1333
            totmap = TOT_MAP
1334
            lowvlabel = "Low Voltage Shutoff"
1335
            fanlabel = "Fan Mode"
1336
            alerttonelabel = "Alert Pulse (Hz)"
1337
            tonescanlabel = "CTCSS/DCS Scan"
1338
            pttdelaylabel = "Caller ID Tx Delay PTT-ID-DLY (ms)"
1339
            scandetlabel = "Scan DET"
1340
            txvoltlabel = "Threshold Voltage Tx"
1341
            holdtimrptlabel = "Hold Time of Repeat (ms)"
1342
            thrvollvllabel = "Threshold Voltage Level"
1343
            modepswlabel = "MODE PSW"
1344
            narrow1label = "150M Bandwidth"
1345
            mute1label = "150M Mute Mode"
1346
            scram1label = "150M Scrambler"
1347
            narrow2label = "450M Bandwidth"
1348
            mute2label = "450M Mute Mode"
1349
            scram2label = "450M Scrambler"
1350
            narrow3label = "20M Bandwidth"
1351
            mute3label = "20M Mute Mode"
1352
            scram3label = "20M Scrambler"
1353
            narrow4label = "50M Bandwidth"
1354
            mute4label = "50M Mute Mode"
1355
            scram4label = "50M Scrambler"
1356
            narrow5label = "350M Bandwidth"
1357
            mute5label = "350M Mute Mode"
1358
            scram5label = "350M Scrambler"
1359
            narrow6label = "850M Bandwidth"
1360
            mute6label = "850M Mute Mode"
1361
            scram6label = "850M Scrambler"
1362
            narrow7label = "Bandwidth"
1363
            mute7label = "Mute Mode"
1364
            scram7label = "Scrambler"
1365

    
1366
        rs = RadioSetting("roger", "Roger Beep",
1367
                          RadioSettingValueList(ROGER_LIST,
1368
                                                ROGER_LIST[_settings.
1369
                                                           roger]))
1370
        cfg1_grp.append(rs)
1371

    
1372
        rs = RadioSetting("beep", "Keypad Beep",
1373
                          RadioSettingValueBoolean(_settings.beep))
1374
        cfg1_grp.append(rs)
1375

    
1376
        rs = RadioSetting("voice", "Voice Guide",
1377
                          RadioSettingValueMap(voicemap,
1378
                                               _settings.voice))
1379
        cfg1_grp.append(rs)
1380

    
1381
        rs = RadioSetting("bcl_a", "Busy Channel Lock-out A",
1382
                          RadioSettingValueBoolean(_settings.bcl_a))
1383
        cfg1_grp.append(rs)
1384

    
1385
        rs = RadioSetting("bcl_b", "Busy Channel Lock-out B",
1386
                          RadioSettingValueBoolean(_settings.bcl_b))
1387
        cfg1_grp.append(rs)
1388

    
1389
        rs = RadioSetting("sc_rev", "Scan Mode",
1390
                          RadioSettingValueMap(SC_REV_MAP, _settings.sc_rev))
1391
        cfg1_grp.append(rs)
1392
        rs = RadioSetting("tot", "Timeout Timer (TOT)",
1393
                          RadioSettingValueMap(
1394
                              totmap, _settings.tot))
1395
        cfg1_grp.append(rs)
1396

    
1397
        if self.MODEL != "KG-1000G Plus":
1398
            rs = RadioSetting("toa", "Timeout Alarm (TOA)",
1399
                              RadioSettingValueList(
1400
                                  TOA_LIST, TOA_LIST[_settings.toa]))
1401
        else:
1402
            rs = RadioSetting("toa", "Overtime Alarm (TOA)",
1403
                              RadioSettingValueMap(
1404
                                  TOA_MAP, _settings.toa))
1405
        cfg1_grp.append(rs)
1406

    
1407
        if self.MODEL != "KG-1000G Plus":
1408
            rs = RadioSetting("ani_sw", "Caller ID Tx - ANI-SW",
1409
                              RadioSettingValueBoolean(_settings.ani_sw))
1410
            cfg1_grp.append(rs)
1411

    
1412
        if self.MODEL != "KG-1000G Plus":
1413
            rs = RadioSetting("ring", "Ring Time (Sec)",
1414
                              RadioSettingValueList(
1415
                                  RING_LIST,
1416
                                  RING_LIST[_settings.ring]))
1417
        else:
1418
            rs = RadioSetting("ring", "Ring Time (Sec)",
1419
                              RadioSettingValueMap(
1420
                                  RING_MAP, _settings.ring))
1421
        cfg1_grp.append(rs)
1422

    
1423
        rs = RadioSetting("dtmfsf", dtmflabel,
1424
                          RadioSettingValueList(
1425
                              dtmflist,
1426
                              dtmflist[_settings.dtmfsf]))
1427
        cfg1_grp.append(rs)
1428

    
1429
        rs = RadioSetting("ptt_id", pttidlabel,
1430
                          RadioSettingValueMap(pttidmap, _settings.ptt_id))
1431
        cfg1_grp.append(rs)
1432

    
1433
        rs = RadioSetting("wt_led", "Standby / WT LED",
1434
                          RadioSettingValueList(
1435
                              BACKLIGHT_LIST,
1436
                              BACKLIGHT_LIST[_settings.wt_led]))
1437
        cfg1_grp.append(rs)
1438

    
1439
        rs = RadioSetting("tx_led", "TX LED",
1440
                          RadioSettingValueList(
1441
                              BACKLIGHT_LIST,
1442
                              BACKLIGHT_LIST[_settings.tx_led]))
1443
        cfg1_grp.append(rs)
1444

    
1445
        rs = RadioSetting("rx_led", "Rx LED",
1446
                          RadioSettingValueList(
1447
                              BACKLIGHT_LIST,
1448
                              BACKLIGHT_LIST[_settings.rx_led]))
1449
        cfg1_grp.append(rs)
1450

    
1451
        if self.MODEL == "KG-1000G Plus":
1452
            rs = RadioSetting("prich_sw", "Priority Scan",
1453
                              RadioSettingValueList(
1454
                                  PRI_CH_SCAN_LIST,
1455
                                  PRI_CH_SCAN_LIST[_settings.prich_sw]))
1456
            cfg1_grp.append(rs)
1457
        else:
1458
            rs = RadioSetting("prich_sw", "Priority Channel Scan",
1459
                              RadioSettingValueBoolean(_settings.prich_sw))
1460
            cfg1_grp.append(rs)
1461

    
1462
        rs = RadioSetting("spk_cont", "Speaker Control",
1463
                          RadioSettingValueMap(
1464
                              spkmap,
1465
                              _settings.spk_cont))
1466
        cfg1_grp.append(rs)
1467

    
1468
        rs = RadioSetting("autolock", "Autolock",
1469
                          RadioSettingValueBoolean(_settings.autolock))
1470
        cfg1_grp.append(rs)
1471

    
1472
        rs = RadioSetting("low_v", lowvlabel,
1473
                          RadioSettingValueBoolean(_settings.low_v))
1474
        cfg1_grp.append(rs)
1475

    
1476
        rs = RadioSetting("fan", fanlabel,
1477
                          RadioSettingValueList(
1478
                              FAN_MODE_LIST,
1479
                              FAN_MODE_LIST[_settings.fan]))
1480
        cfg1_grp.append(rs)
1481

    
1482
        rs = RadioSetting("apo_time", "Auto Power-Off (Min)",
1483
                          RadioSettingValueList(
1484
                              APO_TIME_LIST,
1485
                              APO_TIME_LIST[_settings.apo_time]))
1486
        cfg1_grp.append(rs)
1487

    
1488
        rs = RadioSetting("alert", alerttonelabel,
1489
                          RadioSettingValueMap(ALERT_MAP, _settings.alert))
1490
        cfg1_grp.append(rs)
1491
        rs = RadioSetting("m_pwr", "Medium Power Level (W)",
1492
                          RadioSettingValueMap(M_POWER_MAP,
1493
                                               _settings.m_pwr))
1494
        cfg1_grp.append(rs)
1495

    
1496
        if self.MODEL != "KG-1000G Plus":
1497
            rs = RadioSetting("rpt_set_model", "Model (RPT-SET)",
1498
                              RadioSettingValueList(
1499
                                  RPT_MODE_LIST,
1500
                                  RPT_MODE_LIST[_settings.rpt_set_model]))
1501
        else:
1502
            rs = RadioSetting("rpt_set_model", "Repeater Mode",
1503
                              RadioSettingValueMap(
1504
                                  RPT_MODE_MAP,
1505
                                  _settings.rpt_set_model))
1506
        cfg2_grp.append(rs)
1507

    
1508
        rs = RadioSetting("rpt_spk", "Repeater Speaker Switch (RPT-SPK)",
1509
                          RadioSettingValueBoolean(_settings.rpt_spk))
1510
        cfg2_grp.append(rs)
1511

    
1512
        rs = RadioSetting("rpt_ptt", "Repeater PTT (RPT-PTT)",
1513
                          RadioSettingValueBoolean(_settings.rpt_ptt))
1514
        cfg2_grp.append(rs)
1515

    
1516
        rs = RadioSetting("dtmf_time", "DTMF Tx Duration (ms)",
1517
                          RadioSettingValueList(
1518
                              DTMF_TIMES,
1519
                              DTMF_TIMES[_settings.dtmf_time]))
1520
        cfg2_grp.append(rs)
1521
        rs = RadioSetting("dtmf_int", "DTMF Interval (ms)",
1522
                          RadioSettingValueList(
1523
                              DTMF_INTERVALS,
1524
                              DTMF_INTERVALS[_settings.dtmf_int]))
1525
        cfg2_grp.append(rs)
1526

    
1527
        rs = RadioSetting("sc_qt", tonescanlabel,
1528
                          RadioSettingValueMap(
1529
                              scqt, _settings.sc_qt))
1530
        cfg2_grp.append(rs)
1531

    
1532
        rs = RadioSetting("pri_ch", "Priority Channel",
1533
                          RadioSettingValueInteger(
1534
                              1, 999, _chnum_decode(_settings.pri_ch)))
1535
        cfg2_grp.append(rs)
1536

    
1537
        rs = RadioSetting("ptt_id_dly", pttdelaylabel,
1538
                          RadioSettingValueMap(PTT_ID_DELAY_MAP,
1539
                                               _settings.ptt_id_dly))
1540
        cfg2_grp.append(rs)
1541

    
1542
        rs = RadioSetting("rc_sw", "Remote Control RC-SW",
1543
                          RadioSettingValueBoolean(_settings.rc_sw))
1544
        cfg2_grp.append(rs)
1545

    
1546
        rs = RadioSetting("scan_det", scandetlabel,
1547
                          RadioSettingValueBoolean(_settings.scan_det))
1548
        cfg2_grp.append(rs)
1549

    
1550
        rs = RadioSetting("menu", "Menu Available",
1551
                          RadioSettingValueBoolean(_settings.menu))
1552
        cfg2_grp.append(rs)
1553

    
1554
        rs = RadioSetting("thr_vol_tx", txvoltlabel,
1555
                          RadioSettingValueBoolean(_settings.thr_vol_tx))
1556
        cfg2_grp.append(rs)
1557

    
1558
        rs = RadioSetting("hold_time_rpt", holdtimrptlabel,
1559
                          RadioSettingValueList(
1560
                              HOLD_TIMES,
1561
                              HOLD_TIMES[_settings.hold_time_rpt]))
1562
        cfg2_grp.append(rs)
1563

    
1564
        rs = RadioSetting("auto_am", "Auto AM",
1565
                          RadioSettingValueBoolean(_settings.auto_am))
1566
        cfg2_grp.append(rs)
1567

    
1568
        rs = RadioSetting("rpt_tone", "Repeat Tone",
1569
                          RadioSettingValueBoolean(_settings.rpt_tone))
1570
        cfg2_grp.append(rs)
1571

    
1572
        rs = RadioSetting("pf1_set", "PF1 setting",
1573
                          RadioSettingValueList(
1574
                              pf1set,
1575
                              pf1set[_settings.pf1_set]))
1576
        cfg2_grp.append(rs)
1577

    
1578
        rs = RadioSetting("settings.thr_vol_lvl", thrvollvllabel,
1579
                          RadioSettingValueFloat(
1580
                           9.5, 10.5, _settings.thr_vol_lvl / 100.0, 0.1, 1))
1581
        cfg2_grp.append(rs)
1582

    
1583
        dtmfchars = "0123456789"
1584
        _code = ''
1585
        test = int(_oem.mode_psw)
1586
        _codeobj = '0x{0:0{1}X}'.format(test, 6)
1587
        LOG.debug("codeobj = %s" % _codeobj)
1588
        _psw = str(_codeobj)
1589
        for i in range(2, 8):
1590
            LOG.debug("psw[i] = %s" % _psw[i])
1591
            if _psw[i] in dtmfchars:
1592
                _code += _psw[i]
1593
        val_psw = RadioSettingValueString(6, 6, _code, False)
1594
        val_psw.set_charset(dtmfchars)
1595
        rs = RadioSetting("oem.mode_psw", modepswlabel, val_psw)
1596

    
1597
        def apply_psw_id(setting, obj):
1598
            val2 = hex(int(str(val_psw), 16))
1599
            if (int(val2, 16) != 0):
1600
                while len(val2) < 8:
1601
                    val2 += '0'
1602
            psw = int(str(val2), 16)
1603
            obj.mode_psw = psw
1604
        rs.set_apply_callback(apply_psw_id, _oem)
1605
        cfg2_grp.append(rs)
1606

    
1607
        rs = RadioSetting("ABR", "Backlight On Time (ABR)",
1608
                          RadioSettingValueList(
1609
                              ABR_LIST,
1610
                              ABR_LIST[_settings.ABR]))
1611
        cfg2_grp.append(rs)
1612

    
1613
        rs = RadioSetting("KeyA", "Key A",
1614
                          RadioSettingValueList(
1615
                              key_l,
1616
                              key_l[_settings.KeyA]))
1617
        cfg2_grp.append(rs)
1618
        rs = RadioSetting("KeyB", "Key B",
1619
                          RadioSettingValueList(
1620
                              key_l,
1621
                              key_l[_settings.KeyB]))
1622
        cfg2_grp.append(rs)
1623
        rs = RadioSetting("KeyC", "Key C",
1624
                          RadioSettingValueList(
1625
                              key_l,
1626
                              key_l[_settings.KeyC]))
1627
        cfg2_grp.append(rs)
1628

    
1629
        if self.MODEL == "KG-1000G Plus":
1630
            rs = RadioSetting("KeyD", "Key D",
1631
                              RadioSettingValueList(
1632
                                  KEY_LIST_1000GPLUS,
1633
                                  KEY_LIST_1000GPLUS[_settings.KeyD]))
1634
            cfg2_grp.append(rs)
1635

    
1636
        rs = RadioSetting("key_lock", "Key Lock Active",
1637
                          RadioSettingValueBoolean(_settings.key_lock))
1638
        cfg2_grp.append(rs)
1639

    
1640
        rs = RadioSetting("act_area", "Active Area (BAND)",
1641
                          RadioSettingValueList(
1642
                              ACTIVE_AREA_LIST,
1643
                              ACTIVE_AREA_LIST[_settings.act_area]))
1644
        cfg2_grp.append(rs)
1645
        rs = RadioSetting("tdr_off", "TDR",
1646
                          RadioSettingValueList(
1647
                              TDR_LIST,
1648
                              TDR_LIST[_settings.tdr_off]))
1649
        cfg2_grp.append(rs)
1650

    
1651
        # Freq Limits settings
1652

    
1653
        # Convert Integer back to correct limit HEX value:
1654
        s = self._memobj
1655

    
1656
        _temp = int(s.bandlimits.limit_144M_ChA_rx_start) // 10
1657
        val = RadioSettingValueInteger(0, 999, _temp)
1658
        rs = RadioSetting("bandlimits.limit_144M_ChA_rx_start",
1659
                          "144M Area A Rx Lower Limit (MHz)",
1660
                          val)
1661
        rxlim_grp.append(rs)
1662

    
1663
        _temp = int(s.bandlimits.limit_144M_ChA_rx_stop) // 10
1664
        val = RadioSettingValueInteger(0, 999, _temp)
1665
        rs = RadioSetting("bandlimits.limit_144M_ChA_rx_stop",
1666
                          "144M Area A Rx Upper Limit (+ .9975 MHz)",
1667
                          val)
1668
        rxlim_grp.append(rs)
1669

    
1670
        _temp = int(s.bandlimits.limit_144M_ChB_rx_start) // 10
1671
        val = RadioSettingValueInteger(0, 999, _temp)
1672
        rs = RadioSetting("bandlimits.limit_144M_ChB_rx_start",
1673
                          "144M Area B Rx Lower Limit (MHz)",
1674
                          val)
1675
        rxlim_grp.append(rs)
1676

    
1677
        _temp = int(s.bandlimits.limit_144M_ChB_rx_stop) // 10
1678
        val = RadioSettingValueInteger(0, 999, _temp)
1679
        rs = RadioSetting("bandlimits.limit_144M_ChB_rx_stop",
1680
                          "144M Area B Rx Upper Limit (+ .9975 MHz)",
1681
                          val)
1682
        rxlim_grp.append(rs)
1683

    
1684
        _temp = int(s.bandlimits.limit_70cm_rx_start) // 10
1685
        val = RadioSettingValueInteger(0, 999, _temp)
1686
        rs = RadioSetting("bandlimits.limit_70cm_rx_start",
1687
                          "450M Rx Lower Limit (MHz)",
1688
                          val)
1689
        rxlim_grp.append(rs)
1690

    
1691
        _temp = int(s.bandlimits.limit_70cm_rx_stop) // 10
1692
        val = RadioSettingValueInteger(0, 999, _temp)
1693
        rs = RadioSetting("bandlimits.limit_70cm_rx_stop",
1694
                          "450M Rx Upper Limit (+ .9975 MHz)",
1695
                          val)
1696
        rxlim_grp.append(rs)
1697

    
1698
        if self.MODEL == "KG-UV980P":
1699
            _temp = int(s.bandlimits.limit_10m_rx_start) // 10
1700
            val = RadioSettingValueInteger(0, 999, _temp)
1701
            rs = RadioSetting("bandlimits.limit_10m_rx_start",
1702
                              "20M Rx Lower Limit (MHz)",
1703
                              val)
1704
            rxlim_grp.append(rs)
1705

    
1706
            _temp = int(s.bandlimits.limit_10m_rx_stop) // 10
1707
            val = RadioSettingValueInteger(0, 999, _temp)
1708
            rs = RadioSetting("bandlimits.limit_10m_rx_stop",
1709
                              "20M Rx Upper Limit (+ .9975 MHz)",
1710
                              val)
1711
            rxlim_grp.append(rs)
1712

    
1713
        _temp = int(s.bandlimits.limit_6m_rx_start) // 10
1714
        val = RadioSettingValueInteger(0, 999, _temp)
1715
        rs = RadioSetting("bandlimits.limit_6m_rx_start",
1716
                          "50M Rx Lower Limit (MHz)",
1717
                          val)
1718
        rxlim_grp.append(rs)
1719

    
1720
        _temp = int(s.bandlimits.limit_6m_rx_stop) // 10
1721
        val = RadioSettingValueInteger(0, 999, _temp)
1722
        rs = RadioSetting("bandlimits.limit_6m_rx_stop",
1723
                          "50M Rx Upper Limit (+ .9975 MHz)",
1724
                          val)
1725
        rxlim_grp.append(rs)
1726

    
1727
        _temp = int(s.bandlimits.limit_350M_rx_start) // 10
1728
        val = RadioSettingValueInteger(0, 999, _temp)
1729
        rs = RadioSetting("bandlimits.limit_350M_rx_start",
1730
                          "350M Rx Lower Limit (MHz)",
1731
                          val)
1732
        rxlim_grp.append(rs)
1733

    
1734
        _temp = int(s.bandlimits.limit_350M_rx_stop) // 10
1735
        val = RadioSettingValueInteger(0, 999, _temp)
1736
        rs = RadioSetting("bandlimits.limit_350M_rx_stop",
1737
                          "350M Rx Upper Limit (+ .9975 MHz)",
1738
                          val)
1739
        rxlim_grp.append(rs)
1740

    
1741
        _temp = int(s.bandlimits.limit_850M_rx_start) // 10
1742
        val = RadioSettingValueInteger(0, 999, _temp)
1743
        rs = RadioSetting("bandlimits.limit_850M_rx_start",
1744
                          "850M Rx Lower Limit (MHz)",
1745
                          val)
1746
        rxlim_grp.append(rs)
1747

    
1748
        _temp = int(s.bandlimits.limit_850M_rx_stop) // 10
1749
        val = RadioSettingValueInteger(0, 999, _temp)
1750
        rs = RadioSetting("bandlimits.limit_850M_rx_stop",
1751
                          "850M Rx Upper Limit (+ .9975 MHz)",
1752
                          val)
1753
        rxlim_grp.append(rs)
1754
        if self.MODEL == "KG-UV980P":
1755
            _temp = int(s.bandlimits.limit_144M_tx_start) // 10
1756
            val = RadioSettingValueInteger(0, 999, _temp)
1757
            rs = RadioSetting("bandlimits.limit_144M_tx_start",
1758
                              "144M Tx Lower Limit (MHz)",
1759
                              val)
1760
            txlim_grp.append(rs)
1761

    
1762
            _temp = int(s.bandlimits.limit_144M_tx_stop) // 10
1763
            val = RadioSettingValueInteger(0, 999, _temp)
1764
            rs = RadioSetting("bandlimits.limit_144M_tx_stop",
1765
                              "144M Tx Upper Limit (+ .9975 MHz)",
1766
                              val)
1767
            txlim_grp.append(rs)
1768

    
1769
            _temp = int(s.bandlimits.limit_70cm_tx_start) // 10
1770
            val = RadioSettingValueInteger(0, 999, _temp)
1771
            rs = RadioSetting("bandlimits.limit_70cm_tx_start",
1772
                              "450M Tx Lower Limit (MHz)",
1773
                              val)
1774
            txlim_grp.append(rs)
1775

    
1776
            _temp = int(s.bandlimits.limit_70cm_tx_stop) // 10
1777
            val = RadioSettingValueInteger(0, 999, _temp)
1778
            rs = RadioSetting("bandlimits.limit_70cm_tx_stop",
1779
                              "450M tx Upper Limit (+ .9975 MHz)",
1780
                              val)
1781
            txlim_grp.append(rs)
1782

    
1783
            if self.MODEL == "KG-UV980P":
1784
                _temp = int(s.bandlimits.limit_10m_tx_start) // 10
1785
                val = RadioSettingValueInteger(0, 999, _temp)
1786
                rs = RadioSetting("bandlimits.limit_10m_tx_start",
1787
                                  "20M tx Lower Limit (MHz)",
1788
                                  val)
1789
                txlim_grp.append(rs)
1790

    
1791
                _temp = int(s.bandlimits.limit_10m_tx_stop) // 10
1792
                val = RadioSettingValueInteger(0, 999, _temp)
1793
                rs = RadioSetting("bandlimits.limit_10m_tx_stop",
1794
                                  "20M tx Upper Limit (+ .9975 MHz)",
1795
                                  val)
1796
                txlim_grp.append(rs)
1797

    
1798
            _temp = int(s.bandlimits.limit_6m_tx_start) // 10
1799
            val = RadioSettingValueInteger(0, 999, _temp)
1800
            rs = RadioSetting("bandlimits.limit_6m_tx_start",
1801
                              "50M tx Lower Limit (MHz)",
1802
                              val)
1803
            txlim_grp.append(rs)
1804

    
1805
            _temp = int(s.bandlimits.limit_6m_tx_stop) // 10
1806
            val = RadioSettingValueInteger(0, 999, _temp)
1807
            rs = RadioSetting("bandlimits.limit_6m_tx_stop",
1808
                              "50M tx Upper Limit (+ .9975 MHz)",
1809
                              val)
1810
            txlim_grp.append(rs)
1811

    
1812
        # VFO Settings
1813
        rs = RadioSetting("vfomode_a", "Working Mode",
1814
                          RadioSettingValueMap(wmmap,
1815
                                               _settings.vfomode_a))
1816
        vfoa_grp.append(rs)
1817

    
1818
        rs = RadioSetting("vfoband_a", "Current Band",
1819
                          RadioSettingValueMap(VFOBAND_MAP,
1820
                                               _settings.vfoband_a))
1821
        vfoa_grp.append(rs)
1822

    
1823
        rs = RadioSetting("vfochan_a", "Active/Work Channel",
1824
                          RadioSettingValueInteger(1, 999,
1825
                                                   _chnum_decode(
1826
                                                    _settings.vfochan_a)))
1827
        vfoa_grp.append(rs)
1828

    
1829
        rs = RadioSetting("vfosquelch_a", "Squelch",
1830
                          RadioSettingValueInteger(0, 9,
1831
                                                   _settings.vfosquelch_a))
1832
        vfoa_grp.append(rs)
1833
        rs = RadioSetting("vfostep_a", "Step",
1834
                          RadioSettingValueList(
1835
                            STEP_LIST,
1836
                            STEP_LIST[_settings.vfostep_a]))
1837
        vfoa_grp.append(rs)
1838

    
1839
        # #####################
1840

    
1841
        rs = RadioSetting("vfofreq1", "150M Freq",
1842
                          RadioSettingValueFloat(
1843
                             0, 999.999999, (_freq_decode
1844
                                             (_settings.vfofreq1) /
1845
                                             1000000.0), 0.000001, 6))
1846
        vfo150_grp.append(rs)
1847

    
1848
        if self.MODEL != "KG-1000G Plus":
1849
            rs = RadioSetting("vfoofst1", "150M Offset",
1850
                              RadioSettingValueFloat(
1851
                                0, 999.999999, (_freq_decode
1852
                                                (_settings.vfoofst1) /
1853
                                                1000000.0), 0.000001, 6))
1854
            vfo150_grp.append(rs)
1855

    
1856
        rs = RadioSetting("rxtone1", "150M Rx tone",
1857
                          RadioSettingValueMap(
1858
                            TONE_MAP, _settings.rxtone1))
1859
        vfo150_grp.append(rs)
1860

    
1861
        rs = RadioSetting("txtone1", "150M Tx tone",
1862
                          RadioSettingValueMap(
1863
                            TONE_MAP, _settings.txtone1))
1864
        vfo150_grp.append(rs)
1865

    
1866
        rs = RadioSetting("power1", "150M Power",
1867
                          RadioSettingValueMap(
1868
                            POWER_MAP, _settings.power1))
1869
        vfo150_grp.append(rs)
1870

    
1871
        rs = RadioSetting("narrow1", narrow1label,
1872
                          RadioSettingValueMap(
1873
                            BANDWIDTH_MAP, _settings.narrow1))
1874
        vfo150_grp.append(rs)
1875

    
1876
        rs = RadioSetting("mute1", mute1label,
1877
                          RadioSettingValueMap(
1878
                            MUTE_MODE_MAP, _settings.mute1))
1879
        vfo150_grp.append(rs)
1880

    
1881
        if self.MODEL == "KG-1000G Plus":
1882
            rs = RadioSetting("shft_dir1", "150M Repeater",
1883
                              RadioSettingValueBoolean(
1884
                                _settings.shft_dir1))
1885
        else:
1886
            rs = RadioSetting("shft_dir1", "150M Shift Direction",
1887
                              RadioSettingValueList(
1888
                                DUPLEX_LIST,
1889
                                DUPLEX_LIST[_settings.shft_dir1]))
1890
        vfo150_grp.append(rs)
1891

    
1892
        rs = RadioSetting("compander1", "150M Compander",
1893
                          RadioSettingValueBoolean(
1894
                            _settings.compander1))
1895
        vfo150_grp.append(rs)
1896

    
1897
        rs = RadioSetting("scrambler1", scram1label,
1898
                          RadioSettingValueList(
1899
                            SCRAMBLER_LIST,
1900
                            SCRAMBLER_LIST[_settings.scrambler1]))
1901
        vfo150_grp.append(rs)
1902
        rs = RadioSetting("am_mode1", "150M AM Mode",
1903
                          RadioSettingValueBoolean(
1904
                            _settings.am_mode1))
1905
        vfo150_grp.append(rs)
1906

    
1907
        # ###########################
1908

    
1909
        rs = RadioSetting("vfofreq2", "450M Freq",
1910
                          RadioSettingValueFloat(
1911
                             0, 999.999999, (_freq_decode(
1912
                                             _settings.vfofreq2) /
1913
                                             1000000.0), 0.000001, 6))
1914
        vfo450_grp.append(rs)
1915

    
1916
        if self.MODEL != "KG-1000G Plus":
1917
            rs = RadioSetting("vfoofst2", "450M Offset",
1918
                              RadioSettingValueFloat(
1919
                                0, 999.999999, (_freq_decode(
1920
                                                _settings.vfoofst2) /
1921
                                                1000000.0), 0.000001, 6))
1922
            vfo450_grp.append(rs)
1923

    
1924
        rs = RadioSetting("rxtone2", "450M Rx tone",
1925
                          RadioSettingValueMap(
1926
                            TONE_MAP, _settings.rxtone2))
1927
        vfo450_grp.append(rs)
1928

    
1929
        rs = RadioSetting("txtone2", "450M Tx tone",
1930
                          RadioSettingValueMap(
1931
                            TONE_MAP, _settings.txtone2))
1932
        vfo450_grp.append(rs)
1933

    
1934
        rs = RadioSetting("power2", "450M Power",
1935
                          RadioSettingValueMap(
1936
                            POWER_MAP, _settings.power2))
1937
        vfo450_grp.append(rs)
1938

    
1939
        rs = RadioSetting("narrow2", narrow2label,
1940
                          RadioSettingValueMap(
1941
                            BANDWIDTH_MAP, _settings.narrow2))
1942
        vfo450_grp.append(rs)
1943

    
1944
        rs = RadioSetting("mute2", mute2label,
1945
                          RadioSettingValueMap(
1946
                            MUTE_MODE_MAP, _settings.mute2))
1947
        vfo450_grp.append(rs)
1948

    
1949
        if self.MODEL == "KG-1000G Plus":
1950
            rs = RadioSetting("shft_dir2", "450M Repeater",
1951
                              RadioSettingValueBoolean(
1952
                                _settings.shft_dir2))
1953
        else:
1954
            rs = RadioSetting("shft_dir2", "450M Shift Direction",
1955
                              RadioSettingValueList(
1956
                                DUPLEX_LIST,
1957
                                DUPLEX_LIST[_settings.shft_dir2]))
1958
        vfo450_grp.append(rs)
1959

    
1960
        rs = RadioSetting("compander2", "450M Compander",
1961
                          RadioSettingValueBoolean(
1962
                            _settings.compander2))
1963
        vfo450_grp.append(rs)
1964

    
1965
        rs = RadioSetting("scrambler2", scram2label,
1966
                          RadioSettingValueList(
1967
                            SCRAMBLER_LIST,
1968
                            SCRAMBLER_LIST[_settings.scrambler2]))
1969
        vfo450_grp.append(rs)
1970

    
1971
        rs = RadioSetting("am_mode2", "450M AM Mode",
1972
                          RadioSettingValueBoolean(
1973
                            _settings.am_mode2))
1974
        vfo450_grp.append(rs)
1975

    
1976
        # ###########################
1977
        if self.MODEL == "KG-UV980P":
1978
            rs = RadioSetting("vfofreq3", "20M Freq",
1979
                              RadioSettingValueFloat(
1980
                                0, 999.999999, (_freq_decode(
1981
                                                _settings.vfofreq3) /
1982
                                                1000000.0), 0.000001, 6))
1983
            vfo20_grp.append(rs)
1984

    
1985
            if self.MODEL == "KG-UV980P":
1986
                rs = RadioSetting("vfoofst3", "20M Offset",
1987
                                  RadioSettingValueFloat(
1988
                                    0, 999.999999, (_freq_decode(
1989
                                                    _settings.vfoofst3) /
1990
                                                    1000000.0), 0.000001, 6))
1991
                vfo20_grp.append(rs)
1992

    
1993
            rs = RadioSetting("rxtone3", "20M Rx tone",
1994
                              RadioSettingValueMap(
1995
                                TONE_MAP, _settings.rxtone3))
1996
            vfo20_grp.append(rs)
1997

    
1998
            rs = RadioSetting("txtone3", "20M Tx tone",
1999
                              RadioSettingValueMap(
2000
                                TONE_MAP, _settings.txtone3))
2001
            vfo20_grp.append(rs)
2002

    
2003
            rs = RadioSetting("power3", "20M Power",
2004
                              RadioSettingValueMap(
2005
                                POWER_MAP, _settings.power3))
2006
            vfo20_grp.append(rs)
2007

    
2008
            rs = RadioSetting("narrow3", narrow3label,
2009
                              RadioSettingValueMap(
2010
                                BANDWIDTH_MAP, _settings.narrow3))
2011
            vfo20_grp.append(rs)
2012

    
2013
            rs = RadioSetting("mute3", mute3label,
2014
                              RadioSettingValueMap(
2015
                                MUTE_MODE_MAP, _settings.mute3))
2016
            vfo20_grp.append(rs)
2017

    
2018
            if self.MODEL == "KG-1000G Plus":
2019
                rs = RadioSetting("shft_dir3", "20M Repeater",
2020
                                  RadioSettingValueBoolean(
2021
                                    _settings.shft_dir3))
2022
            else:
2023
                rs = RadioSetting("shft_dir3", "20M Shift Direction",
2024
                                  RadioSettingValueList(
2025
                                    DUPLEX_LIST,
2026
                                    DUPLEX_LIST[_settings.shft_dir3]))
2027
            vfo20_grp.append(rs)
2028

    
2029
            rs = RadioSetting("compander3", "20M Compander",
2030
                              RadioSettingValueBoolean(
2031
                                _settings.compander3))
2032
            vfo20_grp.append(rs)
2033

    
2034
            rs = RadioSetting("scrambler3", scram3label,
2035
                              RadioSettingValueList(
2036
                                SCRAMBLER_LIST,
2037
                                SCRAMBLER_LIST[_settings.scrambler3]))
2038
            vfo20_grp.append(rs)
2039

    
2040
            rs = RadioSetting("am_mode3", "20M AM Mode",
2041
                              RadioSettingValueBoolean(
2042
                                _settings.am_mode3))
2043
            vfo20_grp.append(rs)
2044

    
2045
        # ###########################
2046

    
2047
        rs = RadioSetting("vfofreq4", "50M Freq",
2048
                          RadioSettingValueFloat(
2049
                             0, 999.999999, (_freq_decode(
2050
                                             _settings.vfofreq4) /
2051
                                             1000000.0), 0.000001, 6))
2052
        vfo50_grp.append(rs)
2053

    
2054
        if self.MODEL != "KG-1000G Plus":
2055
            rs = RadioSetting("vfoofst4", "50M Offset",
2056
                              RadioSettingValueFloat(
2057
                                0, 999.999999, (_freq_decode(
2058
                                                _settings.vfoofst4) /
2059
                                                1000000.0), 0.000001, 6))
2060
            vfo50_grp.append(rs)
2061

    
2062
        rs = RadioSetting("rxtone4", "50M Rx tone",
2063
                          RadioSettingValueMap(
2064
                            TONE_MAP, _settings.rxtone4))
2065
        vfo50_grp.append(rs)
2066

    
2067
        rs = RadioSetting("txtone4", "50M Tx tone",
2068
                          RadioSettingValueMap(
2069
                            TONE_MAP, _settings.txtone4))
2070
        vfo50_grp.append(rs)
2071

    
2072
        rs = RadioSetting("power4", "50M Power",
2073
                          RadioSettingValueMap(
2074
                            POWER_MAP, _settings.power4))
2075
        vfo50_grp.append(rs)
2076

    
2077
        rs = RadioSetting("narrow4", narrow4label,
2078
                          RadioSettingValueMap(
2079
                            BANDWIDTH_MAP, _settings.narrow4))
2080
        vfo50_grp.append(rs)
2081

    
2082
        rs = RadioSetting("mute4", mute4label,
2083
                          RadioSettingValueMap(
2084
                            MUTE_MODE_MAP, _settings.mute4))
2085
        vfo50_grp.append(rs)
2086

    
2087
        if self.MODEL == "KG-1000G Plus":
2088
            rs = RadioSetting("shft_dir4", "50M Repeater",
2089
                              RadioSettingValueBoolean(
2090
                                _settings.shft_dir4))
2091
        else:
2092
            rs = RadioSetting("shft_dir4", "50M Shift Direction",
2093
                              RadioSettingValueList(
2094
                                DUPLEX_LIST,
2095
                                DUPLEX_LIST[_settings.shft_dir4]))
2096
        vfo50_grp.append(rs)
2097

    
2098
        rs = RadioSetting("compander4", "50M Compander",
2099
                          RadioSettingValueBoolean(
2100
                            _settings.compander4))
2101
        vfo50_grp.append(rs)
2102

    
2103
        rs = RadioSetting("scrambler4", scram4label,
2104
                          RadioSettingValueList(
2105
                            SCRAMBLER_LIST,
2106
                            SCRAMBLER_LIST[_settings.scrambler4]))
2107
        vfo50_grp.append(rs)
2108

    
2109
        rs = RadioSetting("am_mode4", "50M AM Mode",
2110
                          RadioSettingValueBoolean(
2111
                            _settings.am_mode4))
2112
        vfo50_grp.append(rs)
2113
        # ###########################
2114
        rs = RadioSetting("vfofreq5", "350M Freq",
2115
                          RadioSettingValueFloat(
2116
                             0, 999.999999, (_freq_decode(
2117
                                             _settings.vfofreq5) /
2118
                                             1000000.0), 0.000001, 6))
2119
        vfo350_grp.append(rs)
2120

    
2121
        if self.MODEL != "KG-1000G Plus":
2122
            rs = RadioSetting("vfoofst5", "350M Offset",
2123
                              RadioSettingValueFloat(
2124
                                0, 999.999999, (_freq_decode(
2125
                                                _settings.vfoofst5) /
2126
                                                1000000.0), 0.000001, 6))
2127
            vfo350_grp.append(rs)
2128

    
2129
        rs = RadioSetting("rxtone5", "350M Rx tone",
2130
                          RadioSettingValueMap(
2131
                            TONE_MAP, _settings.rxtone5))
2132
        vfo350_grp.append(rs)
2133

    
2134
        rs = RadioSetting("txtone5", "350M Tx tone",
2135
                          RadioSettingValueMap(
2136
                            TONE_MAP, _settings.txtone5))
2137
        vfo350_grp.append(rs)
2138

    
2139
        rs = RadioSetting("power5", "350M Power",
2140
                          RadioSettingValueMap(
2141
                            POWER_MAP, _settings.power5))
2142
        vfo350_grp.append(rs)
2143

    
2144
        rs = RadioSetting("narrow5", narrow5label,
2145
                          RadioSettingValueMap(
2146
                            BANDWIDTH_MAP, _settings.narrow5))
2147
        vfo350_grp.append(rs)
2148

    
2149
        rs = RadioSetting("mute5", mute5label,
2150
                          RadioSettingValueMap(
2151
                            MUTE_MODE_MAP, _settings.mute5))
2152
        vfo350_grp.append(rs)
2153

    
2154
        if self.MODEL == "KG-1000G Plus":
2155
            rs = RadioSetting("shft_dir5", "350M Repeater",
2156
                              RadioSettingValueBoolean(
2157
                                _settings.shft_dir5))
2158
        else:
2159
            rs = RadioSetting("shft_dir5", "350M Shift Direction",
2160
                              RadioSettingValueList(
2161
                                DUPLEX_LIST,
2162
                                DUPLEX_LIST[_settings.shft_dir5]))
2163
        vfo350_grp.append(rs)
2164

    
2165
        rs = RadioSetting("compander5", "350M Compander",
2166
                          RadioSettingValueBoolean(
2167
                            _settings.compander5))
2168
        vfo350_grp.append(rs)
2169

    
2170
        rs = RadioSetting("scrambler5", scram5label,
2171
                          RadioSettingValueList(
2172
                            SCRAMBLER_LIST,
2173
                            SCRAMBLER_LIST[_settings.scrambler5]))
2174
        vfo350_grp.append(rs)
2175

    
2176
        rs = RadioSetting("am_mode5", "350M AM Mode",
2177
                          RadioSettingValueBoolean(
2178
                            _settings.am_mode5))
2179
        vfo350_grp.append(rs)
2180

    
2181
        # ############################
2182
        rs = RadioSetting("vfofreq6", "850M Freq",
2183
                          RadioSettingValueFloat(
2184
                             0, 999.999999, (_freq_decode(
2185
                                             _settings.vfofreq6) /
2186
                                             1000000.0), 0.000001, 6))
2187
        vfo850_grp.append(rs)
2188
        if self.MODEL != "KG-1000G Plus":
2189
            rs = RadioSetting("vfoofst6", "850M Offset",
2190
                              RadioSettingValueFloat(
2191
                                0, 999.999999, (_freq_decode(
2192
                                                _settings.vfoofst6) /
2193
                                                1000000.0), 0.000001, 6))
2194
            vfo850_grp.append(rs)
2195

    
2196
        rs = RadioSetting("rxtone6", "850M Rx tone",
2197
                          RadioSettingValueMap(
2198
                            TONE_MAP, _settings.rxtone6))
2199
        vfo850_grp.append(rs)
2200

    
2201
        rs = RadioSetting("txtone6", "850M Tx tone",
2202
                          RadioSettingValueMap(
2203
                            TONE_MAP, _settings.txtone6))
2204
        vfo850_grp.append(rs)
2205

    
2206
        rs = RadioSetting("power6", "850M Power",
2207
                          RadioSettingValueMap(
2208
                            POWER_MAP, _settings.power6))
2209
        vfo850_grp.append(rs)
2210

    
2211
        rs = RadioSetting("narrow6", narrow6label,
2212
                          RadioSettingValueMap(
2213
                            BANDWIDTH_MAP, _settings.narrow6))
2214
        vfo850_grp.append(rs)
2215

    
2216
        rs = RadioSetting("mute6", mute6label,
2217
                          RadioSettingValueMap(
2218
                            MUTE_MODE_MAP, _settings.mute6))
2219
        vfo850_grp.append(rs)
2220

    
2221
        if self.MODEL == "KG-1000G Plus":
2222
            rs = RadioSetting("shft_dir6", "850M Repeater",
2223
                              RadioSettingValueBoolean(
2224
                                _settings.shft_dir6))
2225
        else:
2226
            rs = RadioSetting("shft_dir6", "850M Shift Direction",
2227
                              RadioSettingValueList(
2228
                                DUPLEX_LIST,
2229
                                DUPLEX_LIST[_settings.shft_dir6]))
2230
        vfo850_grp.append(rs)
2231

    
2232
        rs = RadioSetting("compander6", "850M Compander",
2233
                          RadioSettingValueBoolean(
2234
                            _settings.compander6))
2235
        vfo850_grp.append(rs)
2236

    
2237
        rs = RadioSetting("scrambler6", scram6label,
2238
                          RadioSettingValueList(
2239
                            SCRAMBLER_LIST,
2240
                            SCRAMBLER_LIST[_settings.scrambler6]))
2241
        vfo850_grp.append(rs)
2242

    
2243
        rs = RadioSetting("am_mode6", "850M AM Mode",
2244
                          RadioSettingValueBoolean(
2245
                            _settings.am_mode6))
2246
        vfo850_grp.append(rs)
2247

    
2248
        # ###########################
2249

    
2250
        rs = RadioSetting("vfomode_b", "Working Mode",
2251
                          RadioSettingValueMap(wmmap,
2252
                                               _settings.vfomode_b))
2253
        vfob_grp.append(rs)
2254

    
2255
        rs = RadioSetting("vfochan_b", "Active/Work Channel",
2256
                          RadioSettingValueInteger(1, 999,
2257
                                                   _chnum_decode(
2258
                                                    _settings.vfochan_b)))
2259
        vfob_grp.append(rs)
2260

    
2261
        rs = RadioSetting("vfofreq7", "Freq",
2262
                          RadioSettingValueFloat(
2263
                             0, 999.999999, (_freq_decode(
2264
                                             _settings.vfofreq7) /
2265
                                             1000000.0), 0.000001, 6))
2266
        vfob_grp.append(rs)
2267

    
2268
        if self.MODEL != "KG-1000G Plus":
2269
            rs = RadioSetting("vfoofst7", "Offset",
2270
                              RadioSettingValueFloat(
2271
                                0, 999.999999, (_freq_decode(
2272
                                                _settings.vfoofst7) /
2273
                                                1000000.0), 0.000001, 6))
2274
            vfob_grp.append(rs)
2275

    
2276
        rs = RadioSetting("rxtone7", "Rx tone",
2277
                          RadioSettingValueMap(
2278
                            TONE_MAP, _settings.rxtone7))
2279
        vfob_grp.append(rs)
2280

    
2281
        rs = RadioSetting("txtone7", "Tx tone",
2282
                          RadioSettingValueMap(
2283
                            TONE_MAP, _settings.txtone7))
2284
        vfob_grp.append(rs)
2285
        rs = RadioSetting("power7", "Power",
2286
                          RadioSettingValueMap(
2287
                            POWER_MAP, _settings.power7))
2288
        vfob_grp.append(rs)
2289
        rs = RadioSetting("narrow7", narrow7label,
2290
                          RadioSettingValueMap(
2291
                            BANDWIDTH_MAP, _settings.narrow7))
2292
        vfob_grp.append(rs)
2293
        rs = RadioSetting("mute7", mute7label,
2294
                          RadioSettingValueMap(
2295
                            MUTE_MODE_MAP, _settings.mute7))
2296
        vfob_grp.append(rs)
2297
        if self.MODEL == "KG-1000G Plus":
2298
            rs = RadioSetting("shft_dir7", "Repeater",
2299
                              RadioSettingValueBoolean(
2300
                                _settings.shft_dir7))
2301
        else:
2302
            rs = RadioSetting("shft_dir7", "Shift Direction",
2303
                              RadioSettingValueList(
2304
                                DUPLEX_LIST,
2305
                                DUPLEX_LIST[_settings.shft_dir7]))
2306
        vfob_grp.append(rs)
2307
        rs = RadioSetting("compander7", "Compander",
2308
                          RadioSettingValueBoolean(
2309
                            _settings.compander7))
2310
        vfob_grp.append(rs)
2311

    
2312
        rs = RadioSetting("scrambler7", scram7label,
2313
                          RadioSettingValueList(
2314
                            SCRAMBLER_LIST,
2315
                            SCRAMBLER_LIST[_settings.scrambler7]))
2316
        vfob_grp.append(rs)
2317

    
2318
        rs = RadioSetting("vfosquelch_b", "Squelch",
2319
                          RadioSettingValueInteger(0, 9,
2320
                                                   _settings.vfosquelch_b))
2321
        vfob_grp.append(rs)
2322
        rs = RadioSetting("vfostep_b", "Step",
2323
                          RadioSettingValueList(
2324
                            STEP_LIST,
2325
                            STEP_LIST[_settings.vfostep_b]))
2326
        vfob_grp.append(rs)
2327

    
2328
        # Scan Group Settings
2329
        def _decode(lst):
2330
            LOG.debug("lst %s", lst)
2331
            _str = ''.join([chr(c) for c in lst
2332
                            if chr(c) in SCANNAME_CHARSET])
2333
            return _str
2334

    
2335
        rs = RadioSetting("scan_a_act", "Scan A Active Group",
2336
                          RadioSettingValueList(
2337
                            SCAN_GROUP_LIST,
2338
                            SCAN_GROUP_LIST[_settings.scan_a_act]))
2339
        scan_grp.append(rs)
2340
        rs = RadioSetting("scan_b_act", "Scan B Active Group",
2341
                          RadioSettingValueList(
2342
                             SCAN_GROUP_LIST,
2343
                             SCAN_GROUP_LIST[_settings.scan_b_act]))
2344
        scan_grp.append(rs)
2345

    
2346
        for i in range(1, 11):
2347
            x = str(i)
2348
            _str = _decode(getattr(_settings, 'scanname%s' % x))
2349
            LOG.debug("ScanName %s", i)
2350
            LOG.debug("is %s", _str)
2351
            # CPS treats PPPPPP as a blank name as it is the factory reset
2352
            # value"
2353
            # The Radio treats PPPPPPP as a blank name and can display 8
2354
            # chars"
2355
            # Force Chirp to blank out the scan name if value is PPPPPPP
2356
            # to match the radio"
2357
            # Blank out the name if first 6 are spaces or null and the
2358
            # 7th is a P to handle
2359
            # firmware peculiarities in handling all 8 characters.
2360
            if _str[0:7] == "PPPPPPP":
2361
                _str = ""
2362
            if self.MODEL != "KG-1000G Plus":
2363
                if _str[0:7] == "\x00\x00\x00\x00\x00\x00P":
2364
                    _str = ""
2365
                elif _str[0:7] == "\x20\x20\x20\x20\x20\x20P":
2366
                    _str = ""
2367
            else:
2368
                if _str[0:6] == "\x00\x00\x00\x00\x00\x00":
2369
                    _str = ""
2370
                elif _str[0:6] == "\x20\x20\x20\x20\x20\x20":
2371
                    _str = ""
2372
                elif (len(_str) == 2) & (_str[0:2] == "PP"):
2373
                    _str = ""
2374
            rs = RadioSetting("scanname" + x, "Scan Name " + x,
2375
                              RadioSettingValueString(0, 8, _str))
2376
            scanname_grp.append(rs)
2377

    
2378
            val = getattr(_settings, 'scanlower%i' % i)
2379
            rs = RadioSetting("scanlower%i" % i, "Scan Lower %i" % i,
2380
                              RadioSettingValueInteger(1, 999, val))
2381
            scan_grp.append(rs)
2382
            val = getattr(_settings, 'scanupper%i' % i)
2383
            rs = RadioSetting("scanupper%i" % i, "Scan Upper %i" % i,
2384
                              RadioSettingValueInteger(1, 999, val))
2385
            scan_grp.append(rs)
2386
# remote settings
2387
        rs = RadioSetting("rc_power", "RC Power",
2388
                          RadioSettingValueList(
2389
                           RC_POWER_LIST,
2390
                           RC_POWER_LIST[_settings.rc_power]))
2391
        remote_grp.append(rs)
2392

    
2393
        def decode_remote_vals(self, setting):
2394
            # parse the id value and replace all C with empty
2395
            # C indicates the end of the id value
2396
            code = ('%06X' % int(setting)).replace('C', '')
2397
            return int(code), code
2398

    
2399
        def apply_remote_id(setting, obj, val):
2400
            val = str(val)
2401
            value = int(val.ljust(3, '0').ljust(6, 'C'), 16)
2402
            setattr(obj, setting.get_name(), value)
2403

    
2404
        val_ani, code_val = decode_remote_vals(self, _settings.ani_edit)
2405
        LOG.debug("ani = %s" % val_ani)
2406
        val_ani = RadioSettingValueString(3, 6, code_val, False)
2407
        val_ani.set_charset(dtmfchars)
2408
        rs = RadioSetting("settings.ani_edit", "ANI Edit", val_ani)
2409

    
2410
        def apply_ani_id(setting, obj):
2411
            LOG.debug("val= %s" % val_ani)
2412
            if str(val_ani)[0] == "0":
2413
                raise errors.RadioError("ANI EDIT must start with \
2414
                                        Non-Zero Digit")
2415
            val = str(val_ani)
2416
            value = int(val.ljust(3, '0').ljust(6, 'C'), 16)
2417
            obj.ani_edit = value
2418
        rs.set_apply_callback(apply_ani_id, _settings)
2419
        remote_grp.append(rs)
2420

    
2421
        val_mcc, code_val = decode_remote_vals(self, _settings.mcc_edit)
2422
        LOG.debug("mcc = %s" % val_mcc)
2423
        val_mcc = RadioSettingValueString(3, 6, code_val, False)
2424
        val_mcc.set_charset(dtmfchars)
2425
        rs = RadioSetting("mcc_edit", "MCC Edit", val_mcc)
2426

    
2427
        rs.set_apply_callback(apply_remote_id, _settings, val_mcc)
2428
        remote_grp.append(rs)
2429

    
2430
        val_scc, code_val = decode_remote_vals(self, _settings.scc_edit)
2431
        LOG.debug("scc = %s" % val_scc)
2432
        val_scc = RadioSettingValueString(3, 6, code_val, False)
2433
        val_scc.set_charset(dtmfchars)
2434
        rs = RadioSetting("scc_edit", "SCC Edit", val_scc)
2435

    
2436
        rs.set_apply_callback(apply_remote_id, _settings, val_scc)
2437
        remote_grp.append(rs)
2438

    
2439
        val_ctrl, code_val = decode_remote_vals(self, _settings.ctrl_edit)
2440
        LOG.debug("ctrl = %s" % val_ctrl)
2441
        val_ctrl = RadioSettingValueString(3, 6, code_val, False)
2442
        val_ctrl.set_charset(dtmfchars)
2443
        rs = RadioSetting("ctrl_edit", "CTRL Edit", val_ctrl)
2444

    
2445
        rs.set_apply_callback(apply_remote_id, _settings, val_ctrl)
2446
        remote_grp.append(rs)
2447

    
2448
        # OEM Settings
2449
        if self.MODEL != "KG-1000G Plus":
2450
            _oem_name = _oem_str_decode(self._memobj.oem.display_name)
2451
        else:
2452
            displayname = self._memobj.oem.display_name
2453
            _oem_name = _oem_str_decode_1000GPLUS(displayname)
2454

    
2455
        rs = RadioSetting("oem.display_name", "Area Message",
2456
                          RadioSettingValueString(1, 8, _oem_name))
2457
        oem_grp.append(rs)
2458

    
2459
        # FM RADIO PRESETS
2460

    
2461
        # memory stores raw integer value like 7600
2462
        # radio will divide 7600 by 100 and interpret correctly at 76.0 MHz
2463

    
2464
        for i in range(1, 21):
2465
            # chan = str(i)
2466
            fmname = "FM_radio%i" % i
2467
            fmlabel = "FM Preset %i" % i
2468
            fmvalue = getattr(_settings, fmname)
2469
            # some CPS versions store values with .01 MHz in error
2470
            # eg 99.5 MHz is stored as 0x26df = 9951 dec = 99.51 MHz
2471
            # even though the radio properly displays 99.5
2472
            # this will drop the 0.01 MHz for Chirp Displayed values
2473
            fmvalue = fmvalue // 10 / 10
2474
            rs = RadioSetting(fmname, fmlabel,
2475
                              RadioSettingValueFloat(76.0, 108.0,
2476
                                                     fmvalue,
2477
                                                     0.1, 1))
2478
            fmradio_grp.append(rs)
2479

    
2480
        return group
2481

    
2482
    def get_settings(self):
2483
        try:
2484
            return self._get_settings()
2485
        except Exception:
2486
            LOG.exception("Failed to parse settings: %s")
2487
            return None
2488

    
2489
    def set_settings(self, settings):
2490
        for element in settings:
2491
            if not isinstance(element, RadioSetting):
2492
                self.set_settings(element)
2493
                continue
2494
            else:
2495
                try:
2496
                    if "." in element.get_name():
2497
                        bits = element.get_name().split(".")
2498
                        obj = self._memobj
2499
                        for bit in bits[:-1]:
2500
                            obj = getattr(obj, bit)
2501
                        setting = bits[-1]
2502
                    else:
2503
                        obj = self._memobj.settings
2504
                        setting = element.get_name()
2505

    
2506
                    if element.has_apply_callback():
2507
                        LOG.debug("Using apply callback")
2508
                        element.run_apply_callback()
2509
                    else:
2510
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2511
                        if self._is_freq(element):
2512
                            #  rescale freq values to match radio
2513
                            # expected values
2514
                            value = _freq_encode(
2515
                                element[0].get_value()*1000000.0)
2516
                            setattr(obj, setting, value)
2517

    
2518
                        elif self._is_fmradio_or_voltage(element):
2519
                            #  rescale FM Radio values to match radio
2520
                            # expected values
2521
                            setattr(obj, setting,
2522
                                    int(element.values()[0]._current * 100.0))
2523

    
2524
                        elif self._is_limit(element):
2525
                            setattr(obj, setting,
2526
                                    int(element[0].get_value()) * 10)
2527

    
2528
                        # Special VFO A Settings
2529
                        #
2530
                        elif self._is_chan(element):
2531
                            value = _chnum_encode(element[0].get_value())
2532
                            setattr(obj, setting, value)
2533
                            continue
2534
                        elif self._is_display_name(element):
2535
                            string = element[0].get_value()
2536
                            if self.MODEL != "KG-1000G Plus":
2537
                                nameenc = _oem_str_encode(string)
2538
                            else:
2539
                                nameenc = _oem_str_encode_1000GPLUS(string)
2540
                            for i in range(0, 8):
2541
                                LOG.debug("nameenc %s" % (nameenc[i]))
2542
                                self._memobj.oem.display_name[i] = \
2543
                                    ord(nameenc[i])
2544

    
2545
                        elif self._is_scan_name(element):
2546
                            string = element[0].get_value()
2547
                            LOG.debug("string %s" % (string))
2548
                            value = _str_encode(string)
2549
                            LOG.debug("scaname %s" % (value))
2550
                            setattr(obj, setting, value.encode())
2551
                        else:
2552
                            setattr(obj, setting, element.value)
2553
                except Exception:
2554
                    LOG.debug(element.get_name())
2555
                    raise
2556

    
2557
    def _is_freq(self, element):
2558
        return ("rxfreq" in element.get_name() or
2559
                "txoffset" in element.get_name() or
2560
                "vfofreq" in element.get_name() or
2561
                "vfoofst" in element.get_name())
2562

    
2563
    def _is_limit(self, element):
2564
        return "limit" in element.get_name()
2565

    
2566
    def _is_fmradio_or_voltage(self, element):
2567
        return ("FM_radio" in element.get_name() or
2568
                "thr_vol_lvl" in element.get_name())
2569

    
2570
    def _is_chan(self, element):
2571
        return ("vfochan" in element.get_name() or
2572
                "pri_ch" in element.get_name())
2573

    
2574
    def _is_display_name(self, element):
2575
        return "display_name" in element.get_name()
2576

    
2577
    def _is_scan_name(self, element):
2578
        return "scanname" in element.get_name()
2579

    
2580

    
2581
@directory.register
2582
class KG1000GRadio(KG980PRadio):
2583

    
2584
    # """Wouxun KG-1000G"""
2585
    VENDOR = "Wouxun"
2586
    MODEL = "KG-1000G"
2587

    
2588

    
2589
@directory.register
2590
class KG1000GPlusRadio(KG980PRadio):
2591

    
2592
    # """Wouxun KG-1000G Plus"""
2593
    VENDOR = "Wouxun"
2594
    MODEL = "KG-1000G Plus"
    (1-1/1)