Project

General

Profile

Bug #11180 ยป f4hwn.py

Jocelyn Maheu, 02/17/2024 07:53 PM

 
1
# Quansheng UV-K5 driver (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
2
# Adapted For UV-K5 EGZUMER custom software By EGZUMER, JOC2
3
# Re-Adapted For UV-K5 EGZUMER/F4HWN custom software By JOC2
4
#
5
# based on template.py Copyright 2012 Dan Smith <dsmith@danplanet.com>
6
#
7
#
8
# This is a preliminary version of a driver for the UV-K5
9
# It is based on my reverse engineering effort described here:
10
# https://github.com/sq5bpf/uvk5-reverse-engineering
11
#
12
# Warning: this driver is experimental, it may brick your radio,
13
# eat your lunch and mess up your configuration.
14
#
15
#
16
# This program is free software: you can redistribute it and/or modify
17
# it under the terms of the GNU General Public License as published by
18
# the Free Software Foundation, either version 2 of the License, or
19
# (at your option) any later version.
20
#
21
# This program is distributed in the hope that it will be useful,
22
# but WITHOUT ANY WARRANTY; without even the implied warranty of
23
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
# GNU General Public License for more details.
25
#
26
# You should have received a copy of the GNU General Public License
27
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
28

    
29
# change/ modification
30
#
31
# 2024-02-09 :section password has been commented, because the new chirp give error with int.
32
#
33

    
34
import struct
35
import logging
36
import wx
37

    
38
from chirp import chirp_common, directory, bitwise, memmap, errors, util
39
from chirp.settings import RadioSetting, RadioSettingGroup, \
40
    RadioSettingValueBoolean, RadioSettingValueList, \
41
    RadioSettingValueInteger, RadioSettingValueString, \
42
    RadioSettings, InvalidValueError
43

    
44
LOG = logging.getLogger(__name__)
45

    
46
# Show the obfuscated version of commands. Not needed normally, but
47
# might be useful for someone who is debugging a similar radio
48
DEBUG_SHOW_OBFUSCATED_COMMANDS = False
49

    
50
# Show the memory being written/received. Not needed normally, because
51
# this is the same information as in the packet hexdumps, but
52
# might be useful for someone debugging some obscure memory issue
53
DEBUG_SHOW_MEMORY_ACTIONS = False
54

    
55
# TODO: remove the driver version when it's in mainline chirp 
56
DRIVER_VERSION = "Quansheng UV-K5/K6/5R driver ver: 2024/02/17 (c) EGZUMER + F4HWN v1.8.3"
57
FIRMWARE_DRIVER_VERSION_UPDATE = "https://github.com/armel/uv-k5-firmware-custom-feat-F4HWN"
58
CHIRP_DRIVER_VERSION_UPDATE = "https://github.com/armel/uv-k5-chirp-driver"
59
 
60
VALEUR_COMPILER = "ENABLE"
61

    
62
MEM_FORMAT = """
63
//#seekto 0x0000;
64
struct {
65
  ul32 freq;
66
  ul32 offset;
67

    
68
// 0x08
69
  u8 rxcode;
70
  u8 txcode;
71

    
72
// 0x0A
73
  u8 txcodeflag:4,
74
  rxcodeflag:4;
75

    
76
// 0x0B
77
  u8 modulation:4,
78
  offsetDir:4;
79

    
80
// 0x0C
81
  u8 __UNUSED1:3,
82
  busyChLockout:1,
83
  txpower:2,
84
  bandwidth:1,
85
  freq_reverse:1;
86

    
87
  // 0x0D
88
  u8 __UNUSED2:4,
89
  dtmf_pttid:3,
90
  dtmf_decode:1;
91

    
92
  // 0x0E
93
  u8 step;
94
  u8 scrambler;
95

    
96
} channel[214];
97

    
98
//#seekto 0xd60;
99
struct {
100
u8 is_scanlist1:1,
101
is_scanlist2:1,
102
compander:2,
103
is_free:1,
104
band:3;
105
} ch_attr[207];
106

    
107
#seekto 0xe40;
108
ul16 fmfreq[20];
109

    
110
#seekto 0xe70;
111
u8 call_channel;
112
u8 squelch;
113
u8 max_talk_time;
114
u8 noaa_autoscan;
115
u8 key_lock;
116
u8 vox_switch;
117
u8 vox_level;
118
u8 mic_gain;
119

    
120

    
121
u8 backlight_min:4,
122
backlight_max:4;
123

    
124
u8 channel_display_mode;
125
u8 crossband;
126
u8 battery_save;
127
u8 dual_watch;
128
u8 backlight_time;
129
u8 ste;
130
u8 freq_mode_allowed;
131

    
132
#seekto 0xe80;
133
u8 ScreenChannel_A;
134
u8 MrChannel_A;
135
u8 FreqChannel_A;
136
u8 ScreenChannel_B;
137
u8 MrChannel_B;
138
u8 FreqChannel_B;
139
u8 NoaaChannel_A;
140
u8 NoaaChannel_B;
141

    
142
#seekto 0xe90;
143

    
144
u8 keyM_longpress_action:7,
145
    button_beep:1;
146

    
147
u8 key1_shortpress_action;
148
u8 key1_longpress_action;
149
u8 key2_shortpress_action;
150
u8 key2_longpress_action;
151
u8 scan_resume_mode;
152
u8 auto_keypad_lock;
153
u8 power_on_dispmode;
154
ul32 password;
155

    
156
#seekto 0xea0;
157
u8 voice;
158
u8 s0_level;
159
u8 s9_level;
160

    
161
#seekto 0xea8;
162
u8 alarm_mode;
163
u8 roger_beep;
164
u8 rp_ste;
165
u8 TX_VFO;
166
u8 Battery_type;
167

    
168
#seekto 0xeb0;
169
char logo_line1[16];
170
char logo_line2[16];
171

    
172
//#seekto 0xed0;
173
struct {
174
    u8 side_tone;
175
    char separate_code;
176
    char group_call_code;
177
    u8 decode_response;
178
    u8 auto_reset_time;
179
    u8 preload_time;
180
    u8 first_code_persist_time;
181
    u8 hash_persist_time;
182
    u8 code_persist_time;
183
    u8 code_interval_time;
184
    u8 permit_remote_kill;
185

    
186
    #seekto 0xee0;
187
    char local_code[3];
188
    #seek 5;
189
    char kill_code[5];
190
    #seek 3;
191
    char revive_code[5];
192
    #seek 3;
193
    char up_code[16];
194
    char down_code[16];
195
} dtmf;
196

    
197
//#seekto 0xf18;
198
u8 slDef;
199
u8 sl1PriorEnab;
200
u8 sl1PriorCh1;
201
u8 sl1PriorCh2;
202
u8 sl2PriorEnab;
203
u8 sl2PriorCh1;
204
u8 sl2PriorCh2;
205

    
206
#seekto 0xf40;
207
u8 int_flock;
208
u8 int_350tx;
209
u8 int_KILLED;
210
u8 int_200tx;
211
u8 int_500tx;
212
u8 int_350en;
213
u8 int_scren;
214

    
215

    
216
u8  backlight_on_TX_RX:2,
217
    AM_fix:1,
218
    mic_bar:1,
219
    battery_text:2,
220
    live_DTMF_decoder:1,
221
    unknown:1;
222

    
223

    
224
#seekto 0xf50;
225
struct {
226
char name[16];
227
} channelname[200];
228

    
229
#seekto 0x1c00;
230
struct {
231
char name[8];
232
char number[3];
233
#seek 5;
234
} dtmfcontact[16];
235

    
236
struct {
237
    struct {
238
        #seekto 0x1E00;
239
        u8 openRssiThr[10];
240
        #seekto 0x1E10;
241
        u8 closeRssiThr[10];
242
        #seekto 0x1E20;
243
        u8 openNoiseThr[10];
244
        #seekto 0x1E30;
245
        u8 closeNoiseThr[10];
246
        #seekto 0x1E40;
247
        u8 closeGlitchThr[10];
248
        #seekto 0x1E50;
249
        u8 openGlitchThr[10];
250
    } sqlBand4_7;
251

    
252
    struct {
253
        #seekto 0x1E60;
254
        u8 openRssiThr[10];
255
        #seekto 0x1E70;
256
        u8 closeRssiThr[10];
257
        #seekto 0x1E80;
258
        u8 openNoiseThr[10];
259
        #seekto 0x1E90;
260
        u8 closeNoiseThr[10];
261
        #seekto 0x1EA0;
262
        u8 closeGlitchThr[10];
263
        #seekto 0x1EB0;
264
        u8 openGlitchThr[10];
265
    } sqlBand1_3;
266

    
267
    #seekto 0x1EC0;
268
    struct {
269
        ul16 level1;
270
        ul16 level2;
271
        ul16 level4;
272
        ul16 level6;
273
    } rssiLevelsBands3_7;
274

    
275
    struct {
276
        ul16 level1;
277
        ul16 level2;
278
        ul16 level4;
279
        ul16 level6;
280
    } rssiLevelsBands1_2;
281

    
282
    struct {
283
        struct {
284
            u8 lower;
285
            u8 center;
286
            u8 upper;
287
        } low;
288
        struct {
289
            u8 lower;
290
            u8 center;
291
            u8 upper;
292
        } mid;
293
        struct {
294
            u8 lower;
295
            u8 center;
296
            u8 upper;
297
        } hi;
298
        #seek 7;
299
    } txp[7];
300

    
301
    #seekto 0x1F40;
302
    ul16 batLvl[6];
303

    
304
    #seekto 0x1F50;
305
    ul16 vox1Thr[10];
306

    
307
    #seekto 0x1F68;
308
    ul16 vox0Thr[10];
309

    
310
    #seekto 0x1F80;
311
    u8 micLevel[5];
312

    
313
    #seekto 0x1F88;
314
    il16 xtalFreqLow;
315

    
316
    #seekto 0x1F8E;
317
    u8 volumeGain;
318
    u8 dacGain;
319
} cal;
320

    
321

    
322
#seekto 0x1FF0;
323
struct {
324
u8 ENABLE_DTMF_CALLING:1,
325
   ENABLE_PWRON_PASSWORD:1,
326
   ENABLE_TX1750:1,
327
   ENABLE_ALARM:1,
328
   ENABLE_VOX:1,
329
   ENABLE_VOICE:1,
330
   ENABLE_NOAA:1,
331
   ENABLE_FMRADIO:1;
332
u8 __UNUSED:3,
333
   ENABLE_AM_FIX:1,
334
   ENABLE_BLMIN_TMP_OFF:1,
335
   ENABLE_RAW_DEMODULATORS:1,
336
   ENABLE_WIDE_RX:1,
337
   ENABLE_FLASHLIGHT:1;
338
} BUILD_OPTIONS;
339

    
340
#seekto 0x1FF2;
341
u8 eeprom0x1ff2;
342
u8 eeprom0x1ff3;
343
u8 eeprom0x1ff4;
344

    
345
u8 set_futur:1,
346
set_met:1,
347
set_lck:1,
348
set_inv:1,
349
set_contrast:4;
350

    
351
u8 set_tot:4,
352
set_eot:4;
353

    
354
u8 set_low:4,
355
set_ptt:4;
356

    
357
u8 eeprom0x1ff8;
358
u8 eeprom0x1ff9;
359
u8 eeprom0x1ffa;
360
u8 eeprom0x1ffb;
361
u8 eeprom0x1ffc;
362
u8 eeprom0x1ffd;
363
u8 eeprom0x1ffe;
364
u8 eeprom0x1fff;
365
"""
366

    
367

    
368
# flags1
369
FLAGS1_OFFSET_NONE = 0b00
370
FLAGS1_OFFSET_MINUS = 0b10
371
FLAGS1_OFFSET_PLUS = 0b01
372

    
373
POWER_HIGH = 0b10
374
POWER_MEDIUM = 0b01
375
POWER_LOW = 0b00
376

    
377
# SET_LOW_POWER f4hwn
378
SET_LOW_LIST = ["125mW", "250mW", "500mW", "1W", "< 20mW"]
379

    
380
# SET_PTT f4hwn
381
SET_PTT_LIST = ["CLASSIC", "ONEPUSH"]
382

    
383
# SET_TOT and SET_EOT f4hwn
384
SET_TOT_EOT_LIST = ["OFF", "SOUND", "VISUAL", "ALL"]
385

    
386
# SET_OFF_ON f4hwn
387
SET_OFF_ON_LIST = ["OFF", "ON"]
388

    
389
# SET_lck f4hwn
390
SET_LCK_LIST = ["KEYS", "KEYS+PTT"]
391

    
392
# SET_MET f4hwn
393
SET_MET_LIST = ["TINY", "CLASSIC"]
394

    
395

    
396
# dtmf_flags
397
PTTID_LIST = ["OFF", "UP CODE", "DOWN CODE", "UP+DOWN CODE", "APOLLO QUINDAR"]
398

    
399
# power
400
UVK5_POWER_LEVELS = [chirp_common.PowerLevel("Low",  watts=1.50),
401
                     chirp_common.PowerLevel("Med",  watts=3.00),
402
                     chirp_common.PowerLevel("High", watts=5.00),
403
                     ]
404

    
405
# scrambler
406
SCRAMBLER_LIST = ["OFF", "2600Hz", "2700Hz", "2800Hz", "2900Hz", "3000Hz",
407
                  "3100Hz", "3200Hz", "3300Hz", "3400Hz", "3500Hz"]
408
# compander
409
COMPANDER_LIST = ["OFF", "TX", "RX", "TX/RX"]
410

    
411
# rx mode
412
RXMODE_LIST = ["MAIN ONLY", "DUAL RX RESPOND", "CROSS BAND", "MAIN TX DUAL RX"]
413

    
414
# channel display mode
415
CHANNELDISP_LIST = ["Frequency (FREQ)", "CHANNEL NUMBER", "NAME", "Name + Frequency (NAME + FREQ)"]
416

    
417
# TalkTime
418
TALK_TIME_LIST = ["30 sec", "1 min", "2 min", "3 min", "4 min", "5 min",
419
                  "6 min", "7 min", "8 min", "9 min", "15 min"]
420
# Auto Keypad Lock
421
AUTO_KEYPAD_LOCK_LIST = ["OFF", "AUTO"]
422

    
423
# battery save
424
BATSAVE_LIST = ["OFF", "1:1", "1:2", "1:3", "1:4"]
425

    
426
# battery type
427
BATTYPE_LIST = ["1600 mAh", "2200 mAh"]
428
# bat txt
429
BAT_TXT_LIST = ["NONE", "VOLTAGE", "PERCENT"]
430
# Backlight auto mode
431
BACKLIGHT_LIST = ["OFF", "5 sec", "10 sec", "20 sec", "1 min", "2 min", "4 min",
432
                  "Always On (ON)"]
433

    
434
# Backlight LVL
435
BACKLIGHT_LVL_LIST = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
436

    
437
# Backlight _TX_RX_LIST
438
BACKLIGHT_TX_RX_LIST = ["OFF", "TX", "RX", "TX/RX"]
439

    
440
# steps TODO: change order
441
STEPS = [2.5, 5, 6.25, 10, 12.5, 25, 8.33, 0.01, 0.05, 0.1, 0.25, 0.5, 1, 1.25,
442
         9, 15, 20, 30, 50, 100, 125, 200, 250, 500]
443

    
444
# ctcss/dcs codes
445
TMODES = ["", "Tone", "DTCS", "DTCS"]
446
TONE_NONE = 0
447
TONE_CTCSS = 1
448
TONE_DCS = 2
449
TONE_RDCS = 3
450

    
451

    
452
CTCSS_TONES = [
453
    67.0, 69.3, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4,
454
    88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2, 110.9,
455
    114.8, 118.8, 123.0, 127.3, 131.8, 136.5, 141.3, 146.2,
456
    151.4, 156.7, 159.8, 162.2, 165.5, 167.9, 171.3, 173.8,
457
    177.3, 179.9, 183.5, 186.2, 189.9, 192.8, 196.6, 199.5,
458
    203.5, 206.5, 210.7, 218.1, 225.7, 229.1, 233.6, 241.8,
459
    250.3, 254.1
460
]
461

    
462
# lifted from ft4.py
463
DTCS_CODES = [  # TODO: add negative codes
464
    23,  25,  26,  31,  32,  36,  43,  47,  51,  53,  54,
465
    65,  71,  72,  73,  74,  114, 115, 116, 122, 125, 131,
466
    132, 134, 143, 145, 152, 155, 156, 162, 165, 172, 174,
467
    205, 212, 223, 225, 226, 243, 244, 245, 246, 251, 252,
468
    255, 261, 263, 265, 266, 271, 274, 306, 311, 315, 325,
469
    331, 332, 343, 346, 351, 356, 364, 365, 371, 411, 412,
470
    413, 423, 431, 432, 445, 446, 452, 454, 455, 462, 464,
471
    465, 466, 503, 506, 516, 523, 526, 532, 546, 565, 606,
472
    612, 624, 627, 631, 632, 654, 662, 664, 703, 712, 723,
473
    731, 732, 734, 743, 754
474
]
475

    
476
# flock list extended
477
FLOCK_LIST = ["DEFAULT+ (137-174, 400-470 + Tx200, Tx350, Tx500)",
478
              "FCC HAM (144-148, 420-450)",
479
              "CE HAM (144-146, 430-440)",
480
              "GB HAM (144-148, 430-440)",
481
              "137-174, 400-430",
482
              "137-174, 400-438",
483
              "Disable All",
484
              "Unlock All"]
485

    
486
SCANRESUME_LIST = ["Listen 5 seconds and resume (TIMEOUT)",
487
                   "Listen until signal disapears (CARRIER)",
488
                   "Stop scanning after receiving a signal (STOP)"]
489
WELCOME_LIST = ["Full screen test (FULL)", "User message (MESSAGE)", "Battery voltage (VOLTAGE)", "NONE"]
490
VOICE_LIST = ["OFF", "Chinese", "English"]
491

    
492
# ACTIVE CHANNEL
493
TX_VFO_LIST = ["A", "B"]
494
ALARMMODE_LIST = ["SITE", "TONE"]
495
ROGER_LIST = ["OFF", "Roger beep (ROGER)", "MDC data burst (MDC)"]
496
RTE_LIST = ["OFF", "100ms", "200ms", "300ms", "400ms",
497
            "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms"]
498
VOX_LIST = ["OFF", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
499

    
500
MEM_SIZE = 0x2000  # size of all memory
501
PROG_SIZE = 0x1d00  # size of the memory that we will write
502
MEM_BLOCK = 0x80  # largest block of memory that we can reliably write
503
CAL_START = 0x1E00  # calibration memory start address
504
F4HWN_START =0x1FF2 # calibration F4HWN memory start address
505

    
506
# fm radio supported frequencies
507
FMMIN = 76.0
508
FMMAX = 108.0
509

    
510
# bands supported by the UV-K5
511
BANDS_STANDARD = {
512
        0: [50.0, 76.0],
513
        1: [108.0, 136.9999],
514
        2: [137.0, 173.9999],
515
        3: [174.0, 349.9999],
516
        4: [350.0, 399.9999],
517
        5: [400.0, 469.9999],
518
        6: [470.0, 600.0]
519
        }
520

    
521
BANDS_WIDE = {
522
        0: [18.0, 108.0],
523
        1: [108.0, 136.9999],
524
        2: [137.0, 173.9999],
525
        3: [174.0, 349.9999],
526
        4: [350.0, 399.9999],
527
        5: [400.0, 469.9999],
528
        6: [470.0, 1300.0]
529
        }
530

    
531
SCANLIST_LIST = ["None", "List1", "List2", "Both"]
532
SCANLIST_SELECT_LIST = ["LIST 1", "LIST 2", "All Channel (ALL)"]
533

    
534
DTMF_CHARS = "0123456789ABCD*# "
535
DTMF_CHARS_ID = "0123456789ABCDabcd"
536
DTMF_CHARS_KILL = "0123456789ABCDabcd"
537
DTMF_CHARS_UPDOWN = "0123456789ABCDabcd#* "
538
DTMF_CODE_CHARS = "ABCD*# "
539
DTMF_DECODE_RESPONSE_LIST = ["DO NOTHING", "Local ringing (RING)", "Replay response (REPLY)",
540
                             "Local ringing + reply response (BOTH)"]
541

    
542
KEYACTIONS_LIST = ["NONE",
543
                   "FLASHLIGHT",
544
                   "POWER",
545
                   "MONITOR",
546
                   "SCAN",
547
                   "VOX",
548
                   "ALARM",
549
                   "FM RADIO",
550
                   "1750Hz TONE",
551
                   "LOCK KEYPAD",
552
                   "Switch main VFO (SWITCH VFO)",
553
                   "Switch frequency/memory mode (VFO/MR)",
554
                   "Switch demodulation (SWITCH DEMODUL)",
555
                   "BKL_MIN_N/U",
556
                   "Change RxMode: *Main only,*Dual RX,*Cross Band,*TX Dual RX (SWITCH RX MODE)",
557
                   "SWITCH PTT",                   
558
                   "SWITCH WIDE NARROW"
559
                  ]
560

    
561
MIC_GAIN_LIST = ["+1.1dB", "+4.0dB", "+8.0dB", "+12.0dB", "+15.1dB"]
562

    
563

    
564
def xorarr(data: bytes):
565
    """the communication is obfuscated using this fine mechanism"""
566
    tbl = [22, 108, 20, 230, 46, 145, 13, 64, 33, 53, 213, 64, 19, 3, 233, 128]
567
    ret = b""
568
    idx = 0
569
    for byte in data:
570
        ret += bytes([byte ^ tbl[idx]])
571
        idx = (idx+1) % len(tbl)
572
    return ret
573

    
574

    
575
def calculate_crc16_xmodem(data: bytes):
576
    """
577
    if this crc was used for communication to AND from the radio, then it
578
    would be a measure to increase reliability.
579
    but it's only used towards the radio, so it's for further obfuscation
580
    """
581
    poly = 0x1021
582
    crc = 0x0
583
    for byte in data:
584
        crc = crc ^ (byte << 8)
585
        for _ in range(8):
586
            crc = crc << 1
587
            if crc & 0x10000:
588
                crc = (crc ^ poly) & 0xFFFF
589
    return crc & 0xFFFF
590

    
591

    
592
def _send_command(serport, data: bytes):
593
    """Send a command to UV-K5 radio"""
594
    LOG.debug("Sending command (unobfuscated) len=0x%4.4x:\n%s",
595
              len(data), util.hexprint(data))
596

    
597
    crc = calculate_crc16_xmodem(data)
598
    data2 = data + struct.pack("<H", crc)
599

    
600
    command = struct.pack(">HBB", 0xabcd, len(data), 0) + \
601
        xorarr(data2) + \
602
        struct.pack(">H", 0xdcba)
603
    if DEBUG_SHOW_OBFUSCATED_COMMANDS:
604
        LOG.debug("Sending command (obfuscated):\n%s", util.hexprint(command))
605
    try:
606
        result = serport.write(command)
607
    except Exception as e:
608
        raise errors.RadioError("Error writing data to radio") from e
609
    return result
610

    
611

    
612
def _receive_reply(serport):
613
    header = serport.read(4)
614
    if len(header) != 4:
615
        LOG.warning("Header short read: [%s] len=%i",
616
                    util.hexprint(header), len(header))
617
        raise errors.RadioError("Header short read")
618
    if header[0] != 0xAB or header[1] != 0xCD or header[3] != 0x00:
619
        LOG.warning("Bad response header: %s len=%i",
620
                    util.hexprint(header), len(header))
621
        raise errors.RadioError("Bad response header")
622

    
623
    cmd = serport.read(int(header[2]))
624
    if len(cmd) != int(header[2]):
625
        LOG.warning("Body short read: [%s] len=%i",
626
                    util.hexprint(cmd), len(cmd))
627
        raise errors.RadioError("Command body short read")
628

    
629
    footer = serport.read(4)
630

    
631
    if len(footer) != 4:
632
        LOG.warning("Footer short read: [%s] len=%i",
633
                    util.hexprint(footer), len(footer))
634
        raise errors.RadioError("Footer short read")
635

    
636
    if footer[2] != 0xDC or footer[3] != 0xBA:
637
        LOG.debug("Reply before bad response footer (obfuscated)"
638
                  "len=0x%4.4x:\n%s", len(cmd), util.hexprint(cmd))
639
        LOG.warning("Bad response footer: %s len=%i",
640
                    util.hexprint(footer), len(footer))
641
        raise errors.RadioError("Bad response footer")
642

    
643
    if DEBUG_SHOW_OBFUSCATED_COMMANDS:
644
        LOG.debug("Received reply (obfuscated) len=0x%4.4x:\n%s",
645
                  len(cmd), util.hexprint(cmd))
646

    
647
    cmd2 = xorarr(cmd)
648

    
649
    LOG.debug("Received reply (unobfuscated) len=0x%4.4x:\n%s",
650
              len(cmd2), util.hexprint(cmd2))
651

    
652
    return cmd2
653

    
654

    
655
def _getstring(data: bytes, begin, maxlen):
656
    tmplen = min(maxlen+1, len(data))
657
    ss = [data[i] for i in range(begin, tmplen)]
658
    key = 0
659
    for key, val in enumerate(ss):
660
        if val < ord(' ') or val > ord('~'):
661
            return ''.join(chr(x) for x in ss[0:key])
662
    return ''
663

    
664

    
665
def _sayhello(serport):
666
    hellopacket = b"\x14\x05\x04\x00\x6a\x39\x57\x64"
667

    
668
    tries = 5
669
    while True:
670
        LOG.debug("Sending hello packet")
671
        _send_command(serport, hellopacket)
672
        rep = _receive_reply(serport)
673
        if rep:
674
            break
675
        tries -= 1
676
        if tries == 0:
677
            LOG.warning("Failed to initialise radio")
678
            raise errors.RadioError("Failed to initialize radio")
679
    if rep.startswith(b'\x18\x05'):
680
        raise errors.RadioError("Radio is in programming mode, "
681
                                "restart radio into normal mode")
682
    firmware = _getstring(rep, 4, 24)
683

    
684
    LOG.info("Found firmware: %s", firmware)
685
    return firmware
686

    
687

    
688
def _readmem(serport, offset, length):
689
    LOG.debug("Sending readmem offset=0x%4.4x len=0x%4.4x", offset, length)
690

    
691
    readmem = b"\x1b\x05\x08\x00" + \
692
        struct.pack("<HBB", offset, length, 0) + \
693
        b"\x6a\x39\x57\x64"
694
    _send_command(serport, readmem)
695
    rep = _receive_reply(serport)
696
    if DEBUG_SHOW_MEMORY_ACTIONS:
697
        LOG.debug("readmem Received data len=0x%4.4x:\n%s",
698
                  len(rep), util.hexprint(rep))
699
    return rep[8:]
700

    
701

    
702
def _writemem(serport, data, offset):
703
    LOG.debug("Sending writemem offset=0x%4.4x len=0x%4.4x",
704
              offset, len(data))
705

    
706
    if DEBUG_SHOW_MEMORY_ACTIONS:
707
        LOG.debug("writemem sent data offset=0x%4.4x len=0x%4.4x:\n%s",
708
                  offset, len(data), util.hexprint(data))
709

    
710
    dlen = len(data)
711
    writemem = b"\x1d\x05" + \
712
        struct.pack("<BBHBB", dlen+8, 0, offset, dlen, 1) + \
713
        b"\x6a\x39\x57\x64"+data
714

    
715
    _send_command(serport, writemem)
716
    rep = _receive_reply(serport)
717

    
718
    LOG.debug("writemem Received data: %s len=%i",
719
              util.hexprint(rep), len(rep))
720

    
721
    if (rep[0] == 0x1e and
722
       rep[4] == (offset & 0xff) and
723
       rep[5] == (offset >> 8) & 0xff):
724
        return True
725

    
726
    LOG.warning("Bad data from writemem")
727
    raise errors.RadioError("Bad response to writemem")
728

    
729

    
730
def _resetradio(serport):
731
    resetpacket = b"\xdd\x05\x00\x00"
732
    _send_command(serport, resetpacket)
733

    
734

    
735
def do_download(radio):
736
    """download eeprom from radio"""
737
    serport = radio.pipe
738
    serport.timeout = 0.5
739
    status = chirp_common.Status()
740
    status.cur = 0
741
    status.max = MEM_SIZE
742
    status.msg = "Downloading from radio"
743
    radio.status_fn(status)
744

    
745
    eeprom = b""
746
    f = _sayhello(serport)
747
    if f:
748
        radio.FIRMWARE_VERSION = f
749
    else:
750
        raise errors.RadioError("Failed to initialize radio")
751

    
752
    addr = 0
753
    while addr < MEM_SIZE:
754
        data = _readmem(serport, addr, MEM_BLOCK)
755
        status.cur = addr
756
        radio.status_fn(status)
757

    
758
        if data and len(data) == MEM_BLOCK:
759
            eeprom += data
760
            addr += MEM_BLOCK
761
        else:
762
            raise errors.RadioError("Memory download incomplete")
763

    
764
    return memmap.MemoryMapBytes(eeprom)
765

    
766

    
767
def do_upload(radio):
768
    """upload configuration to radio eeprom"""
769
    serport = radio.pipe
770
    serport.timeout = 0.5
771
    status = chirp_common.Status()
772
    status.cur = 0
773
    status.msg = "Uploading to radio"
774

    
775
    if radio.upload_f4hwn:
776
        status.max = MEM_SIZE-F4HWN_START
777
        start_addr = F4HWN_START
778
        stop_addr = MEM_SIZE
779
    
780
    else:    
781
        if radio.upload_calibration:
782
            status.max = F4HWN_START-CAL_START
783
            start_addr = CAL_START
784
            stop_addr = F4HWN_START
785
        
786
        else:
787
            status.max = PROG_SIZE
788
            start_addr = 0
789
            stop_addr = PROG_SIZE
790

    
791
    radio.status_fn(status)
792

    
793
    f = _sayhello(serport)
794
    if f:
795
        radio.FIRMWARE_VERSION = f
796
    else:
797
        return False
798

    
799
    addr = start_addr
800
    while addr < stop_addr:
801
        dat = radio.get_mmap()[addr:addr+MEM_BLOCK]
802
        _writemem(serport, dat, addr)
803
        status.cur = addr - start_addr
804
        radio.status_fn(status)
805
        if dat:
806
            addr += MEM_BLOCK
807
        else:
808
            raise errors.RadioError("Memory upload incomplete")
809
    status.msg = "Uploaded OK"
810

    
811
    _resetradio(serport)
812

    
813
    return True
814

    
815

    
816
def min_max_def(value, min_val, max_val, default):
817
    """returns value if in bounds or default otherwise"""
818
    if min_val is not None and value < min_val:
819
        return default
820
    if max_val is not None and value > max_val:
821
        return default
822
    return value
823

    
824

    
825
def list_def(value, lst, default):
826
    """return value if is in the list, default otherwise"""
827
    if isinstance(default, str):
828
        default = lst.index(default)
829
    if value < 0 or value >= len(lst):
830
        return default
831
    return value
832

    
833

    
834
@directory.register
835
class UVK5RadioEgzumer(chirp_common.CloneModeRadio):
836
    """Quansheng UV-K5 (egzumer + f4hwn)"""
837
    VENDOR = "Quansheng"
838
    MODEL = "UV-K5 (egzumer + f4hwn)"
839
    BAUD_RATE = 38400
840
    NEEDS_COMPAT_SERIAL = False
841
    FIRMWARE_VERSION = ""
842

    
843
#    upload_calibration = False
844
    upload_f4hwn = False
845

    
846
    def _get_bands(self):
847
        is_wide = self._memobj.BUILD_OPTIONS.ENABLE_WIDE_RX \
848
            if self._memobj is not None else True
849
        bands = BANDS_WIDE if is_wide else BANDS_STANDARD
850
        return bands
851

    
852
    def _find_band(self, hz):
853
        mhz = hz/1000000.0
854
        bands = self._get_bands()
855
        for bnd, rng in bands.items():
856
            if rng[0] <= mhz <= rng[1]:
857
                return bnd
858
        return False
859

    
860
    def _get_vfo_channel_names(self):
861
        """generates VFO_CHANNEL_NAMES"""
862
        bands = self._get_bands()
863
        names = []
864
        for bnd, rng in bands.items():
865
            name = f"F{bnd + 1}({round(rng[0])}M-{round(rng[1])}M)"
866
            names.append(name + "A")
867
            names.append(name + "B")
868
        return names
869

    
870
    def _get_specials(self):
871
        """generates SPECIALS"""
872
        specials = {}
873
        for idx, name in enumerate(self._get_vfo_channel_names()):
874
            specials[name] = 200 + idx
875
        return specials
876

    
877
    @classmethod
878
    def get_prompts(cls):
879
        rp = chirp_common.RadioPrompts()
880
        rp.experimental = \
881
            'This is an experimental driver for the Quansheng UV-K5. ' \
882
            'It may harm your radio, or worse. Use at your own risk.\n\n' \
883
            'Before attempting to do any changes please download' \
884
            'the memory image from the radio with chirp ' \
885
            'and keep it. This can be later used to recover the ' \
886
            'original settings. \n\n' \
887
            'some details are not yet implemented'
888
        rp.pre_download = \
889
            "1. Turn radio on.\n" \
890
            "2. Connect cable to mic/spkr connector.\n" \
891
            "3. Make sure connector is firmly connected.\n" \
892
            "4. Click OK to download image from device.\n\n" \
893
            "It may not work if you turn on the radio " \
894
            "with the cable already attached\n"
895
        rp.pre_upload = \
896
            "1. Turn radio on.\n" \
897
            "2. Connect cable to mic/spkr connector.\n" \
898
            "3. Make sure connector is firmly connected.\n" \
899
            "4. Click OK to upload the image to device.\n\n" \
900
            "It may not work if you turn on the radio " \
901
            "with the cable already attached"
902
        return rp
903

    
904
    # Return information about this radio's features, including
905
    # how many memories it has, what bands it supports, etc
906
    def get_features(self):
907
        rf = chirp_common.RadioFeatures()
908
        rf.has_bank = False
909
        rf.valid_dtcs_codes = DTCS_CODES
910
        rf.has_rx_dtcs = True
911
        rf.has_ctone = True
912
        rf.has_settings = True
913
        rf.has_comment = False
914
        rf.valid_name_length = 10
915
        rf.valid_power_levels = UVK5_POWER_LEVELS
916
        rf.valid_special_chans = self._get_vfo_channel_names()
917
        rf.valid_duplexes = ["", "-", "+", "off"]
918

    
919
        steps = STEPS.copy()
920
        steps.sort()
921
        rf.valid_tuning_steps = steps
922

    
923
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
924
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
925
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
926

    
927
        rf.valid_characters = chirp_common.CHARSET_ASCII
928
        rf.valid_modes = ["FM", "NFM", "AM", "NAM", "USB"]
929

    
930
        rf.valid_skips = [""]
931

    
932
        # This radio supports memories 1-200, 201-214 are the VFO memories
933
        rf.memory_bounds = (1, 200)
934

    
935
        # This is what the BK4819 chip supports
936
        # Will leave it in a comment, might be useful someday
937
        # rf.valid_bands = [(18000000,  620000000),
938
        #                  (840000000, 1300000000)
939
        #                  ]
940
        rf.valid_bands = []
941
        bands = self._get_bands()
942
        for _, rng in bands.items():
943
            rf.valid_bands.append(
944
                    (int(rng[0]*1000000), int(rng[1]*1000000)))
945
        return rf
946

    
947
    # Do a download of the radio from the serial port
948
    def sync_in(self):
949
        self._mmap = do_download(self)
950
        self.process_mmap()
951

    
952
    # Do an upload of the radio to the serial port
953
    def sync_out(self):
954
        do_upload(self)
955

    
956
    # Convert the raw byte array into a memory object structure
957
    def process_mmap(self):
958
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
959

    
960
    # Return a raw representation of the memory object, which
961
    # is very helpful for development
962
    def get_raw_memory(self, number):
963
        return repr(self._memobj.channel[number-1])
964

    
965
    def validate_memory(self, mem):
966
        msgs = super().validate_memory(mem)
967

    
968
        if mem.duplex == 'off':
969
            return msgs
970

    
971
        # find tx frequency
972
        if mem.duplex == '-':
973
            txfreq = mem.freq - mem.offset
974
        elif mem.duplex == '+':
975
            txfreq = mem.freq + mem.offset
976
        else:
977
            txfreq = mem.freq
978

    
979
        # find band
980
        band = self._find_band(txfreq)
981
        if band is False:
982
            msg = f"Transmit frequency {txfreq/1000000.0:.4f}MHz " \
983
                   "is not supported by this radio"
984
            msgs.append(chirp_common.ValidationWarning(msg))
985

    
986
        band = self._find_band(mem.freq)
987
        if band is False:
988
            msg = f"The frequency {mem.freq/1000000.0:%.4f}MHz " \
989
                   "is not supported by this radio"
990
            msgs.append(chirp_common.ValidationWarning(msg))
991

    
992
        return msgs
993

    
994
    def _set_tone(self, mem, _mem):
995
        ((txmode, txtone, txpol),
996
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
997

    
998
        if txmode == "Tone":
999
            txtoval = CTCSS_TONES.index(txtone)
1000
            txmoval = 0b01
1001
        elif txmode == "DTCS":
1002
            txmoval = txpol == "R" and 0b11 or 0b10
1003
            txtoval = DTCS_CODES.index(txtone)
1004
        else:
1005
            txmoval = 0
1006
            txtoval = 0
1007

    
1008
        if rxmode == "Tone":
1009
            rxtoval = CTCSS_TONES.index(rxtone)
1010
            rxmoval = 0b01
1011
        elif rxmode == "DTCS":
1012
            rxmoval = rxpol == "R" and 0b11 or 0b10
1013
            rxtoval = DTCS_CODES.index(rxtone)
1014
        else:
1015
            rxmoval = 0
1016
            rxtoval = 0
1017

    
1018
        _mem.rxcodeflag = rxmoval
1019
        _mem.txcodeflag = txmoval
1020
        _mem.rxcode = rxtoval
1021
        _mem.txcode = txtoval
1022

    
1023
    def _get_tone(self, mem, _mem):
1024
        rxtype = _mem.rxcodeflag
1025
        txtype = _mem.txcodeflag
1026
        rx_tmode = TMODES[rxtype]
1027
        tx_tmode = TMODES[txtype]
1028

    
1029
        rx_tone = tx_tone = None
1030

    
1031
        if tx_tmode == "Tone":
1032
            if _mem.txcode < len(CTCSS_TONES):
1033
                tx_tone = CTCSS_TONES[_mem.txcode]
1034
            else:
1035
                tx_tone = 0
1036
                tx_tmode = ""
1037
        elif tx_tmode == "DTCS":
1038
            if _mem.txcode < len(DTCS_CODES):
1039
                tx_tone = DTCS_CODES[_mem.txcode]
1040
            else:
1041
                tx_tone = 0
1042
                tx_tmode = ""
1043

    
1044
        if rx_tmode == "Tone":
1045
            if _mem.rxcode < len(CTCSS_TONES):
1046
                rx_tone = CTCSS_TONES[_mem.rxcode]
1047
            else:
1048
                rx_tone = 0
1049
                rx_tmode = ""
1050
        elif rx_tmode == "DTCS":
1051
            if _mem.rxcode < len(DTCS_CODES):
1052
                rx_tone = DTCS_CODES[_mem.rxcode]
1053
            else:
1054
                rx_tone = 0
1055
                rx_tmode = ""
1056

    
1057
        tx_pol = txtype == 0x03 and "R" or "N"
1058
        rx_pol = rxtype == 0x03 and "R" or "N"
1059

    
1060
        chirp_common.split_tone_decode(mem, (tx_tmode, tx_tone, tx_pol),
1061
                                       (rx_tmode, rx_tone, rx_pol))
1062

    
1063
    # Extract a high-level memory object from the low-level memory map
1064
    # This is called to populate a memory in the UI
1065
    def get_memory(self, number):
1066

    
1067
        mem = chirp_common.Memory()
1068

    
1069
        if isinstance(number, str):
1070
            ch_num = self._get_specials()[number]
1071
            mem.extd_number = number
1072
        else:
1073
            ch_num = number - 1
1074

    
1075
        mem.number = ch_num + 1
1076

    
1077
        _mem = self._memobj.channel[ch_num]
1078

    
1079
        is_empty = False
1080
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
1081
        if (_mem.freq == 0xffffffff) or (_mem.freq == 0):
1082
            is_empty = True
1083

    
1084
        # We'll also look at the channel attributes if a memory has them
1085
        tmpscn = 0
1086
        tmp_comp = 0
1087
        if ch_num < 200:
1088
            _mem3 = self._memobj.ch_attr[ch_num]
1089
            # free memory bit
1090
            is_empty |= _mem3.is_free
1091
            # scanlists
1092
            tmpscn = _mem3.is_scanlist1 + _mem3.is_scanlist2 * 2
1093
            tmp_comp = list_def(_mem3.compander, COMPANDER_LIST, 0)
1094
        elif ch_num < 214:
1095
            att_num = 200 + int((ch_num - 200) / 2)
1096
            _mem3 = self._memobj.ch_attr[att_num]
1097
            is_empty |= _mem3.is_free
1098
            tmp_comp = list_def(_mem3.compander, COMPANDER_LIST, 0)
1099

    
1100
        if is_empty:
1101
            mem.empty = True
1102
            # set some sane defaults:
1103
            mem.power = UVK5_POWER_LEVELS[2]
1104
            mem.extra = RadioSettingGroup("Extra", "extra")
1105

    
1106
            val = RadioSettingValueBoolean(False)
1107
            rs = RadioSetting("busyChLockout", "BusyCL", val)
1108
            mem.extra.append(rs)
1109

    
1110
            val = RadioSettingValueBoolean(False)
1111
            rs = RadioSetting("frev", "FreqRev", val)
1112
            mem.extra.append(rs)
1113

    
1114
            val = RadioSettingValueList(PTTID_LIST)
1115
            rs = RadioSetting("pttid", "PTTID", val)
1116
            mem.extra.append(rs)
1117

    
1118
            val = RadioSettingValueBoolean(False)
1119
            rs = RadioSetting("dtmfdecode", "DTMF decode", val)
1120
            if self._memobj.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
1121
                mem.extra.append(rs)
1122

    
1123
            val = RadioSettingValueList(SCRAMBLER_LIST)
1124
            rs = RadioSetting("scrambler", "Scrambler", val)
1125
            mem.extra.append(rs)
1126

    
1127
            val = RadioSettingValueList(COMPANDER_LIST)
1128
            rs = RadioSetting("compander", "Compander", val)
1129
            mem.extra.append(rs)
1130

    
1131
            val = RadioSettingValueList(SCANLIST_LIST)
1132
            rs = RadioSetting("scanlists", "Scanlists", val)
1133
            mem.extra.append(rs)
1134

    
1135
            # actually the step and duplex are overwritten by chirp based on
1136
            # bandplan. they are here to document sane defaults for IARU r1
1137
            # mem.tuning_step = 25.0
1138
            # mem.duplex = "off"
1139

    
1140
            return mem
1141

    
1142
        if ch_num > 199:
1143
            mem.name = self._get_vfo_channel_names()[ch_num-200]
1144
            mem.immutable = ["name", "scanlists"]
1145
        else:
1146
            _mem2 = self._memobj.channelname[ch_num]
1147
            for char in _mem2.name:
1148
                if str(char) == "\xFF" or str(char) == "\x00":
1149
                    break
1150
                mem.name += str(char)
1151
            mem.name = mem.name.rstrip()
1152

    
1153
        # Convert your low-level frequency to Hertz
1154
        mem.freq = int(_mem.freq)*10
1155
        mem.offset = int(_mem.offset)*10
1156

    
1157
        if mem.offset == 0:
1158
            mem.duplex = ''
1159
        else:
1160
            if _mem.offsetDir == FLAGS1_OFFSET_MINUS:
1161
                if _mem.freq == _mem.offset:
1162
                    # fake tx disable by setting tx to 0 MHz
1163
                    mem.duplex = 'off'
1164
                    mem.offset = 0
1165
                else:
1166
                    mem.duplex = '-'
1167
            elif _mem.offsetDir == FLAGS1_OFFSET_PLUS:
1168
                mem.duplex = '+'
1169
            else:
1170
                mem.duplex = ''
1171

    
1172
        # tone data
1173
        self._get_tone(mem, _mem)
1174

    
1175
        # mode
1176
        temp_modes = self.get_features().valid_modes
1177
        temp_modul = _mem.modulation*2 + _mem.bandwidth
1178
        if temp_modul < len(temp_modes):
1179
            mem.mode = temp_modes[temp_modul]
1180
        elif temp_modul == 5:  # USB with narrow setting
1181
            mem.mode = temp_modes[4]
1182
        elif temp_modul >= len(temp_modes):
1183
            mem.mode = "UNSUPPORTED BY CHIRP"
1184

    
1185
        # tuning step
1186
        tstep = _mem.step
1187
        if tstep < len(STEPS):
1188
            mem.tuning_step = STEPS[tstep]
1189
        else:
1190
            mem.tuning_step = 2.5
1191

    
1192
        # power
1193
        if _mem.txpower == POWER_HIGH:
1194
            mem.power = UVK5_POWER_LEVELS[2]
1195
        elif _mem.txpower == POWER_MEDIUM:
1196
            mem.power = UVK5_POWER_LEVELS[1]
1197
        else:
1198
            mem.power = UVK5_POWER_LEVELS[0]
1199

    
1200
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
1201
        if (_mem.freq == 0xffffffff) or (_mem.freq == 0):
1202
            mem.empty = True
1203
        else:
1204
            mem.empty = False
1205

    
1206
        mem.extra = RadioSettingGroup("Extra", "extra")
1207

    
1208
        # BusyCL
1209
        val = RadioSettingValueBoolean(_mem.busyChLockout)
1210
        rs = RadioSetting("busyChLockout", "Busy Ch Lockout    (BusyCL)", val)
1211
        mem.extra.append(rs)
1212

    
1213
        # Frequency reverse
1214
        val = RadioSettingValueBoolean(_mem.freq_reverse)
1215
        rs = RadioSetting("frev", "Reverse Frequencies (R)", val)
1216
        mem.extra.append(rs)
1217

    
1218
        # PTTID
1219
        pttid = list_def(_mem.dtmf_pttid, PTTID_LIST, 0)
1220
        val = RadioSettingValueList(PTTID_LIST, None, pttid)
1221
        rs = RadioSetting("pttid", "PTT ID (PTT ID)", val)
1222
        mem.extra.append(rs)
1223

    
1224
        # DTMF DECODE
1225
        val = RadioSettingValueBoolean(_mem.dtmf_decode)
1226
        rs = RadioSetting("dtmfdecode", "DTMF decode (D Decd)", val)
1227
        if self._memobj.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
1228
            mem.extra.append(rs)
1229

    
1230
        # Scrambler
1231
        enc = list_def(_mem.scrambler, SCRAMBLER_LIST, 0)
1232
        val = RadioSettingValueList(SCRAMBLER_LIST, None, enc)
1233
        rs = RadioSetting("scrambler", "Scrambler (Scramb)", val)
1234
        mem.extra.append(rs)
1235

    
1236
        # Compander
1237
        val = RadioSettingValueList(COMPANDER_LIST, None, tmp_comp)
1238
        rs = RadioSetting("compander", "Compander (Compnd)", val)
1239
        mem.extra.append(rs)
1240

    
1241
        val = RadioSettingValueList(SCANLIST_LIST, None, tmpscn)
1242
        rs = RadioSetting("scanlists", "Scanlists (SList)", val)
1243
        mem.extra.append(rs)
1244

    
1245
        return mem
1246

    
1247
    def set_settings(self, settings):
1248
        _mem = self._memobj
1249
        for element in settings:
1250
            if not isinstance(element, RadioSetting):
1251
                self.set_settings(element)
1252
                continue
1253

    
1254
            elname = element.get_name()
1255

    
1256
            # basic settings
1257

    
1258
            # VFO_A e80 ScreenChannel_A
1259
            if elname == "VFO_A_chn":
1260
                _mem.ScreenChannel_A = int(element.value)
1261
                if _mem.ScreenChannel_A < 200:
1262
                    _mem.MrChannel_A = _mem.ScreenChannel_A
1263
                elif _mem.ScreenChannel_A < 207:
1264
                    _mem.FreqChannel_A = _mem.ScreenChannel_A
1265
                else:
1266
                    _mem.NoaaChannel_A = _mem.ScreenChannel_A
1267

    
1268
            # VFO_B e83
1269
            elif elname == "VFO_B_chn":
1270
                _mem.ScreenChannel_B = int(element.value)
1271
                if _mem.ScreenChannel_B < 200:
1272
                    _mem.MrChannel_B = _mem.ScreenChannel_B
1273
                elif _mem.ScreenChannel_B < 207:
1274
                    _mem.FreqChannel_B = _mem.ScreenChannel_B
1275
                else:
1276
                    _mem.NoaaChannel_B = _mem.ScreenChannel_B
1277

    
1278
            # TX_VFO  channel selected A,B
1279
            elif elname == "TX_VFO":
1280
                _mem.TX_VFO = int(element.value)
1281

    
1282
            # call channel
1283
            elif elname == "call_channel":
1284
                _mem.call_channel = int(element.value)
1285

    
1286
            # squelch
1287
            elif elname == "squelch":
1288
                _mem.squelch = int(element.value)
1289

    
1290
            # TOT
1291
            elif elname == "tot":
1292
                _mem.max_talk_time = int(element.value)
1293

    
1294
            # NOAA autoscan
1295
            elif elname == "noaa_autoscan":
1296
                _mem.noaa_autoscan = int(element.value)
1297

    
1298
            # VOX
1299
            elif elname == "vox":
1300
                voxvalue = int(element.value)
1301
                _mem.vox_switch = voxvalue > 0
1302
                _mem.vox_level = (voxvalue - 1) if _mem.vox_switch else 0
1303

    
1304
            # mic gain
1305
            elif elname == "mic_gain":
1306
                _mem.mic_gain = int(element.value)
1307

    
1308
            # Channel display mode
1309
            elif elname == "channel_display_mode":
1310
                _mem.channel_display_mode = int(element.value)
1311

    
1312
            # RX Mode
1313
            elif elname == "rx_mode":
1314
                tmptxmode = int(element.value)
1315
                tmpmainvfo = _mem.TX_VFO + 1
1316
                _mem.crossband = tmpmainvfo * bool(tmptxmode & 0b10)
1317
                _mem.dual_watch = tmpmainvfo * bool(tmptxmode & 0b01)
1318

    
1319
            # Battery Save
1320
            elif elname == "battery_save":
1321
                _mem.battery_save = int(element.value)
1322

    
1323
            # Backlight auto mode
1324
            elif elname == "backlight_time":
1325
                _mem.backlight_time = int(element.value)
1326

    
1327
            # Backlight min
1328
            elif elname == "backlight_min":
1329
                _mem.backlight_min = int(element.value)
1330

    
1331
            # Backlight max
1332
            elif elname == "backlight_max":
1333
                _mem.backlight_max = int(element.value)
1334

    
1335
            # Backlight TX_RX
1336
            elif elname == "backlight_on_TX_RX":
1337
                _mem.backlight_on_TX_RX = int(element.value)
1338
            # AM_fix
1339
            elif elname == "AM_fix":
1340
                _mem.AM_fix = int(element.value)
1341

    
1342
            # mic_bar
1343
            elif elname == "mem.mic_bar":
1344
                _mem.mic_bar = int(element.value)
1345

    
1346
            # Batterie txt
1347
            elif elname == "_mem.battery_text":
1348
                _mem.battery_text = int(element.value)
1349

    
1350
            # Tail tone elimination
1351
            elif elname == "ste":
1352
                _mem.ste = int(element.value)
1353

    
1354
            # VFO Open
1355
            elif elname == "freq_mode_allowed":
1356
                _mem.freq_mode_allowed = int(element.value)
1357

    
1358
            # Beep control
1359
            elif elname == "button_beep":
1360
                _mem.button_beep = int(element.value)
1361

    
1362
            # Scan resume mode
1363
            elif elname == "scan_resume_mode":
1364
                _mem.scan_resume_mode = int(element.value)
1365

    
1366
            # Keypad lock
1367
            elif elname == "key_lock":
1368
                _mem.key_lock = int(element.value)
1369

    
1370
            # Auto keypad lock
1371
            elif elname == "auto_keypad_lock":
1372
                _mem.auto_keypad_lock = int(element.value)
1373

    
1374
            # Power on display mode
1375
            elif elname == "welcome_mode":
1376
                _mem.power_on_dispmode = int(element.value)
1377

    
1378
            # Keypad Tone
1379
            elif elname == "voice":
1380
                _mem.voice = int(element.value)
1381

    
1382
            elif elname == "s0_level":
1383
                _mem.s0_level = -int(element.value)
1384

    
1385
            elif elname == "s9_level":
1386
                _mem.s9_level = -int(element.value)
1387

    
1388
#            elif elname == "password":
1389
#                if element.value.get_value() is None or element.value == "":
1390
#                    _mem.password = 0xFFFFFFFF
1391
#                else:
1392
#                    _mem.password = int(element.value)
1393

    
1394
            # Alarm mode
1395
            elif elname == "alarm_mode":
1396
                _mem.alarm_mode = int(element.value)
1397

    
1398
            # Reminding of end of talk
1399
            elif elname == "roger_beep":
1400
                _mem.roger_beep = int(element.value)
1401

    
1402
            # Repeater tail tone elimination
1403
            elif elname == "rp_ste":
1404
                _mem.rp_ste = int(element.value)
1405

    
1406
            # Logo string 1
1407
            elif elname == "logo1":
1408
                bts = str(element.value).rstrip("\x20\xff\x00")+"\x00"*12
1409
                _mem.logo_line1 = bts[0:12]+"\x00\xff\xff\xff"
1410

    
1411
            # Logo string 2
1412
            elif elname == "logo2":
1413
                bts = str(element.value).rstrip("\x20\xff\x00")+"\x00"*12
1414
                _mem.logo_line2 = bts[0:12]+"\x00\xff\xff\xff"
1415

    
1416
            # unlock settings
1417

    
1418
            # FLOCK
1419
            elif elname == "int_flock":
1420
                _mem.int_flock = int(element.value)
1421

    
1422
            # 350TX
1423
            elif elname == "int_350tx":
1424
                _mem.int_350tx = int(element.value)
1425

    
1426
            # KILLED
1427
            elif elname == "int_KILLED":
1428
                _mem.int_KILLED = int(element.value)
1429

    
1430
            # 200TX
1431
            elif elname == "int_200tx":
1432
                _mem.int_200tx = int(element.value)
1433

    
1434
            # 500TX
1435
            elif elname == "int_500tx":
1436
                _mem.int_500tx = int(element.value)
1437

    
1438
            # 350EN
1439
            elif elname == "int_350en":
1440
                _mem.int_350en = int(element.value)
1441

    
1442
            # SCREN
1443
            elif elname == "int_scren":
1444
                _mem.int_scren = int(element.value)
1445

    
1446
            # battery type
1447
            elif elname == "Battery_type":
1448
                _mem.Battery_type = int(element.value)
1449

    
1450
            # set low_power f4hwn
1451
            elif elname == "set_low":
1452
                _mem.set_low = int(element.value)
1453

    
1454
            # set ptt f4hwn
1455
            elif elname == "set_ptt":
1456
                _mem.set_ptt = int(element.value)
1457

    
1458
            # set tot f4hwn
1459
            elif elname == "set_tot":
1460
                _mem.set_tot = int(element.value)
1461

    
1462
            # set eot f4hwn
1463
            elif elname == "set_eot":
1464
                _mem.set_eot = int(element.value)
1465

    
1466
            # set_contrast f4hwn
1467
            elif elname == "set_contrast":
1468
                _mem.set_contrast = int(element.value)
1469

    
1470
            # set inv f4hwn
1471
            elif elname == "set_inv":
1472
                _mem.set_inv = int(element.value)
1473

    
1474
            # set lck f4hwn
1475
            elif elname == "set_lck":
1476
                _mem.set_lck = int(element.value)
1477

    
1478
            # set met f4hwn
1479
            elif elname == "set_met":
1480
                _mem.set_met = int(element.value)
1481
                               
1482
            # fm radio
1483
            for i in range(1, 21):
1484
                freqname = "FM_" + str(i)
1485
                if elname == freqname:
1486
                    val = str(element.value).strip()
1487
                    try:
1488
                        val2 = int(float(val)*10)
1489
                    except Exception:
1490
                        val2 = 0xffff
1491

    
1492
                    if val2 < FMMIN*10 or val2 > FMMAX*10:
1493
                        val2 = 0xffff
1494
#                        raise errors.InvalidValueError(
1495
#                                "FM radio frequency should be a value "
1496
#                                "in the range %.1f - %.1f" % (FMMIN , FMMAX))
1497
                    _mem.fmfreq[i-1] = val2
1498

    
1499
            # dtmf settings
1500
            if elname == "dtmf_side_tone":
1501
                _mem.dtmf.side_tone = int(element.value)
1502

    
1503
            elif elname == "dtmf_separate_code":
1504
                _mem.dtmf.separate_code = str(element.value)
1505

    
1506
            elif elname == "dtmf_group_call_code":
1507
                _mem.dtmf.group_call_code = element.value
1508

    
1509
            elif elname == "dtmf_decode_response":
1510
                _mem.dtmf.decode_response = int(element.value)
1511

    
1512
            elif elname == "dtmf_auto_reset_time":
1513
                _mem.dtmf.auto_reset_time = int(element.value)
1514

    
1515
            elif elname == "dtmf_preload_time":
1516
                _mem.dtmf.preload_time = int(int(element.value)/10)
1517

    
1518
            elif elname == "dtmf_first_code_persist_time":
1519
                _mem.dtmf.first_code_persist_time = int(int(element.value)/10)
1520

    
1521
            elif elname == "dtmf_hash_persist_time":
1522
                _mem.dtmf.hash_persist_time = int(int(element.value)/10)
1523

    
1524
            elif elname == "dtmf_code_persist_time":
1525
                _mem.dtmf.code_persist_time = \
1526
                        int(int(element.value)/10)
1527

    
1528
            elif elname == "dtmf_code_interval_time":
1529
                _mem.dtmf.code_interval_time = \
1530
                        int(int(element.value)/10)
1531

    
1532
            elif elname == "dtmf_permit_remote_kill":
1533
                _mem.dtmf.permit_remote_kill = \
1534
                        int(element.value)
1535

    
1536
            elif elname == "dtmf_dtmf_local_code":
1537
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*3
1538
                _mem.dtmf.local_code = k[0:3]
1539

    
1540
            elif elname == "dtmf_dtmf_up_code":
1541
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*16
1542
                _mem.dtmf.up_code = k[0:16]
1543

    
1544
            elif elname == "dtmf_dtmf_down_code":
1545
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*16
1546
                _mem.dtmf.down_code = k[0:16]
1547

    
1548
            elif elname == "dtmf_kill_code":
1549
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1550
                _mem.dtmf.kill_code = k[0:5]
1551

    
1552
            elif elname == "dtmf_revive_code":
1553
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1554
                _mem.dtmf.revive_code = k[0:5]
1555

    
1556
            elif elname == "live_DTMF_decoder":
1557
                _mem.live_DTMF_decoder = int(element.value)
1558

    
1559
            # dtmf contacts
1560
            for i in range(1, 17):
1561
                varname = "DTMF_" + str(i)
1562
                if elname == varname:
1563
                    k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8
1564
                    _mem.dtmfcontact[i-1].name = k[0:8]
1565

    
1566
                varnumname = "DTMFNUM_" + str(i)
1567
                if elname == varnumname:
1568
                    k = str(element.value).rstrip("\x20\xff\x00") + "\xff"*3
1569
                    _mem.dtmfcontact[i-1].number = k[0:3]
1570

    
1571
            # scanlist stuff
1572
            if elname == "slDef":
1573
                _mem.slDef = int(element.value)
1574

    
1575
            elif elname == "sl1PriorEnab":
1576
                _mem.sl1PriorEnab = int(element.value)
1577

    
1578
            elif elname == "sl2PriorEnab":
1579
                _mem.sl2PriorEnab = int(element.value)
1580

    
1581
            elif elname in ["sl1PriorCh1", "sl1PriorCh2", "sl2PriorCh1",
1582
                            "sl2PriorCh2"]:
1583
                val = int(element.value)
1584

    
1585
                if val > 200 or val < 1:
1586
                    val = 0xff
1587
                else:
1588
                    val -= 1
1589

    
1590
                _mem[elname] = val
1591

    
1592
            if elname == "key1_shortpress_action":
1593
                _mem.key1_shortpress_action = KEYACTIONS_LIST.index(element.value)
1594

    
1595
            elif elname == "key1_longpress_action":
1596
                _mem.key1_longpress_action = KEYACTIONS_LIST.index(element.value)
1597

    
1598
            elif elname == "key2_shortpress_action":
1599
                _mem.key2_shortpress_action = KEYACTIONS_LIST.index(element.value)
1600

    
1601
            elif elname == "key2_longpress_action":
1602
                _mem.key2_longpress_action = KEYACTIONS_LIST.index(element.value)
1603

    
1604
            elif elname == "keyM_longpress_action":
1605
                _mem.keyM_longpress_action = KEYACTIONS_LIST.index(element.value)
1606

    
1607
            elif elname == "upload_calibration":
1608
                self._upload_calibration = bool(element.value)
1609

    
1610
            elif element.changed() and elname.startswith("_mem.cal."):
1611
                exec(elname + " = element.value.get_value()")
1612

    
1613
    def get_settings(self):
1614
        _mem = self._memobj
1615
        basic = RadioSettingGroup("basic", "Basic Settings")
1616
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1617
        keya = RadioSettingGroup("keya", "Programmable Keys")
1618
        dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
1619
        dtmfc = RadioSettingGroup("dtmfc", "DTMF Contacts")
1620
        scanl = RadioSettingGroup("scn", "Scan Lists")
1621
        unlock = RadioSettingGroup("unlock", "Unlock Settings")
1622
        fmradio = RadioSettingGroup("fmradio", "FM Radio")
1623
        calibration = RadioSettingGroup("calibration", "Calibration")
1624

    
1625
        roinfo = RadioSettingGroup("roinfo", "Driver Information + Link to get latest driver F4HWN")
1626
        top = RadioSettings()
1627
        top.append(basic)
1628
        top.append(advanced)
1629
        top.append(keya)
1630
        top.append(dtmf)
1631
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
1632
            top.append(dtmfc)
1633
        top.append(scanl)
1634
        top.append(unlock)
1635
        if _mem.BUILD_OPTIONS.ENABLE_FMRADIO:
1636
            top.append(fmradio)
1637
        top.append(roinfo)
1638
        top.append(calibration)
1639

    
1640
        # helper function
1641
        def append_label(radio_setting, label, descr=""):
1642
            if not hasattr(append_label, 'idx'):
1643
                append_label.idx = 0
1644

    
1645
            val = RadioSettingValueString(len(descr), len(descr), descr)
1646
            val.set_mutable(False)
1647
            rs = RadioSetting("label" + str(append_label.idx), label, val)
1648
            append_label.idx += 1
1649
            radio_setting.append(rs)
1650

    
1651
        # Programmable keys
1652
        def get_action(action_num):
1653
            """"get actual key action"""
1654
            has_alarm = self._memobj.BUILD_OPTIONS.ENABLE_ALARM
1655
            has1750 = self._memobj.BUILD_OPTIONS.ENABLE_TX1750
1656
            has_flashlight = self._memobj.BUILD_OPTIONS.ENABLE_FLASHLIGHT
1657
            lst = KEYACTIONS_LIST.copy()
1658
            if not has_alarm:
1659
                lst.remove("ALARM")
1660
            if not has1750:
1661
                lst.remove("1750Hz TONE")
1662
            if not has_flashlight:
1663
                lst.remove("FLASHLIGHT")
1664
            lst.remove("BKL_MIN_N/U") # F4HWN remove this
1665

    
1666
            action_num = int(action_num)
1667
            if action_num >= len(KEYACTIONS_LIST) or \
1668
               KEYACTIONS_LIST[action_num] not in lst:
1669
                action_num = 0
1670
            return lst, KEYACTIONS_LIST[action_num]
1671

    
1672
        val1s = RadioSettingValueList(*get_action(_mem.key1_shortpress_action))
1673
        rs = RadioSetting("key1_shortpress_action",
1674
                          "Side key 1 short press (F1Shrt)", val1s)
1675
        rs.set_doc('F1Shrt: Select what action do you want to do when press this F1 key for a SHORT time, F1 key is locate on the side, the first key under the PTT')                          
1676
        keya.append(rs)
1677

    
1678
        val1l = RadioSettingValueList(*get_action(_mem.key1_longpress_action))
1679
        rs = RadioSetting("key1_longpress_action",
1680
                          "Side key 1 long press (F1Long)", val1l)
1681
        rs.set_doc('F1Long: Select what action do you want to do when press this F1 key for a LONG time, F1 key is locate on the side, the first key under the PTT')                          
1682
        keya.append(rs)
1683

    
1684
        val2s = RadioSettingValueList(*get_action(_mem.key2_shortpress_action))
1685
        rs = RadioSetting("key2_shortpress_action",
1686
                          "Side key 2 short press (F2Shrt)", val2s)
1687
        rs.set_doc('F2Shrt: Select what action do you want to do when press this F2 key for a SHORT time, F2 key is locate on the side, the second key under the PTT')                          
1688
        keya.append(rs)
1689

    
1690
        val2l = RadioSettingValueList(*get_action(_mem.key2_longpress_action))
1691
        rs = RadioSetting("key2_longpress_action",
1692
                          "Side key 2 long press (F2Long)", val2l)
1693
        rs.set_doc('F2Long: Select what action do you want to do when press this F2 key for a LONG time, F2 key is locate on the side, the second key under the PTT')
1694
        keya.append(rs)
1695

    
1696
        valm = RadioSettingValueList(*get_action(_mem.keyM_longpress_action))
1697
        rs = RadioSetting("keyM_longpress_action",
1698
                          "Menu key long press (M Long)", valm)
1699
        rs.set_doc('M Long: Select what action do you want to do when press this M key for a LONG time, M key is locate under the LCD, on the left side')
1700
        keya.append(rs)
1701

    
1702
        # ----------------- DTMF settings
1703

    
1704
        tmpval = str(_mem.dtmf.separate_code)
1705
        if tmpval not in DTMF_CODE_CHARS:
1706
            tmpval = '*'
1707
        val = RadioSettingValueString(1, 1, tmpval)
1708
        val.set_charset(DTMF_CODE_CHARS)
1709
        sep_code_setting = RadioSetting("dtmf_separate_code",
1710
                                        "Separate Code", val)
1711
        sep_code_setting.set_doc('Separate Code:')
1712

    
1713
        tmpval = str(_mem.dtmf.group_call_code)
1714
        if tmpval not in DTMF_CODE_CHARS:
1715
            tmpval = '#'
1716
        val = RadioSettingValueString(1, 1, tmpval)
1717
        val.set_charset(DTMF_CODE_CHARS)
1718
        group_code_setting = RadioSetting("dtmf_group_call_code",
1719
                                          "Group Call Code", val)
1720
        group_code_setting.set_doc('Group Call Code: ')
1721

    
1722
        tmpval = min_max_def(_mem.dtmf.first_code_persist_time * 10,
1723
                             30, 1000, 300)
1724
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1725
        first_code_per_setting = \
1726
            RadioSetting("dtmf_first_code_persist_time",
1727
                         "First code persist time (ms)", val)
1728
        first_code_per_setting.set_doc('First code persist time: how long to you want the first dtmf will be sent in milisecond')
1729

    
1730
        tmpval = min_max_def(_mem.dtmf.hash_persist_time * 10, 30, 1000, 300)
1731
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1732
        spec_per_setting = RadioSetting("dtmf_hash_persist_time",
1733
                                        "#/* persist time (ms)", val)
1734
        spec_per_setting.set_doc('#/* persist time: how long this code # or / or * will be sent in milisecond')
1735
        
1736
        tmpval = min_max_def(_mem.dtmf.code_persist_time * 10, 30, 1000, 300)
1737
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1738
        code_per_setting = RadioSetting("dtmf_code_persist_time",
1739
                                        "Code persist time (ms)", val)
1740
        code_per_setting.set_doc('Code persist time: how long the code will be sent in milisecond')
1741

    
1742
        tmpval = min_max_def(_mem.dtmf.code_interval_time * 10, 30, 1000, 300)
1743
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1744
        code_int_setting = RadioSetting("dtmf_code_interval_time",
1745
                                        "Code interval time (ms)", val)
1746
        code_int_setting.set_doc('Code interval time: how long to wait between each code sent in milisecond')
1747

    
1748
        tmpval = str(_mem.dtmf.local_code).upper().strip(
1749
                "\x00\xff\x20")
1750
        for i in tmpval:
1751
            if i in DTMF_CHARS_ID:
1752
                continue
1753
            tmpval = "103"
1754
            break
1755
        val = RadioSettingValueString(3, 3, tmpval)
1756
        val.set_charset(DTMF_CHARS_ID)
1757
        ani_id_setting = \
1758
            RadioSetting("dtmf_dtmf_local_code",
1759
                         "Local code (3 chars 0-9 ABCD) (ANI ID)", val)
1760
        ani_id_setting.set_doc('ANI ID: DTMF communication radio ID')                         
1761

    
1762
        tmpval = str(_mem.dtmf.up_code).upper().strip(
1763
                "\x00\xff\x20")
1764
        for i in tmpval:
1765
            if i in DTMF_CHARS_UPDOWN or i == "":
1766
                continue
1767
            else:
1768
                tmpval = "123"
1769
                break
1770
        val = RadioSettingValueString(1, 16, tmpval)
1771
        val.set_charset(DTMF_CHARS_UPDOWN)
1772
        up_code_setting = \
1773
            RadioSetting("dtmf_dtmf_up_code",
1774
                         "Up code (1-16 chars 0-9 ABCD*#) (UPCode)", val)
1775
        up_code_setting.set_doc('UPCode: DTMF code that is sent at the beginning of transmission ')
1776

    
1777
        tmpval = str(_mem.dtmf.down_code).upper().strip(
1778
                "\x00\xff\x20")
1779
        for i in tmpval:
1780
            if i in DTMF_CHARS_UPDOWN:
1781
                continue
1782
            else:
1783
                tmpval = "456"
1784
                break
1785
        val = RadioSettingValueString(1, 16, tmpval)
1786
        val.set_charset(DTMF_CHARS_UPDOWN)
1787
        dw_code_setting = \
1788
            RadioSetting("dtmf_dtmf_down_code",
1789
                         "Down code (1-16 chars 0-9 ABCD*#) (DWCode)", val)
1790
        dw_code_setting.set_doc('DWCode:  DTMF code that is sent at the end of a transmission ')
1791

    
1792
        val = RadioSettingValueBoolean(_mem.dtmf.side_tone)
1793
        dtmf_side_tone_setting = \
1794
            RadioSetting("dtmf_side_tone",
1795
                         "DTMF Sidetone on speaker when sent (D ST)", val)
1796
        dtmf_side_tone_setting.set_doc('D ST: DTMF side tone switch, lets you hear transmitted tones in the radio speaker ')
1797

    
1798
        tmpval = list_def(_mem.dtmf.decode_response,
1799
                          DTMF_DECODE_RESPONSE_LIST, 0)
1800
        val = RadioSettingValueList(DTMF_DECODE_RESPONSE_LIST, None, tmpval)
1801
        dtmf_resp_setting = RadioSetting("dtmf_decode_response",
1802
                                         "Decode Response (D Resp)", val)
1803
        dtmf_resp_setting.set_doc('D Resp: DTMF decoding response ')
1804

    
1805
        tmpval = min_max_def(_mem.dtmf.auto_reset_time, 5, 60, 10)
1806
        val = RadioSettingValueInteger(5, 60, tmpval)
1807
        d_hold_setting = RadioSetting("dtmf_auto_reset_time",
1808
                                      "Auto reset time (s) (D Hold)", val)
1809
        d_hold_setting.set_doc('D Hold: DTMF auto reset time ')
1810

    
1811
        # D Prel
1812
        tmpval = min_max_def(_mem.dtmf.preload_time * 10, 30, 990, 300)
1813
        val = RadioSettingValueInteger(30, 990, tmpval, 10)
1814
        d_prel_setting = RadioSetting("dtmf_preload_time",
1815
                                      "Pre-load time (ms) (D Prel)", val)
1816
        d_prel_setting.set_doc('D Prel: DTMF pre-load time ')
1817
        
1818
        # D LIVE
1819
        val = RadioSettingValueBoolean(_mem.live_DTMF_decoder)
1820
        d_live_setting = \
1821
            RadioSetting("live_DTMF_decoder", "Displays DTMF codes"
1822
                         " received in the middle of the screen (D Live)", val)
1823
        d_live_setting.set_doc('D Live: displays DTMF codes received by radio in the middle of the screen ')
1824
        
1825
        val = RadioSettingValueBoolean(_mem.dtmf.permit_remote_kill)
1826
        perm_kill_setting = RadioSetting("dtmf_permit_remote_kill",
1827
                                         "Permit remote kill", val)
1828

    
1829
        tmpval = str(_mem.dtmf.kill_code).upper().strip(
1830
                "\x00\xff\x20")
1831
        for i in tmpval:
1832
            if i in DTMF_CHARS_KILL:
1833
                continue
1834
            else:
1835
                tmpval = "77777"
1836
                break
1837
        if not len(tmpval) == 5:
1838
            tmpval = "77777"
1839
        val = RadioSettingValueString(5, 5, tmpval)
1840
        val.set_charset(DTMF_CHARS_KILL)
1841
        kill_code_setting = RadioSetting("dtmf_kill_code",
1842
                                         "Kill code (5 chars 0-9 ABCD)", val)
1843

    
1844
        tmpval = str(_mem.dtmf.revive_code).upper().strip(
1845
                "\x00\xff\x20")
1846
        for i in tmpval:
1847
            if i in DTMF_CHARS_KILL:
1848
                continue
1849
            else:
1850
                tmpval = "88888"
1851
                break
1852
        if not len(tmpval) == 5:
1853
            tmpval = "88888"
1854
        val = RadioSettingValueString(5, 5, tmpval)
1855
        val.set_charset(DTMF_CHARS_KILL)
1856
        rev_code_setting = RadioSetting("dtmf_revive_code",
1857
                                        "Revive code (5 chars 0-9 ABCD)", val)
1858

    
1859
        val = RadioSettingValueBoolean(_mem.int_KILLED)
1860
        killed_setting = RadioSetting("int_KILLED", "DTMF kill lock", val)
1861

    
1862
        # ----------------- DTMF Contacts
1863

    
1864
        append_label(dtmfc, "DTMF Contacts  (D List)",
1865
                     "All DTMF Contacts are 3 codes "
1866
                     "(valid: 0-9 * # ABCD), "
1867
                     "or an empty string")
1868

    
1869
        for i in range(1, 17):
1870
            varname = "DTMF_"+str(i)
1871
            varnumname = "DTMFNUM_"+str(i)
1872
            vardescr = "DTMF Contact "+str(i)+" name"
1873
            varinumdescr = "DTMF Contact "+str(i)+" number"
1874

    
1875
            cntn = str(_mem.dtmfcontact[i-1].name).strip("\x20\x00\xff")
1876
            cntnum = str(_mem.dtmfcontact[i-1].number).strip("\x20\x00\xff")
1877

    
1878
            val = RadioSettingValueString(0, 8, cntn)
1879
            rs = RadioSetting(varname, vardescr, val)
1880
            dtmfc.append(rs)
1881

    
1882
            val = RadioSettingValueString(0, 3, cntnum)
1883
            val.set_charset(DTMF_CHARS)
1884
            rs = RadioSetting(varnumname, varinumdescr, val)
1885
            dtmfc.append(rs)
1886

    
1887
        # ----------------- Scan Lists
1888

    
1889
        tmpscanl = list_def(_mem.slDef, SCANLIST_SELECT_LIST, 0)
1890
        val = RadioSettingValueList(SCANLIST_SELECT_LIST, None, tmpscanl)
1891
        rs = RadioSetting("slDef", "Default scanlist (SList)", val)
1892
        rs.set_doc('SList: Selects which channel is used by memory channel scanner ')
1893
        scanl.append(rs)
1894

    
1895
        val = RadioSettingValueBoolean(_mem.sl1PriorEnab)
1896
        rs = RadioSetting("sl1PriorEnab", "List 1 priority channel scan", val)
1897
        rs.set_doc('List 1 priority: is this list as priority ')        
1898
        scanl.append(rs)
1899

    
1900
        ch_list = ["None"]
1901
        for ch in range(1, 201):
1902
            ch_list.append("Channel M" + str(ch))
1903

    
1904
        tmpch = list_def(_mem.sl1PriorCh1 + 1, ch_list, 0)
1905
        val = RadioSettingValueList(ch_list, None, tmpch)
1906
        rs = RadioSetting("sl1PriorCh1", "List 1 priority channel 1", val)
1907
        rs.set_doc('List 1 priority channel 1: select the channel you want for priority ')        
1908
        scanl.append(rs)
1909

    
1910
        tmpch = list_def(_mem.sl1PriorCh2 + 1, ch_list, 0)
1911
        val = RadioSettingValueList(ch_list, None, tmpch)
1912
        rs = RadioSetting("sl1PriorCh2", "List 1 priority channel 2", val)
1913
        rs.set_doc('List 1 priority channel 2: select the channel you want for priority ')
1914
        scanl.append(rs)
1915

    
1916
        val = RadioSettingValueBoolean(_mem.sl2PriorEnab)
1917
        rs = RadioSetting("sl2PriorEnab", "List 2 priority channel scan", val)
1918
        rs.set_doc('List 2 priority: is this list as priority ')
1919
        scanl.append(rs)
1920

    
1921
        tmpch = list_def(_mem.sl2PriorCh1 + 1, ch_list, 0)
1922
        val = RadioSettingValueList(ch_list, None, tmpch)
1923
        rs = RadioSetting("sl2PriorCh1", "List 2 priority channel 1", val)
1924
        rs.set_doc('List 2 priority channel 1: select the channel you want for priority ')
1925
        scanl.append(rs)
1926

    
1927
        tmpch = list_def(_mem.sl2PriorCh2 + 1, ch_list, 0)
1928
        val = RadioSettingValueList(ch_list, None, tmpch)
1929
        rs = RadioSetting("sl2PriorCh2", "List 2 priority channel 2", val)
1930
        rs.set_doc('List 2 priority channel 2: select the channel you want for priority ')
1931
        scanl.append(rs)
1932

    
1933
        # ----------------- Basic settings
1934

    
1935
        ch_list = []
1936
        for ch in range(1, 201):
1937
            ch_list.append("Channel M" + str(ch))
1938
        for bnd in range(1, 8):
1939
            ch_list.append("Band F" + str(bnd))
1940
        if _mem.BUILD_OPTIONS.ENABLE_NOAA:
1941
            for bnd in range(1, 11):
1942
                ch_list.append("NOAA N" + str(bnd))
1943

    
1944
        tmpfreq0 = list_def(_mem.ScreenChannel_A, ch_list, 0)
1945
        val = RadioSettingValueList(ch_list, None, tmpfreq0)
1946
        freq0_setting = RadioSetting("VFO_A_chn",
1947
                                     "VFO A current channel/band", val)
1948
        freq0_setting.set_doc('VFO A current channel/band: To select what is in the VFO A, ' + \
1949
                              '*CHANNEL number 1-200, *BAND F1-F7, look in memory tab to view what ' + \
1950
                              'is program their ')
1951

    
1952
        tmpfreq1 = list_def(_mem.ScreenChannel_B, ch_list, 0)
1953
        val = RadioSettingValueList(ch_list, None, tmpfreq1)
1954
        freq1_setting = RadioSetting("VFO_B_chn",
1955
                                     "VFO B current channel/band", val)
1956
        freq1_setting.set_doc('VFO B current channel/band: To select what is in the VFO B, ' + \
1957
                              '*CHANNEL number 1-200, *BAND F1-F7, look in memory tab to view what ' + \
1958
                              'is program their ')
1959

    
1960
        tmptxvfo = list_def(_mem.TX_VFO, TX_VFO_LIST, 0)
1961
        val = RadioSettingValueList(TX_VFO_LIST, None, tmptxvfo)
1962
        tx_vfo_setting = RadioSetting("TX_VFO", "Main VFO", val)
1963
        tx_vfo_setting.set_doc('Main VFO: To select the frequency that is active in the radio, ' + \
1964
                               '( A is on TOP, B is on the BOTTOM ) ')
1965
        val = RadioSettingValueBoolean(False)
1966

    
1967
        def validate_upload_f4hwn(value):
1968
            return value
1969

    
1970
        val.set_validate_callback(validate_upload_f4hwn)
1971
        Upload_f4hwn = RadioSetting("upload_f4hwn",
1972
                                     "Upload F4HWN setting to radio", val)
1973
        Upload_f4hwn.set_doc('Upload F4HWN: this section of feature is in a different range of memory. ' + \
1974
                             'So it need to be upload to the radio separately, check this case to ' + \
1975
                             'upload ONLY and ONLY all the section of F4HWN feature to the radio. ' + \
1976
                             'It will be realy fast to upload, then the radio will reboot. ' + \
1977
                             'After uploading, uncheck the case to be able to upload all other feature')
1978
        self.upload_f4hwn = val
1979
                                     
1980
        # Set_Low_Power f4hwn
1981
        tmpsetlow = list_def(_mem.set_low, SET_LOW_LIST, 0)
1982
        val = RadioSettingValueList(SET_LOW_LIST, SET_LOW_LIST[tmpsetlow])
1983
        SetLowSetting = RadioSetting("set_low", "Set the power LOW level to specific power (SetLow)", val)
1984
        SetLowSetting.set_doc('SetLow: When the POWER TX is set to power Low ' + \
1985
                              '(see tab memories in colon POWER to know the TX LEVEL for each channel), ' + \
1986
                              'this Low will be at this power TX level ')
1987
        
1988
        # Set_Ptt f4hwn
1989
        tmpsetptt = list_def(_mem.set_ptt, SET_PTT_LIST, 0)
1990
        val = RadioSettingValueList(SET_PTT_LIST, SET_PTT_LIST[tmpsetptt])
1991
        SetPttSetting = RadioSetting("set_ptt", "Ptt Mode, Set how react the ptt (SetPtt)", val)
1992
        SetPttSetting.set_doc('SetPtt: *CLASSIC: press = start transmission, release = stop transmission.  ' + \
1993
                              '*ONEPUSH: you no longer need to press the PTT continuously to transmit.     ' + \
1994
                              ' Simply press once to start transmission, and press a second time to stop.  ' + \
1995
                              ' No more finger cramps :) ')
1996

    
1997
        # Set_tot f4hwn
1998
        tmpsettot = list_def(_mem.set_tot, SET_TOT_EOT_LIST, 0)
1999
        val = RadioSettingValueList(SET_TOT_EOT_LIST, SET_TOT_EOT_LIST[tmpsettot])
2000
        SetTotSetting = RadioSetting("set_tot", "Set TX timeout indicator (SetTot)", val)
2001
        SetTotSetting.set_doc('SetTot: to display the Set TX timeout indicator ')
2002

    
2003
        # Set_eot f4hwn
2004
        tmpseteot = list_def(_mem.set_eot, SET_TOT_EOT_LIST, 0)
2005
        val = RadioSettingValueList(SET_TOT_EOT_LIST, SET_TOT_EOT_LIST[tmpseteot])
2006
        SetEotSetting = RadioSetting("set_eot", " Set End Of Transmission indicator (SetEot)", val)
2007
        SetEotSetting.set_doc('SetEot: to display the end of the Transmission ')
2008

    
2009
        # Set_contrast f4hwn
2010
        tmpcontrast = min_max_def(_mem.set_contrast, 0, 15, 11)
2011
        val = RadioSettingValueInteger(0, 15, tmpcontrast)
2012
        contrastSetting = RadioSetting("set_contrast", "Set Contrast Level (SetCtr)", val)
2013
        contrastSetting.set_doc('SetCtr: to set contrast (0 to 15), default: 10 ')
2014
       
2015
        # Set_inv f4hwn
2016
        tmpsetinv = list_def(_mem.set_inv, SET_OFF_ON_LIST, 0)
2017
        val = RadioSettingValueList(SET_OFF_ON_LIST, SET_OFF_ON_LIST[tmpsetinv])
2018
        SetInvSetting = RadioSetting("set_inv", "Set inverse lcd (SetInv)", val)
2019
        SetInvSetting.set_doc('SetInv: the texte display is black on white or texte white on black, default: black on white')
2020

    
2021
        # Set_lck, uses
2022
        tmpsetlck = list_def(_mem.set_lck, SET_LCK_LIST, 0)
2023
        val = RadioSettingValueList(SET_LCK_LIST, SET_LCK_LIST[tmpsetlck])
2024
        SetLckSetting = RadioSetting("set_lck", "Lock the PTT when keypad is lock (SetLck)", val)
2025
        SetLckSetting.set_doc('SetLck: when keypad is lock, do you want to lock the ptt with it or not ')
2026
        
2027
        # Set_met f4hwn
2028
        tmpsetmet = list_def(_mem.set_met, SET_MET_LIST, 0)
2029
        val = RadioSettingValueList(SET_MET_LIST, SET_MET_LIST[tmpsetmet])
2030
        SetMetSetting = RadioSetting("set_met", "Display the Smeter style (SetMet)", val)
2031
        SetMetSetting.set_doc('SetMet: change the style of the Smeter display, from CLASSIC to TINY ')
2032

    
2033
        tmpsq = min_max_def(_mem.squelch, 0, 9, 1)
2034
        val = RadioSettingValueInteger(0, 9, tmpsq)
2035
        squelch_setting = RadioSetting("squelch", "Squelch (Sql)", val)
2036
        squelch_setting.set_doc('Sql: squelch sensitivity level 0 to 9, 0 = no restriction')
2037
        
2038
        ch_list = []
2039
        for ch in range(1, 201):
2040
            ch_list.append("Channel M" + str(ch))
2041

    
2042
        tmpc = list_def(_mem.call_channel, ch_list, 0)
2043
        val = RadioSettingValueList(ch_list, None, tmpc)
2044
        call_channel_setting = RadioSetting("call_channel",
2045
                                            "One key call channel (1 Call)",
2046
                                            val)
2047
        call_channel_setting.set_doc('1 Call: one key call channel, lets you quickly switch to the channel with 9 Call button ')
2048

    
2049
        val = RadioSettingValueBoolean(_mem.key_lock)
2050
        keypad_lock_setting = RadioSetting("key_lock", "Keypad locked", val)
2051
        keypad_lock_setting.set_doc('Keypad locked: Activated the lock on the keypad now')
2052
        
2053
        tmpval = list_def(_mem.auto_keypad_lock, AUTO_KEYPAD_LOCK_LIST, 1)
2054
        val = RadioSettingValueList(AUTO_KEYPAD_LOCK_LIST, None, tmpval)
2055
        auto_keypad_lock_setting = RadioSetting("auto_keypad_lock",
2056
                         "Auto keypad lock after inactivity ~15sec (KeyLck)", val)
2057
        auto_keypad_lock_setting.set_doc('KeyLck: Keypad lock after inactivity ~15sec *OFF (no lock keypad), AUTO ')
2058
        
2059
        tmptot = list_def(_mem.max_talk_time,  TALK_TIME_LIST, 1)
2060
        val = RadioSettingValueList(TALK_TIME_LIST, None, tmptot)
2061
        tx_t_out_setting = RadioSetting("tot",
2062
                                        "Max talk, TX Time Out (TxTOut)", val)
2063
        tx_t_out_setting.set_doc('TxTOut: select the time limit max transmission ')                                        
2064

    
2065
        tmpbatsave = list_def(_mem.battery_save, BATSAVE_LIST, 4)
2066
        val = RadioSettingValueList(BATSAVE_LIST, None, tmpbatsave)
2067
        bat_save_setting = RadioSetting("battery_save",
2068
                                        "Battery save (BatSav)", val)
2069
        bat_save_setting.set_doc('BatSav: battery save option, a rate between active time and sleep time ')
2070
        
2071
        val = RadioSettingValueBoolean(_mem.noaa_autoscan)
2072
        noaa_auto_scan_setting = RadioSetting("noaa_autoscan",
2073
                                              "NOAA Autoscan (NOAA-S)", val)
2074
        noaa_auto_scan_setting.set_doc('NOAA-S: ')
2075
        
2076
        tmpmicgain = list_def(_mem.mic_gain, MIC_GAIN_LIST, 2)
2077
        val = RadioSettingValueList(MIC_GAIN_LIST, None, tmpmicgain)
2078
        mic_gain_setting = RadioSetting("mic_gain", "Mic Gain (Mic)", val)
2079
        mic_gain_setting.set_doc('Mic: Select the microphone sensitivity level ( Gain) ')
2080
        
2081
        val = RadioSettingValueBoolean(_mem.mic_bar)
2082
        mic_bar_setting = RadioSetting("mic_bar",
2083
                                       "Microphone Bar display (MicBar)", val)
2084
        mic_bar_setting.set_doc('MicBar: Display microphone bar that appears while transmitting ')
2085

    
2086
        tmpchdispmode = list_def(_mem.channel_display_mode,
2087
                                 CHANNELDISP_LIST, 0)
2088
        val = RadioSettingValueList(CHANNELDISP_LIST, None, tmpchdispmode)
2089
        ch_disp_setting = RadioSetting("channel_display_mode",
2090
                                       "Channel display mode (ChDisp)", val)
2091
        ch_disp_setting.set_doc('ChDisp: what to display on screen. *The frequency, *The channel number, *The name, *Then name plus the frequency ')
2092

    
2093
        tmpdispmode = list_def(_mem.power_on_dispmode, WELCOME_LIST, 0)
2094
        val = RadioSettingValueList(WELCOME_LIST, None, tmpdispmode)
2095
        p_on_msg_setting = RadioSetting("welcome_mode",
2096
                                        "Power ON display message (POnMsg)",
2097
                                        val)
2098
        p_on_msg_setting.set_doc('POnMsg: ON power upof the radio, what do you want to display. *Full screen (black). *Your Message. *Voltage of the battery. *Nothing')
2099

    
2100
        logo1 = str(_mem.logo_line1).strip("\x20\x00\xff") + "\x00"
2101
        logo1 = _getstring(logo1.encode('ascii', errors='ignore'), 0, 12)
2102
        val = RadioSettingValueString(0, 12, logo1)
2103
        logo1_setting = RadioSetting("logo1",
2104
                                     "Message line 1 ( MAX 12 characters ) ",
2105
                                     val)
2106
        logo1_setting.set_doc('Message line 1: The first line you can put your message, their is a maximum of 12 characters')
2107

    
2108
        logo2 = str(_mem.logo_line2).strip("\x20\x00\xff") + "\x00"
2109
        logo2 = _getstring(logo2.encode('ascii', errors='ignore'), 0, 12)
2110
        val = RadioSettingValueString(0, 12, logo2)
2111
        logo2_setting = RadioSetting("logo2",
2112
                                     "Message line 2 ( MAX 12 characters )",
2113
                                     val)
2114
        logo2_setting.set_doc('Message line 2: The second line you can put your message, their is a maximum of 12 characters')
2115

    
2116
        tmpbattxt = list_def(_mem.battery_text, BAT_TXT_LIST, 2)
2117
        val = RadioSettingValueList(BAT_TXT_LIST, None, tmpbattxt)
2118
        bat_txt_setting = RadioSetting("battery_text",
2119
                                       "Battery Level Display (BatTXT)", val)
2120
        bat_txt_setting.set_doc('BatTXT: Display additional battery value on the status bar in % or volts')
2121

    
2122
        tmpback = list_def(_mem.backlight_time, BACKLIGHT_LIST, 0)
2123
        val = RadioSettingValueList(BACKLIGHT_LIST, None, tmpback)
2124
        back_lt_setting = RadioSetting("backlight_time",
2125
                                       "Backlight time (BackLt)", val)
2126
        back_lt_setting.set_doc('BackLt: backlight duration, how long the backlight will stay ON after a action end')
2127

    
2128
        tmpback = list_def(_mem.backlight_min, BACKLIGHT_LVL_LIST, 0)
2129
        val = RadioSettingValueList(BACKLIGHT_LVL_LIST, None, tmpback)
2130
        bl_min_setting = RadioSetting("backlight_min",
2131
                                      "Backlight level min (BLMin)", val)
2132
        bl_min_setting.set_doc('BLMin: minimal backlight brightness, when the screen backlight turns OFF it will go dim to this value')
2133

    
2134
        tmpback = list_def(_mem.backlight_max, BACKLIGHT_LVL_LIST, 10)
2135
        val = RadioSettingValueList(BACKLIGHT_LVL_LIST, None, tmpback)
2136
        bl_max_setting = RadioSetting("backlight_max",
2137
                                      "Backlight level max (BLMax)", val)
2138
        bl_max_setting.set_doc('BLMax:  maximal backlight brightness, when the screen backlight turns ON it will turn bright to this value')
2139
        
2140
        tmpback = list_def(_mem.backlight_on_TX_RX, BACKLIGHT_TX_RX_LIST, 0)
2141
        val = RadioSettingValueList(BACKLIGHT_TX_RX_LIST, None, tmpback)
2142
        blt_trx_setting = RadioSetting("backlight_on_TX_RX",
2143
                                       "Backlight on TX/RX (BltTRX)", val)
2144
        blt_trx_setting.set_doc('BltTRX: backlight activation on TX or RX or both TX and RX or no backlight')
2145
        
2146
        val = RadioSettingValueBoolean(_mem.button_beep)
2147
        beep_setting = RadioSetting("button_beep",
2148
                                    "Key press beep sound (Beep)", val)
2149
        beep_setting.set_doc('Beep: keypad press beep sound')
2150

    
2151
        tmpalarmmode = list_def(_mem.roger_beep, ROGER_LIST, 0)
2152
        val = RadioSettingValueList(ROGER_LIST, None, tmpalarmmode)
2153
        roger_setting = RadioSetting("roger_beep",
2154
                                     "End of transmission beep (Roger)", val)
2155
        roger_setting.set_doc('Roger: squelch tail eliminator, eliminates noise at the end of a transmission')
2156

    
2157
        val = RadioSettingValueBoolean(_mem.ste)
2158
        ste_setting = RadioSetting("ste", "Squelch tail elimination (STE)", val)
2159
        ste_setting.set_doc('STE: squelch tail eliminator, eliminates noise at the end of a transmission')
2160

    
2161
        tmprte = list_def(_mem.rp_ste, RTE_LIST, 0)
2162
        val = RadioSettingValueList(RTE_LIST, None, tmprte)
2163
        rp_ste_setting = \
2164
            RadioSetting("rp_ste",
2165
                         "Repeater squelch tail elimination (RP STE)", val)
2166
        rp_ste_setting.set_doc('RP STE: repeater squelch tail eliminator')
2167

    
2168
        val = RadioSettingValueBoolean(_mem.AM_fix)
2169
        am_fix_setting = RadioSetting("AM_fix",
2170
                                      "AM reception fix (AM Fix)", val)
2171
        am_fix_setting.set_doc('AM Fix: activates autogain AM fix function')
2172

    
2173
        tmpvox = min_max_def((_mem.vox_level + 1) * _mem.vox_switch, 0, 10, 0)
2174
        val = RadioSettingValueList(VOX_LIST, None, tmpvox)
2175
        vox_setting = RadioSetting("vox", "Voice-operated switch (VOX)", val)
2176
        vox_setting.set_doc('VOX: voice TX activation sensitivity level VOX Setting ')
2177
        
2178
        tmprxmode = list_def((bool(_mem.crossband) << 1)
2179
                             + bool(_mem.dual_watch),
2180
                             RXMODE_LIST, 0)
2181
        val = RadioSettingValueList(RXMODE_LIST, None, tmprxmode)
2182
        rx_mode_setting = RadioSetting("rx_mode", "RX Mode (RxMode)", val)
2183
        rx_mode_setting.set_doc('RxMode: *MAIN ONLY: transmits and listens on the main frequency. *DUAL RX RESPOND: ' + \
2184
                                'listens both frequencies, if signal received on the secondary frequency, it locks to ' + \
2185
                                'it for a couple of seconds so you can respond to the call (DWR). *CROSS BAND: always ' + \
2186
                                'transmits on the primary and listens on the secondary frequency (XB). *MAIN TX DUAL RX:' + \
2187
                                'always transmits on the primary, listens to both (DW).')
2188

    
2189
        val = RadioSettingValueBoolean(_mem.freq_mode_allowed)
2190
        freq_mode_allowed_setting = RadioSetting("freq_mode_allowed",
2191
                                                 "Frequency mode allowed", val)
2192
        freq_mode_allowed_setting.set_doc('Frequency mode allowed ')
2193

    
2194
        tmpscanres = list_def(_mem.scan_resume_mode, SCANRESUME_LIST, 0)
2195
        val = RadioSettingValueList(SCANRESUME_LIST, None, tmpscanres)
2196
        scn_rev_setting = RadioSetting("scan_resume_mode",
2197
                                       "Scan resume mode (ScnRev)", val)
2198
        scn_rev_setting.set_doc('ScnRev: scan resume mode *CARRIER - resume scan after signal disappears. ' + \
2199
                                '*TIMEOUT - resume scan after 5 seconds pause. *STOP - after receiving a signal, stop the scan')
2200
        
2201
        tmpvoice = list_def(_mem.voice, VOICE_LIST, 0)
2202
        val = RadioSettingValueList(VOICE_LIST, None, tmpvoice)
2203
        voice_setting = RadioSetting("voice", "Voice", val)
2204

    
2205
        tmpalarmmode = list_def(_mem.alarm_mode, ALARMMODE_LIST, 0)
2206
        val = RadioSettingValueList(ALARMMODE_LIST, None, tmpalarmmode)
2207
        alarm_setting = RadioSetting("alarm_mode", "Alarm mode", val)
2208

    
2209
        # ----------------- Extra settings
2210

    
2211
        # S-meter
2212
        tmp_s0 = -int(_mem.s0_level)
2213
        tmp_s9 = -int(_mem.s9_level)
2214

    
2215
        if tmp_s0 not in range(-200, -91) or tmp_s9 not in range(-160, -51) \
2216
           or tmp_s9 < tmp_s0+9:
2217

    
2218
            tmp_s0 = -130
2219
            tmp_s9 = -76
2220
        val = RadioSettingValueInteger(-200, -90, tmp_s0)
2221
        s0_level_setting = RadioSetting("s0_level",
2222
                                        "S-meter S0 level [dBm]", val)
2223
        s0_level_setting.set_doc('S-meter S0 level [dBm]: To set the level calibration for S0 ')
2224

    
2225
        val = RadioSettingValueInteger(-160, -50, tmp_s9)
2226
        s9_level_setting = RadioSetting("s9_level",
2227
                                        "S-meter S9 level [dBm]", val)
2228
        s9_level_setting.set_doc('S-meter S9 level [dBm]: To set the level calibration for S9 ')
2229

    
2230
        # Battery Type
2231
        tmpbtype = list_def(_mem.Battery_type, BATTYPE_LIST, 0)
2232
        val = RadioSettingValueList(BATTYPE_LIST, BATTYPE_LIST[tmpbtype])
2233
        bat_type_setting = RadioSetting("Battery_type",
2234
                                        "Battery Type (BatTyp)", val)
2235
        bat_type_setting.set_doc('BatTyp: What type of battery the radio is using, this affect the level value of the battery in the display')
2236

    
2237
        # Power on password
2238
#        def validate_password(value):
2239
#            value = value.strip(" ")
2240
#            if value.isdigit():
2241
#                return value.zfill(6)
2242
#            if value != "":
2243
#                raise InvalidValueError("Power on password "
2244
#                                        "can only have digits")
2245
#            return ""
2246

    
2247
#        pswd_str = str(int(_mem.password)).zfill(6) \
2248
#            if _mem.password < 1000000 else ""
2249
#        val = RadioSettingValueString(0, 6, pswd_str)
2250
#        val.set_validate_callback(validate_password)
2251
#        pswd_setting = RadioSetting("password", "Power on password", val)
2252

    
2253
        # ----------------- FM radio
2254

    
2255
        append_label(fmradio, "Channel Memory Radio (MR)", "Frequency [MHz]")
2256

    
2257
        for i in range(1, 21):
2258
            fmfreq = _mem.fmfreq[i-1]/10.0
2259
            freq_name = str(fmfreq)
2260
            if fmfreq < FMMIN or fmfreq > FMMAX:
2261
                freq_name = ""
2262
            rs = RadioSetting("FM_" + str(i), "Ch " + str(i),
2263
                              RadioSettingValueString(0, 5, freq_name))
2264
            rs.set_doc('Radio Frequency: Enter the radio frequency in your area in MHZ, example: 96.9 ' + \
2265
                       'To listen the FM radio, Long press on the 5 key, then if you want to scan FM radio ' + \
2266
                       'Station near you, press the *. but scan will erase the existing FM radio list ')
2267
            fmradio.append(rs)
2268

    
2269
        # ----------------- Unlock settings
2270

    
2271
        # F-LOCK
2272
        def validate_int_flock(value):
2273
            mem_val = self._memobj.int_flock
2274
            if mem_val != 7 and value == FLOCK_LIST[7]:
2275
                msg = "\"" + value + "\" can only be enabled from radio menu"
2276
                raise InvalidValueError(msg)
2277
            return value
2278

    
2279
        tmpflock = list_def(_mem.int_flock, FLOCK_LIST, 0)
2280
        val = RadioSettingValueList(FLOCK_LIST, None, tmpflock)
2281
        val.set_validate_callback(validate_int_flock)
2282
        f_lock_setting = RadioSetting("int_flock",
2283
                                      "TX Frequency Lock (F Lock)", val)
2284
        f_lock_setting.set_doc('F Lock: Sets the TX frequency band plan ')                                     
2285

    
2286
        val = RadioSettingValueBoolean(_mem.int_200tx)
2287
        tx200_setting = RadioSetting("int_200tx",
2288
                                     "Unlock 174-350MHz TX (Tx 200)", val)
2289
        tx200_setting.set_doc('Enables transmission on 200MHz ')
2290

    
2291
        val = RadioSettingValueBoolean(_mem.int_350tx)
2292
        tx350_setting = RadioSetting("int_350tx",
2293
                                     "Unlock 350-400MHz TX (Tx 350)", val)
2294
        tx350_setting.set_doc('Enables transmission on 350MHz ')
2295
        
2296
        val = RadioSettingValueBoolean(_mem.int_500tx)
2297
        tx500_setting = RadioSetting("int_500tx",
2298
                                     "Unlock 500-600MHz TX (Tx 500)", val)
2299
        tx500_setting.set_doc('Enables transmission on 500MHz ')
2300
        
2301
        val = RadioSettingValueBoolean(_mem.int_350en)
2302
        en350_setting = RadioSetting("int_350en",
2303
                                     "Unlock 350-400MHz RX (350 En)", val)
2304
        en350_setting.set_doc('Enables reception on 350MHz ')
2305
        
2306
        val = RadioSettingValueBoolean(_mem.int_scren)
2307
        en_scrambler_setting = RadioSetting("int_scren",
2308
                                            "Scrambler enabled (ScraEn)", val)
2309
        en_scrambler_setting.set_doc('Enables Scrambler ')
2310
        
2311
        # ----------------- Driver Info
2312

    
2313
        if self.FIRMWARE_VERSION == "":
2314
            firmware = "To get the firmware version please download" \
2315
                       "the image from the radio first"
2316
        else:
2317
            firmware = self.FIRMWARE_VERSION
2318

    
2319
        append_label(roinfo,
2320
                     "=" * 6 + " Firmware F4HWN " + "=" * 300, "=" * 300)
2321

    
2322
        append_label(roinfo, "Firmware Version", firmware)
2323
        val = RadioSettingValueString(0,75,FIRMWARE_DRIVER_VERSION_UPDATE)
2324
        rs = RadioSetting("Update","Latest Firmware F4HWN" + ", copy link:(CTRL-C), paste:(CTRL-V) to your browser -> ", val)                      
2325
        rs.set_doc('Be sure you have the latest firmware available')
2326
        roinfo.append(rs)
2327
        
2328
        append_label(roinfo,
2329
                     "=" * 6 + " Chirp Driver F4HWN " + "=" * 300, "=" * 300)
2330
                     
2331
        append_label(roinfo, "Driver Chirp Version", DRIVER_VERSION)
2332
        val = RadioSettingValueString(0,75,CHIRP_DRIVER_VERSION_UPDATE)
2333
        rs = RadioSetting("Update1","Latest Driver " + self.MODEL + ", copy link:(CTRL-C), paste:(CTRL-V) to your browser -> ", val)                      
2334
        rs.set_doc('Be sure you have the latest chirp driver available')
2335
        roinfo.append(rs)
2336
        
2337
        # ----------------- Calibration
2338

    
2339
        val = RadioSettingValueBoolean(False)
2340

    
2341
        def validate_upload_calibration(value):
2342
            if value and not self.upload_calibration:
2343
                msg = "This option may brake your radio!!!\n" \
2344
                    "You are doing this at your own risk.\n" \
2345
                    "Make sure you have a working calibration backup.\n" \
2346
                    "Don't use it unless you know what you're doing."
2347
                ret = wx.MessageBox(msg, "Warning", wx.OK | wx.CANCEL |
2348
                                    wx.CANCEL_DEFAULT | wx.ICON_WARNING)
2349
                value = ret == wx.OK
2350
            self.upload_calibration = value
2351
            return value
2352

    
2353
        val.set_validate_callback(validate_upload_calibration)
2354
        radio_setting = RadioSetting("upload_calibration",
2355
                                     "Upload calibration", val)
2356
        radio_setting.set_doc('To Upload to the radio only the setting in the calibration section, you need to check the case, then upload to the radio')
2357
        calibration.append(radio_setting)
2358

    
2359
        radio_setting_group = RadioSettingGroup("squelch_calibration",
2360
                                                "Squelch")
2361
        calibration.append(radio_setting_group)
2362

    
2363
        bands = {"sqlBand1_3": "Frequency Band 1-3",
2364
                 "sqlBand4_7": "Frequency Band 4-7"}
2365
        for bnd, bndn in bands.items():
2366
            append_label(radio_setting_group,
2367
                         "=" * 6 + " " + bndn + " " + "=" * 300, "=" * 300)
2368
            for sql in range(0, 10):
2369
                prefix = "_mem.cal." + bnd + "."
2370
                postfix = "[" + str(sql) + "]"
2371
                append_label(radio_setting_group, "Squelch " + str(sql))
2372

    
2373
                name = prefix + "openRssiThr" + postfix
2374
                tempval = min_max_def(eval(name), 0, 255, 0)
2375
                val = RadioSettingValueInteger(0, 255, tempval)
2376
                radio_setting = RadioSetting(name, "RSSI threshold open", val)
2377
                radio_setting_group.append(radio_setting)
2378

    
2379
                name = prefix + "closeRssiThr" + postfix
2380
                tempval = min_max_def(eval(name), 0, 255, 0)
2381
                val = RadioSettingValueInteger(0, 255, tempval)
2382
                radio_setting = RadioSetting(name, "RSSI threshold close", val)
2383
                radio_setting_group.append(radio_setting)
2384

    
2385
                name = prefix + "openNoiseThr" + postfix
2386
                tempval = min_max_def(eval(name), 0, 127, 0)
2387
                val = RadioSettingValueInteger(0, 127, tempval)
2388
                radio_setting = RadioSetting(name, "Noise threshold open", val)
2389
                radio_setting_group.append(radio_setting)
2390

    
2391
                name = prefix + "closeNoiseThr" + postfix
2392
                tempval = min_max_def(eval(name), 0, 127, 0)
2393
                val = RadioSettingValueInteger(0, 127, tempval)
2394
                radio_setting = RadioSetting(name, "Noise threshold close",
2395
                                             val)
2396
                radio_setting_group.append(radio_setting)
2397

    
2398
                name = prefix + "openGlitchThr" + postfix
2399
                tempval = min_max_def(eval(name), 0, 255, 0)
2400
                val = RadioSettingValueInteger(0, 255, tempval)
2401
                radio_setting = RadioSetting(name, "Glitch threshold open",
2402
                                             val)
2403
                radio_setting_group.append(radio_setting)
2404

    
2405
                name = prefix + "closeGlitchThr" + postfix
2406
                tempval = min_max_def(eval(name), 0, 255, 0)
2407
                val = RadioSettingValueInteger(0, 255, tempval)
2408
                radio_setting = RadioSetting(name, "Glitch threshold close",
2409
                                             val)
2410
                radio_setting_group.append(radio_setting)
2411

    
2412
        radio_setting_group = RadioSettingGroup("rssi_level_calibration",
2413
                                                "RSSI levels")
2414
        calibration.append(radio_setting_group)
2415

    
2416
        bands = {"rssiLevelsBands1_2": "1-2 ", "rssiLevelsBands3_7": "3-7 "}
2417
        for bnd, bndn in bands.items():
2418
            append_label(radio_setting_group,
2419
                         "=" * 6 +
2420
                         " RSSI levels for QS original small bar graph, bands "
2421
                         + bndn + "=" * 300, "=" * 300)
2422
            for lvl in [1, 2, 4, 6]:
2423
                name = "_mem.cal." + bnd + ".level" + str(lvl)
2424
                tempval = min_max_def(eval(name), 0, 65535, 0)
2425
                val = RadioSettingValueInteger(0, 65535, tempval)
2426
                radio_setting = RadioSetting(name, "Level " + str(lvl), val)
2427
                radio_setting_group.append(radio_setting)
2428

    
2429
#
2430

    
2431
        radio_setting_group = RadioSettingGroup("tx_power_calibration",
2432
                                                "TX power")
2433
        calibration.append(radio_setting_group)
2434

    
2435
        for bnd in range(0, 7):
2436
            append_label(radio_setting_group, "=" * 6 + " TX power band "
2437
                         + str(bnd+1) + " " + "=" * 300, "=" * 300)
2438
            powers = {"low": "Low", "mid": "Medium", "hi": "High"}
2439
            for pwr, pwrn in powers.items():
2440
                append_label(radio_setting_group, pwrn)
2441
                bounds = ["lower", "center", "upper"]
2442
                for bound in bounds:
2443
                    name = f"_mem.cal.txp[{bnd}].{pwr}.{bound}"
2444
                    tempval = min_max_def(eval(name), 0, 255, 0)
2445
                    val = RadioSettingValueInteger(0, 255, tempval)
2446
                    radio_setting = RadioSetting(name, bound.capitalize(), val)
2447
                    radio_setting_group.append(radio_setting)
2448

    
2449
#
2450

    
2451
        radio_setting_group = RadioSettingGroup("battery_calibration",
2452
                                                "Battery")
2453
        calibration.append(radio_setting_group)
2454

    
2455
        for lvl in range(0, 6):
2456
            name = "_mem.cal.batLvl[" + str(lvl) + "]"
2457
            temp_val = min_max_def(eval(name), 0, 4999, 4999)
2458
            val = RadioSettingValueInteger(0, 4999, temp_val)
2459
            radio_setting = \
2460
                RadioSetting(name, "Level " + str(lvl) +
2461
                             (" (voltage calibration)" if lvl == 3 else ""),
2462
                             val)
2463
            radio_setting_group.append(radio_setting)
2464

    
2465
        radio_setting_group = RadioSettingGroup("vox_calibration", "VOX")
2466
        calibration.append(radio_setting_group)
2467

    
2468
        for lvl in range(0, 10):
2469
            append_label(radio_setting_group, "Level " + str(lvl + 1))
2470

    
2471
            name = "_mem.cal.vox1Thr[" + str(lvl) + "]"
2472
            val = RadioSettingValueInteger(0, 65535, eval(name))
2473
            radio_setting = RadioSetting(name, "On", val)
2474
            radio_setting_group.append(radio_setting)
2475

    
2476
            name = "_mem.cal.vox0Thr[" + str(lvl) + "]"
2477
            val = RadioSettingValueInteger(0, 65535, eval(name))
2478
            radio_setting = RadioSetting(name, "Off", val)
2479
            radio_setting_group.append(radio_setting)
2480

    
2481
        radio_setting_group = RadioSettingGroup("mic_calibration",
2482
                                                "Microphone sensitivity")
2483
        calibration.append(radio_setting_group)
2484

    
2485
        for lvl in range(0, 5):
2486
            name = "_mem.cal.micLevel[" + str(lvl) + "]"
2487
            tempval = min_max_def(eval(name), 0, 31, 31)
2488
            val = RadioSettingValueInteger(0, 31, tempval)
2489
            radio_setting = RadioSetting(name, "Level " + str(lvl), val)
2490
            radio_setting_group.append(radio_setting)
2491

    
2492
        radio_setting_group = RadioSettingGroup("other_calibration", "Other")
2493
        calibration.append(radio_setting_group)
2494

    
2495
        name = "_mem.cal.xtalFreqLow"
2496
        temp_val = min_max_def(eval(name), -1000, 1000, 0)
2497
        val = RadioSettingValueInteger(-1000, 1000, temp_val)
2498
        radio_setting = RadioSetting(name, "Xtal frequency low", val)
2499
        radio_setting_group.append(radio_setting)
2500

    
2501
        name = "_mem.cal.volumeGain"
2502
        temp_val = min_max_def(eval(name), 0, 63, 58)
2503
        val = RadioSettingValueInteger(0, 63, temp_val)
2504
        radio_setting = RadioSetting(name, "Volume gain", val)
2505
        radio_setting_group.append(radio_setting)
2506

    
2507
        name = "_mem.cal.dacGain"
2508
        temp_val = min_max_def(eval(name), 0, 15, 8)
2509
        val = RadioSettingValueInteger(0, 15, temp_val)
2510
        radio_setting = RadioSetting(name, "DAC gain", val)
2511
        radio_setting_group.append(radio_setting)
2512

    
2513
        # -------- LAYOUT
2514
        append_label(basic,
2515
                     "=" * 6 + " F4HWN, Begin Setting, if this area need to be upload select the upload F4HWN" + "=" * 300, "=" * 300)
2516
        basic.append(Upload_f4hwn)
2517
        basic.append(SetLowSetting)
2518
        basic.append(SetPttSetting)
2519
        basic.append(SetTotSetting)
2520
        basic.append(SetEotSetting)
2521
        basic.append(contrastSetting)
2522
        basic.append(SetInvSetting)
2523
        basic.append(SetLckSetting)
2524
        basic.append(SetMetSetting)
2525
        append_label(basic,
2526
                     "=" * 6 + " F4HWN, End Setting " + "=" * 300, "=" * 300)
2527

    
2528
        append_label(basic,
2529
                     "=" * 6 + " General settings " + "=" * 300, "=" * 300)
2530

    
2531
        basic.append(squelch_setting)
2532
        basic.append(rx_mode_setting)
2533
        basic.append(call_channel_setting)
2534
        basic.append(auto_keypad_lock_setting)
2535
        basic.append(tx_t_out_setting)
2536
        basic.append(bat_save_setting)
2537
        basic.append(scn_rev_setting)
2538
        if _mem.BUILD_OPTIONS.ENABLE_NOAA:
2539
            basic.append(noaa_auto_scan_setting)
2540
        if _mem.BUILD_OPTIONS.ENABLE_AM_FIX:
2541
            basic.append(am_fix_setting)
2542

    
2543
        append_label(basic,
2544
                     "=" * 6 + " Display settings " + "=" * 300, "=" * 300)
2545

    
2546
        basic.append(bat_txt_setting)
2547
        basic.append(mic_bar_setting)
2548
        basic.append(ch_disp_setting)
2549
        basic.append(p_on_msg_setting)
2550
        basic.append(logo1_setting)
2551
        basic.append(logo2_setting)
2552

    
2553
        append_label(basic, "=" * 6 + " Backlight settings "
2554
                     + "=" * 300, "=" * 300)
2555

    
2556
        basic.append(back_lt_setting)
2557
        basic.append(bl_min_setting)
2558
        basic.append(bl_max_setting)
2559
        basic.append(blt_trx_setting)
2560

    
2561
        append_label(basic, "=" * 6 + " Audio related settings "
2562
                     + "=" * 300, "=" * 300)
2563

    
2564
        if _mem.BUILD_OPTIONS.ENABLE_VOX:
2565
            basic.append(vox_setting)
2566
        basic.append(mic_gain_setting)
2567
        basic.append(beep_setting)
2568
        basic.append(roger_setting)
2569
        basic.append(ste_setting)
2570
        basic.append(rp_ste_setting)
2571
        if _mem.BUILD_OPTIONS.ENABLE_VOICE:
2572
            basic.append(voice_setting)
2573
        if _mem.BUILD_OPTIONS.ENABLE_ALARM:
2574
            basic.append(alarm_setting)
2575

    
2576
        append_label(basic, "=" * 6 + " Radio state " + "=" * 300, "=" * 300)
2577

    
2578
        basic.append(freq0_setting)
2579
        basic.append(freq1_setting)
2580
        basic.append(tx_vfo_setting)
2581
        basic.append(keypad_lock_setting)
2582

    
2583
        advanced.append(freq_mode_allowed_setting)
2584
        advanced.append(bat_type_setting)
2585
        advanced.append(s0_level_setting)
2586
        advanced.append(s9_level_setting)
2587
#        if _mem.BUILD_OPTIONS.ENABLE_PWRON_PASSWORD:
2588
#            advanced.append(pswd_setting)
2589

    
2590
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2591
            dtmf.append(sep_code_setting)
2592
            dtmf.append(group_code_setting)
2593
        dtmf.append(first_code_per_setting)
2594
        dtmf.append(spec_per_setting)
2595
        dtmf.append(code_per_setting)
2596
        dtmf.append(code_int_setting)
2597
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2598
            dtmf.append(ani_id_setting)
2599
        dtmf.append(up_code_setting)
2600
        dtmf.append(dw_code_setting)
2601
        dtmf.append(d_prel_setting)
2602
        dtmf.append(dtmf_side_tone_setting)
2603
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2604
            dtmf.append(dtmf_resp_setting)
2605
            dtmf.append(d_hold_setting)
2606
            dtmf.append(d_live_setting)
2607
            dtmf.append(perm_kill_setting)
2608
            dtmf.append(kill_code_setting)
2609
            dtmf.append(rev_code_setting)
2610
            dtmf.append(killed_setting)
2611

    
2612
        unlock.append(f_lock_setting)
2613
        unlock.append(tx200_setting)
2614
        unlock.append(tx350_setting)
2615
        unlock.append(tx500_setting)
2616
        unlock.append(en350_setting)      
2617
# desable by f4hwn
2618
#       unlock.append(en_scrambler_setting)
2619

    
2620
        return top
2621

    
2622
    def set_memory(self, memory):
2623
        """
2624
        Store details about a high-level memory to the memory map
2625
        This is called when a user edits a memory in the UI
2626
        """
2627
        number = memory.number-1
2628
        att_num = number if number < 200 else 200 + int((number - 200) / 2)
2629

    
2630
        # Get a low-level memory object mapped to the image
2631
        _mem_chan = self._memobj.channel[number]
2632
        _mem_attr = self._memobj.ch_attr[att_num]
2633

    
2634
        _mem_attr.is_scanlist1 = 0
2635
        _mem_attr.is_scanlist2 = 0
2636
        _mem_attr.compander = 0
2637
        _mem_attr.is_free = 1
2638
        _mem_attr.band = 0x7
2639

    
2640
        # empty memory
2641
        if memory.empty:
2642
            _mem_chan.set_raw("\xFF" * 16)
2643
            if number < 200:
2644
                _mem_chname = self._memobj.channelname[number]
2645
                _mem_chname.set_raw("\xFF" * 16)
2646
            return memory
2647

    
2648
        # find band
2649
        band = self._find_band(memory.freq)
2650

    
2651
        # mode
2652
        tmp_mode = self.get_features().valid_modes.index(memory.mode)
2653
        _mem_chan.modulation = tmp_mode / 2
2654
        _mem_chan.bandwidth = tmp_mode % 2
2655
        if memory.mode == "USB":
2656
            _mem_chan.bandwidth = 1  # narrow
2657

    
2658
        # frequency/offset
2659
        _mem_chan.freq = memory.freq/10
2660
        _mem_chan.offset = memory.offset/10
2661

    
2662
        if memory.duplex == "":
2663
            _mem_chan.offset = 0
2664
            _mem_chan.offsetDir = 0
2665
        elif memory.duplex == '-':
2666
            _mem_chan.offsetDir = FLAGS1_OFFSET_MINUS
2667
        elif memory.duplex == '+':
2668
            _mem_chan.offsetDir = FLAGS1_OFFSET_PLUS
2669
        elif memory.duplex == 'off':
2670
            # we fake tx disable by setting the tx freq to 0 MHz
2671
            _mem_chan.offsetDir = FLAGS1_OFFSET_MINUS
2672
            _mem_chan.offset = _mem_chan.freq
2673
        # set band
2674

    
2675
        _mem_attr.is_free = 0
2676
        _mem_attr.band = band
2677

    
2678
        # channels >200 are the 14 VFO chanells and don't have names
2679
        if number < 200:
2680
            _mem_chname = self._memobj.channelname[number]
2681
            tag = memory.name.ljust(10) + "\x00"*6
2682
            _mem_chname.name = tag  # Store the alpha tag
2683

    
2684
        # tone data
2685
        self._set_tone(memory, _mem_chan)
2686

    
2687
        # step
2688
        _mem_chan.step = STEPS.index(memory.tuning_step)
2689

    
2690
        # tx power
2691
        if str(memory.power) == str(UVK5_POWER_LEVELS[2]):
2692
            _mem_chan.txpower = POWER_HIGH
2693
        elif str(memory.power) == str(UVK5_POWER_LEVELS[1]):
2694
            _mem_chan.txpower = POWER_MEDIUM
2695
        else:
2696
            _mem_chan.txpower = POWER_LOW
2697

    
2698
        # -------- EXTRA SETTINGS
2699

    
2700
        def get_setting(name, def_val):
2701
            if name in memory.extra:
2702
                return int(memory.extra[name].value)
2703
            return def_val
2704

    
2705
        _mem_chan.busyChLockout = get_setting("busyChLockout", False)
2706
        _mem_chan.dtmf_pttid = get_setting("pttid", 0)
2707
        _mem_chan.freq_reverse = get_setting("frev", False)
2708
        _mem_chan.dtmf_decode = get_setting("dtmfdecode", False)
2709
        _mem_chan.scrambler = get_setting("scrambler", 0)
2710
        _mem_attr.compander = get_setting("compander", 0)
2711
        if number < 200:
2712
            tmp_val = get_setting("scanlists", 0)
2713
            _mem_attr.is_scanlist1 = bool(tmp_val & 1)
2714
            _mem_attr.is_scanlist2 = bool(tmp_val & 2)
2715

    
2716
        return memory
    (1-1/1)