Project

General

Profile

New Model #11270 » uvk5_egzumer_f4hwn_fr.py

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

    
30
# change/ modification
31
#
32
# 2024-02-09 :section password has been commented, because the new chirp give error with int.
33
# 2024-02-18 : a bug fix has been remove, that seem not work under mac os.. under investigation..
34
#              search for (bugfix calibration)
35

    
36
import struct
37
import logging
38
import wx
39

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

    
46
LOG = logging.getLogger(__name__)
47

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

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

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

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

    
70
// 0x08
71
  u8 rxcode;
72
  u8 txcode;
73

    
74
// 0x0A
75
  u8 txcodeflag:4,
76
  rxcodeflag:4;
77

    
78
// 0x0B
79
  u8 modulation:4,
80
  offsetDir:4;
81

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

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

    
94
  // 0x0E
95
  u8 step;
96
  u8 scrambler;
97

    
98
} channel[214];
99

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

    
109
#seekto 0xe40;
110
ul16 fmfreq[20];
111

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

    
122

    
123
u8 backlight_min:4,
124
backlight_max:4;
125

    
126
u8 channel_display_mode;
127
u8 crossband;
128
u8 battery_save;
129
u8 dual_watch;
130
u8 backlight_time;
131
u8 ste;
132
u8 freq_mode_allowed;
133

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

    
144
#seekto 0xe90;
145

    
146
u8 keyM_longpress_action:7,
147
    button_beep:1;
148

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

    
158
#seekto 0xea0;
159
u8 voice;
160
u8 s0_level;
161
u8 s9_level;
162

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

    
170
#seekto 0xeb0;
171
char logo_line1[16];
172
char logo_line2[16];
173

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

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

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

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

    
217

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

    
225

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

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

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

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

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

    
277
    struct {
278
        ul16 level1;
279
        ul16 level2;
280
        ul16 level4;
281
        ul16 level6;
282
    } rssiLevelsBands1_2;
283

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

    
303
    #seekto 0x1F40;
304
    ul16 batLvl[6];
305

    
306
    #seekto 0x1F50;
307
    ul16 vox1Thr[10];
308

    
309
    #seekto 0x1F68;
310
    ul16 vox0Thr[10];
311

    
312
    #seekto 0x1F80;
313
    u8 micLevel[5];
314

    
315
    #seekto 0x1F88;
316
    il16 xtalFreqLow;
317

    
318
    #seekto 0x1F8E;
319
    u8 volumeGain;
320
    u8 dacGain;
321
} cal;
322

    
323

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

    
342
#seekto 0x1FF2;
343
u8 eeprom0x1ff2;
344
u8 eeprom0x1ff3;
345
u8 eeprom0x1ff4;
346

    
347
u8 set_gui:1,
348
set_met:1,
349
set_lck:1,
350
set_inv:1,
351
set_contrast:4;
352

    
353
u8 set_tot:4,
354
set_eot:4;
355

    
356
u8 set_low:4,
357
set_ptt:4;
358

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

    
369

    
370
# flags1
371
FLAGS1_OFFSET_NONE = 0b00
372
FLAGS1_OFFSET_MINUS = 0b10
373
FLAGS1_OFFSET_PLUS = 0b01
374

    
375
POWER_HIGH = 0b10
376
POWER_MEDIUM = 0b01
377
POWER_LOW = 0b00
378

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

    
382
# SET_PTT f4hwn
383
SET_PTT_LIST = ["CLASSIC", "ONEPUSH"]
384

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

    
388
# SET_OFF_ON f4hwn
389
SET_OFF_ON_LIST = ["OFF", "ON"]
390

    
391
# SET_lck f4hwn
392
SET_LCK_LIST = ["KEYS", "KEYS+PTT"]
393

    
394
# SET_MET SET_GUI f4hwn
395
SET_MET_LIST = ["TINY", "CLASSIC"]
396

    
397

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

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

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

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

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

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

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

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

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

    
439
# Backlight _TX_RX_LIST
440
BACKLIGHT_TX_RX_LIST = ["OFF", "TX", "RX", "TX/RX"]
441

    
442
# steps TODO: change order
443
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,
444
         9, 15, 20, 30, 50, 100, 125, 200, 250, 500]
445

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

    
453

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

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

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

    
488
SCANRESUME_LIST = ["Ecouter 5 seconds et reprendre scan (TIMEOUT)",
489
                   "Ecouter jusqu'a ce qu\'il n\'y aie plus de signal (CARRIER)",
490
                   "Arret du SCAN a la reception d'un signal  (STOP)"]
491
WELCOME_LIST = ["Message 1 + Tension + Son  (ALL)", "Bips  (SOUND)", "Message 1 et Message 2  (MESSAGE)", "Tension de la Batterie  (VOLTAGE)", "Rien  (NONE)"]
492
VOICE_LIST = ["OFF", "Chinese", "English"]
493

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

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

    
508
# fm radio supported frequencies
509
FMMIN = 76.0
510
FMMAX = 108.0
511

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

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

    
533
SCANLIST_LIST = ["None", "List1", "List2", "Both"]
534
SCANLIST_SELECT_LIST = ["LIST 1", "LIST 2", "Toutes les Lists  (ALL)"]
535

    
536
DTMF_CHARS = "0123456789ABCD*# "
537
DTMF_CHARS_ID = "0123456789ABCDabcd"
538
DTMF_CHARS_KILL = "0123456789ABCDabcd"
539
DTMF_CHARS_UPDOWN = "0123456789ABCDabcd#* "
540
DTMF_CODE_CHARS = "ABCD*# "
541
DTMF_DECODE_RESPONSE_LIST = ["NONE", "Sonnerie locale  (RING)", "Replay response  (REPLAY)",
542
                             "Sonnerie local + reply response  (BOTH)"]
543

    
544
KEYACTIONS_LIST = ["NONE",
545
                   "FLASHLIGHT",
546
                   "POWER",
547
                   "MONITOR",
548
                   "SCAN",
549
                   "Fonction VOX (VOX)",
550
                   "ALARM",
551
                   "FM RADIO",
552
                   "1750Hz TONE",
553
                   "LOCK KEYPAD",
554
                   "Bascule de VFO A/B (Haut/Bas) (SWITCH VFO)",
555
                   "Bascule mode Frequence/Memoire (VFO/MR)",
556
                   "Bascule le Mode (SWITCH DEMODUL)",
557
                   "Eteindre l\'ecran (BLMIN TMP OFF)",
558
                   "Bascule mode Reception (SWITCH RX MODE)",
559
                   "Bascule le mode PTT (SWITCH PTT)",
560
                   "Bascule le filtre BF (WIDE/NARROW)"
561
                  ]
562

    
563
MIC_GAIN_LIST = ["+1.1dB", "+4.0dB", "+8.0dB", "+12.0dB", "+15.1dB"]
564

    
565

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

    
576

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

    
593

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

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

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

    
613

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

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

    
631
    footer = serport.read(4)
632

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

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

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

    
649
    cmd2 = xorarr(cmd)
650

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

    
654
    return cmd2
655

    
656

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

    
666

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

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

    
686
    LOG.info("Found firmware: %s", firmware)
687
    return firmware
688

    
689

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

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

    
703

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

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

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

    
717
    _send_command(serport, writemem)
718
    rep = _receive_reply(serport)
719

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

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

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

    
731

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

    
736

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

    
747
    eeprom = b""
748
    f = _sayhello(serport)
749
    if f:
750
        radio.FIRMWARE_VERSION = f
751
    else:
752
        raise errors.RadioError("Erreur d'initialisation de la radio")
753

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

    
760
        if data and len(data) == MEM_BLOCK:
761
            eeprom += data
762
            addr += MEM_BLOCK
763
        else:
764
            raise errors.RadioError("Telechargement memoire incomplete")
765

    
766
    return memmap.MemoryMapBytes(eeprom)
767

    
768

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

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

    
793
    radio.status_fn(status)
794

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

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

    
813
    _resetradio(serport)
814

    
815
    return True
816

    
817

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

    
826

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

    
835

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

    
845
# this change to send power level chan in the calibration but under macos it give error
846
# bugfix calibration : put in comment next line: upload_calibration = False
847
    upload_calibration = False
848
    upload_f4hwn = False
849

    
850
    def _get_bands(self):
851
        is_wide = self._memobj.BUILD_OPTIONS.ENABLE_WIDE_RX \
852
            if self._memobj is not None else True
853
        bands = BANDS_WIDE if is_wide else BANDS_STANDARD
854
        return bands
855

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

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

    
874
    def _get_specials(self):
875
        """generates SPECIALS"""
876
        specials = {}
877
        for idx, name in enumerate(self._get_vfo_channel_names()):
878
            specials[name] = 200 + idx
879
        return specials
880

    
881
    @classmethod
882
    def get_prompts(cls):
883
        rp = chirp_common.RadioPrompts()
884
        rp.experimental = \
885
            'Ceci est un pilote experimental pour le Quansheng UV-K5.\n' \
886
            'Celui-ci peut endomager les reglages de votre radio ou pire. L\'utilisation est a vos risques.\n' \
887
            'Avant de faire n\'import quel changement, faites une sauvegarde des parametres avec chirp\n' \
888
            'et garder la intacte. Cela peut etre utilse pour restaurer les reglages d\'origine.\n\n' \
889
            'Quelque detail ne sont pas encore implante ou fonctionelle.'
890
        rp.pre_download = \
891
            "1. Allumez la radio. ( sans tenir aucun bouton enfoncer )\n" \
892
            "2. Connectez le cable sur le connecteur Haut-Parleur/Micro.\n" \
893
            "3. ASSUREZ vous que le connecteur et bien enfoncer.\n" \
894
            "4. Cliquer sur OK pour telecharger l\'image de votre radio.\n\n" \
895
            "Cela peut ne pas fonctionner si vous allumer la radio " \
896
            "avec le cordon deja connecter.\n"
897
        rp.pre_upload = \
898
            "1. Allumez la radio.( sans tenir aucun bouton enfoncer )\n" \
899
            "2. Connectez le cable sur le connecteur Haut-Parleur/Micro.\n" \
900
            "3. ASSUREZ vous que le connecteur et bien enfoncer.\n" \
901
            "4. Cliquer sur OK pour envoyer l'image vers la radio.\n\n" \
902
            "Cela peut ne pas fonctionner si vous allumer la radio " \
903
            "avec le cordon deja connecter.\n"
904
        return rp
905

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

    
921
        steps = STEPS.copy()
922
        steps.sort()
923
        rf.valid_tuning_steps = steps
924

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

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

    
932
        rf.valid_skips = [""]
933

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

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

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

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

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

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

    
967
    def validate_memory(self, mem):
968
        msgs = super().validate_memory(mem)
969

    
970
        if mem.duplex == 'off':
971
            return msgs
972

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

    
981
        # find band
982
        band = self._find_band(txfreq)
983
        if band is False:
984
            msg = f"La frequence de transmission {txfreq/1000000.0:.4f}MHz " \
985
                   "n'est pas supporter par cette radio"
986
            msgs.append(chirp_common.ValidationWarning(msg))
987

    
988
        band = self._find_band(mem.freq)
989
        if band is False:
990
            msg = f"La frequence {mem.freq/1000000.0:%.4f}MHz " \
991
                   "n'est pas supporter par cette radio"
992
            msgs.append(chirp_common.ValidationWarning(msg))
993

    
994
        return msgs
995

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

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

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

    
1020
        _mem.rxcodeflag = rxmoval
1021
        _mem.txcodeflag = txmoval
1022
        _mem.rxcode = rxtoval
1023
        _mem.txcode = txtoval
1024

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

    
1031
        rx_tone = tx_tone = None
1032

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

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

    
1059
        tx_pol = txtype == 0x03 and "R" or "N"
1060
        rx_pol = rxtype == 0x03 and "R" or "N"
1061

    
1062
        chirp_common.split_tone_decode(mem, (tx_tmode, tx_tone, tx_pol),
1063
                                       (rx_tmode, rx_tone, rx_pol))
1064

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

    
1069
        mem = chirp_common.Memory()
1070

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

    
1077
        mem.number = ch_num + 1
1078

    
1079
        _mem = self._memobj.channel[ch_num]
1080

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

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

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

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

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

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

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

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

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

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

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

    
1142
            return mem
1143

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

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

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

    
1174
        # tone data
1175
        self._get_tone(mem, _mem)
1176

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

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

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

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

    
1208
        mem.extra = RadioSettingGroup("Extra", "extra")
1209

    
1210
        # BusyCL
1211
        val = RadioSettingValueBoolean(_mem.busyChLockout)
1212
        rs = RadioSetting("busyChLockout", "Busy Ch Lockout    (BusyCL)", val)
1213
        rs.set_doc('BusyCL : Si le canal est occuper, ne pas permetre la transmission TX.') 
1214
        mem.extra.append(rs)
1215

    
1216
        # Frequency reverse
1217
        val = RadioSettingValueBoolean(_mem.freq_reverse)
1218
        rs = RadioSetting("frev", "Reverse Frequencies (R)", val)
1219
        rs.set_doc('R : Reverse pour ecoute l\'entre d\'un relais et d\'un transpondeur?') 
1220
        mem.extra.append(rs)
1221

    
1222
        # PTTID
1223
        pttid = list_def(_mem.dtmf_pttid, PTTID_LIST, 0)
1224
        val = RadioSettingValueList(PTTID_LIST, None, pttid)
1225
        rs = RadioSetting("pttid", "Transmission ID (PTT ID)", val)
1226
        rs.set_doc('PTT ID : Envois code DTMF\n' + \
1227
                '* NONE : Rien est envoyer\n' + \
1228
                '* UP CODE : Envoie UPCODE lorsqu\'on passe en emision.\n' + \
1229
                '* DOWW CODE : Envoie DWCODE lorsqu\'on repasse en ecoute\n' + \
1230
                '* UP+DOWN Code : Envois UPCODE et DWCODE\n' + \
1231
                '* APOLLO QUINDAR : Envoie un bip en debut et fin d\'emission.')
1232
        mem.extra.append(rs)
1233

    
1234
        # DTMF DECODE
1235
        val = RadioSettingValueBoolean(_mem.dtmf_decode)
1236
        rs = RadioSetting("dtmfdecode", "Decodage DTMF (D Decd)", val)
1237
        if self._memobj.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
1238
            mem.extra.append(rs)
1239

    
1240
        # Scrambler
1241
        enc = list_def(_mem.scrambler, SCRAMBLER_LIST, 0)
1242
        val = RadioSettingValueList(SCRAMBLER_LIST, None, enc)
1243
        rs = RadioSetting("scrambler", "Scrambler (Scramb)", val)
1244
        rs.set_doc('Scramb : Permet le brouillage audio des transmissions.') 
1245
        mem.extra.append(rs)
1246

    
1247
        # Compander
1248
        val = RadioSettingValueList(COMPANDER_LIST, None, tmp_comp)
1249
        rs = RadioSetting("compander", "Compander (Compnd)", val)
1250
        rs.set_doc('Compnd : Comment voulez vous que Compander fonctionne sur cette frequence.') 
1251
        mem.extra.append(rs)
1252

    
1253
        val = RadioSettingValueList(SCANLIST_LIST, None, tmpscn)
1254
        rs = RadioSetting("scanlists", "Scanlists (SList)", val)
1255
        rs.set_doc('SList : Selectionner la liste desirer pour la fonction SCAN.') 
1256
        mem.extra.append(rs)
1257

    
1258
        return mem
1259

    
1260
    def set_settings(self, settings):
1261
        _mem = self._memobj
1262
        for element in settings:
1263
            if not isinstance(element, RadioSetting):
1264
                self.set_settings(element)
1265
                continue
1266

    
1267
            elname = element.get_name()
1268

    
1269
            # basic settings
1270

    
1271
            # VFO_A e80 ScreenChannel_A
1272
            if elname == "VFO_A_chn":
1273
                _mem.ScreenChannel_A = int(element.value)
1274
                if _mem.ScreenChannel_A < 200:
1275
                    _mem.MrChannel_A = _mem.ScreenChannel_A
1276
                elif _mem.ScreenChannel_A < 207:
1277
                    _mem.FreqChannel_A = _mem.ScreenChannel_A
1278
                else:
1279
                    _mem.NoaaChannel_A = _mem.ScreenChannel_A
1280

    
1281
            # VFO_B e83
1282
            elif elname == "VFO_B_chn":
1283
                _mem.ScreenChannel_B = int(element.value)
1284
                if _mem.ScreenChannel_B < 200:
1285
                    _mem.MrChannel_B = _mem.ScreenChannel_B
1286
                elif _mem.ScreenChannel_B < 207:
1287
                    _mem.FreqChannel_B = _mem.ScreenChannel_B
1288
                else:
1289
                    _mem.NoaaChannel_B = _mem.ScreenChannel_B
1290

    
1291
            # TX_VFO  channel selected A,B
1292
            elif elname == "TX_VFO":
1293
                _mem.TX_VFO = int(element.value)
1294

    
1295
            # call channel
1296
            elif elname == "call_channel":
1297
                _mem.call_channel = int(element.value)
1298

    
1299
            # squelch
1300
            elif elname == "squelch":
1301
                _mem.squelch = int(element.value)
1302

    
1303
            # TOT
1304
            elif elname == "tot":
1305
                _mem.max_talk_time = int(element.value)
1306

    
1307
            # NOAA autoscan
1308
            elif elname == "noaa_autoscan":
1309
                _mem.noaa_autoscan = int(element.value)
1310

    
1311
            # VOX
1312
            elif elname == "vox":
1313
                voxvalue = int(element.value)
1314
                _mem.vox_switch = voxvalue > 0
1315
                _mem.vox_level = (voxvalue - 1) if _mem.vox_switch else 0
1316

    
1317
            # mic gain
1318
            elif elname == "mic_gain":
1319
                _mem.mic_gain = int(element.value)
1320

    
1321
            # Channel display mode
1322
            elif elname == "channel_display_mode":
1323
                _mem.channel_display_mode = int(element.value)
1324

    
1325
            # RX Mode
1326
            elif elname == "rx_mode":
1327
                tmptxmode = int(element.value)
1328
                tmpmainvfo = _mem.TX_VFO + 1
1329
                _mem.crossband = tmpmainvfo * bool(tmptxmode & 0b10)
1330
                _mem.dual_watch = tmpmainvfo * bool(tmptxmode & 0b01)
1331

    
1332
            # Battery Save
1333
            elif elname == "battery_save":
1334
                _mem.battery_save = int(element.value)
1335

    
1336
            # Backlight auto mode
1337
            elif elname == "backlight_time":
1338
                _mem.backlight_time = int(element.value)
1339

    
1340
            # Backlight min
1341
            elif elname == "backlight_min":
1342
                _mem.backlight_min = int(element.value)
1343

    
1344
            # Backlight max
1345
            elif elname == "backlight_max":
1346
                _mem.backlight_max = int(element.value)
1347

    
1348
            # Backlight TX_RX
1349
            elif elname == "backlight_on_TX_RX":
1350
                _mem.backlight_on_TX_RX = int(element.value)
1351
            # AM_fix
1352
            elif elname == "AM_fix":
1353
                _mem.AM_fix = int(element.value)
1354

    
1355
            # mic_bar
1356
            elif elname == "mem.mic_bar":
1357
                _mem.mic_bar = int(element.value)
1358

    
1359
            # Batterie txt
1360
            elif elname == "_mem.battery_text":
1361
                _mem.battery_text = int(element.value)
1362

    
1363
            # Tail tone elimination
1364
            elif elname == "ste":
1365
                _mem.ste = int(element.value)
1366

    
1367
            # VFO Open
1368
            elif elname == "freq_mode_allowed":
1369
                _mem.freq_mode_allowed = int(element.value)
1370

    
1371
            # Beep control
1372
            elif elname == "button_beep":
1373
                _mem.button_beep = int(element.value)
1374

    
1375
            # Scan resume mode
1376
            elif elname == "scan_resume_mode":
1377
                _mem.scan_resume_mode = int(element.value)
1378

    
1379
            # Keypad lock
1380
            elif elname == "key_lock":
1381
                _mem.key_lock = int(element.value)
1382

    
1383
            # Auto keypad lock
1384
            elif elname == "auto_keypad_lock":
1385
                _mem.auto_keypad_lock = int(element.value)
1386

    
1387
            # Power on display mode
1388
            elif elname == "welcome_mode":
1389
                _mem.power_on_dispmode = int(element.value)
1390

    
1391
            # Keypad Tone
1392
            elif elname == "voice":
1393
                _mem.voice = int(element.value)
1394

    
1395
            elif elname == "s0_level":
1396
                _mem.s0_level = -int(element.value)
1397

    
1398
            elif elname == "s9_level":
1399
                _mem.s9_level = -int(element.value)
1400

    
1401
#            elif elname == "password":
1402
#                if element.value.get_value() is None or element.value == "":
1403
#                    _mem.password = 0xFFFFFFFF
1404
#                else:
1405
#                    _mem.password = int(element.value)
1406

    
1407
            # Alarm mode
1408
            elif elname == "alarm_mode":
1409
                _mem.alarm_mode = int(element.value)
1410

    
1411
            # Reminding of end of talk
1412
            elif elname == "roger_beep":
1413
                _mem.roger_beep = int(element.value)
1414

    
1415
            # Repeater tail tone elimination
1416
            elif elname == "rp_ste":
1417
                _mem.rp_ste = int(element.value)
1418

    
1419
            # Logo string 1
1420
            elif elname == "logo1":
1421
                bts = str(element.value).rstrip("\x20\xff\x00")+"\x00"*12
1422
                _mem.logo_line1 = bts[0:12]+"\x00\xff\xff\xff"
1423

    
1424
            # Logo string 2
1425
            elif elname == "logo2":
1426
                bts = str(element.value).rstrip("\x20\xff\x00")+"\x00"*12
1427
                _mem.logo_line2 = bts[0:12]+"\x00\xff\xff\xff"
1428

    
1429
            # unlock settings
1430

    
1431
            # FLOCK
1432
            elif elname == "int_flock":
1433
                _mem.int_flock = int(element.value)
1434

    
1435
            # 350TX
1436
            elif elname == "int_350tx":
1437
                _mem.int_350tx = int(element.value)
1438

    
1439
            # KILLED
1440
            elif elname == "int_KILLED":
1441
                _mem.int_KILLED = int(element.value)
1442

    
1443
            # 200TX
1444
            elif elname == "int_200tx":
1445
                _mem.int_200tx = int(element.value)
1446

    
1447
            # 500TX
1448
            elif elname == "int_500tx":
1449
                _mem.int_500tx = int(element.value)
1450

    
1451
            # 350EN
1452
            elif elname == "int_350en":
1453
                _mem.int_350en = int(element.value)
1454

    
1455
            # SCREN
1456
            elif elname == "int_scren":
1457
                _mem.int_scren = int(element.value)
1458

    
1459
            # battery type
1460
            elif elname == "Battery_type":
1461
                _mem.Battery_type = int(element.value)
1462

    
1463
            # set low_power f4hwn
1464
            elif elname == "set_low":
1465
                _mem.set_low = int(element.value)
1466

    
1467
            # set ptt f4hwn
1468
            elif elname == "set_ptt":
1469
                _mem.set_ptt = int(element.value)
1470

    
1471
            # set tot f4hwn
1472
            elif elname == "set_tot":
1473
                _mem.set_tot = int(element.value)
1474

    
1475
            # set eot f4hwn
1476
            elif elname == "set_eot":
1477
                _mem.set_eot = int(element.value)
1478

    
1479
            # set_contrast f4hwn
1480
            elif elname == "set_contrast":
1481
                _mem.set_contrast = int(element.value)
1482

    
1483
            # set inv f4hwn
1484
            elif elname == "set_inv":
1485
                _mem.set_inv = int(element.value)
1486

    
1487
            # set lck f4hwn
1488
            elif elname == "set_lck":
1489
                _mem.set_lck = int(element.value)
1490

    
1491
            # set met f4hwn
1492
            elif elname == "set_met":
1493
                _mem.set_met = int(element.value)
1494

    
1495
            # set gui f4hwn
1496
            elif elname == "set_gui":
1497
                _mem.set_gui = int(element.value)
1498
                               
1499
            # fm radio
1500
            for i in range(1, 21):
1501
                freqname = "FM_" + str(i)
1502
                if elname == freqname:
1503
                    val = str(element.value).strip()
1504
                    try:
1505
                        val2 = int(float(val)*10)
1506
                    except Exception:
1507
                        val2 = 0xffff
1508

    
1509
                    if val2 < FMMIN*10 or val2 > FMMAX*10:
1510
                        val2 = 0xffff
1511
#                        raise errors.InvalidValueError(
1512
#                                "FM radio frequency should be a value "
1513
#                                "in the range %.1f - %.1f" % (FMMIN , FMMAX))
1514
                    _mem.fmfreq[i-1] = val2
1515

    
1516
            # dtmf settings
1517
            if elname == "dtmf_side_tone":
1518
                _mem.dtmf.side_tone = int(element.value)
1519

    
1520
            elif elname == "dtmf_separate_code":
1521
                _mem.dtmf.separate_code = str(element.value)
1522

    
1523
            elif elname == "dtmf_group_call_code":
1524
                _mem.dtmf.group_call_code = element.value
1525

    
1526
            elif elname == "dtmf_decode_response":
1527
                _mem.dtmf.decode_response = int(element.value)
1528

    
1529
            elif elname == "dtmf_auto_reset_time":
1530
                _mem.dtmf.auto_reset_time = int(element.value)
1531

    
1532
            elif elname == "dtmf_preload_time":
1533
                _mem.dtmf.preload_time = int(int(element.value)/10)
1534

    
1535
            elif elname == "dtmf_first_code_persist_time":
1536
                _mem.dtmf.first_code_persist_time = int(int(element.value)/10)
1537

    
1538
            elif elname == "dtmf_hash_persist_time":
1539
                _mem.dtmf.hash_persist_time = int(int(element.value)/10)
1540

    
1541
            elif elname == "dtmf_code_persist_time":
1542
                _mem.dtmf.code_persist_time = int(int(element.value)/10)
1543

    
1544
            elif elname == "dtmf_code_interval_time":
1545
                _mem.dtmf.code_interval_time = int(int(element.value)/10)
1546

    
1547
            elif elname == "dtmf_permit_remote_kill":
1548
                _mem.dtmf.permit_remote_kill = int(element.value)
1549

    
1550
            elif elname == "dtmf_dtmf_local_code":
1551
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*3
1552
                _mem.dtmf.local_code = k[0:3]
1553

    
1554
            elif elname == "dtmf_dtmf_up_code":
1555
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*16
1556
                _mem.dtmf.up_code = k[0:16]
1557

    
1558
            elif elname == "dtmf_dtmf_down_code":
1559
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*16
1560
                _mem.dtmf.down_code = k[0:16]
1561

    
1562
            elif elname == "dtmf_kill_code":
1563
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1564
                _mem.dtmf.kill_code = k[0:5]
1565

    
1566
            elif elname == "dtmf_revive_code":
1567
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1568
                _mem.dtmf.revive_code = k[0:5]
1569

    
1570
            elif elname == "live_DTMF_decoder":
1571
                _mem.live_DTMF_decoder = int(element.value)
1572

    
1573
            # dtmf contacts
1574
            for i in range(1, 17):
1575
                varname = "DTMF_" + str(i)
1576
                if elname == varname:
1577
                    k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8
1578
                    _mem.dtmfcontact[i-1].name = k[0:8]
1579

    
1580
                varnumname = "DTMFNUM_" + str(i)
1581
                if elname == varnumname:
1582
                    k = str(element.value).rstrip("\x20\xff\x00") + "\xff"*3
1583
                    _mem.dtmfcontact[i-1].number = k[0:3]
1584

    
1585
            # scanlist stuff
1586
            if elname == "slDef":
1587
                _mem.slDef = int(element.value)
1588

    
1589
            elif elname == "sl1PriorEnab":
1590
                _mem.sl1PriorEnab = int(element.value)
1591

    
1592
            elif elname == "sl2PriorEnab":
1593
                _mem.sl2PriorEnab = int(element.value)
1594

    
1595
            elif elname in ["sl1PriorCh1", "sl1PriorCh2", "sl2PriorCh1", "sl2PriorCh2"]:
1596
                val = int(element.value)
1597

    
1598
                if val > 200 or val < 1:
1599
                    val = 0xff
1600
                else:
1601
                    val -= 1
1602

    
1603
                _mem[elname] = val
1604

    
1605
            if elname == "key1_shortpress_action":
1606
                _mem.key1_shortpress_action = KEYACTIONS_LIST.index(element.value)
1607

    
1608
            elif elname == "key1_longpress_action":
1609
                _mem.key1_longpress_action = KEYACTIONS_LIST.index(element.value)
1610

    
1611
            elif elname == "key2_shortpress_action":
1612
                _mem.key2_shortpress_action = KEYACTIONS_LIST.index(element.value)
1613

    
1614
            elif elname == "key2_longpress_action":
1615
                _mem.key2_longpress_action = KEYACTIONS_LIST.index(element.value)
1616

    
1617
            elif elname == "keyM_longpress_action":
1618
                _mem.keyM_longpress_action = KEYACTIONS_LIST.index(element.value)
1619

    
1620
# this change to send power level chan in the calibration but under macos it give error
1621
# bugfix calibration : remove the comment on next 2 line:
1622
#            elif elname == "upload_calibration":
1623
#                self._upload_calibration = bool(element.value)
1624

    
1625
            elif element.changed() and elname.startswith("_mem.cal."):
1626
                exec(elname + " = element.value.get_value()")
1627

    
1628
    def get_settings(self):
1629
        _mem = self._memobj
1630
        basic = RadioSettingGroup("basic", "Reglages de base")
1631
        advanced = RadioSettingGroup("advanced", "Reglages Avancer")
1632
        keya = RadioSettingGroup("keya", "Touche Programmable")
1633
        dtmf = RadioSettingGroup("dtmf", "Reglages DTMF")
1634
        dtmfc = RadioSettingGroup("dtmfc", "Contacts DTMF")
1635
        scanl = RadioSettingGroup("scn", "Listes de SCAN")
1636
        unlock = RadioSettingGroup("unlock", "Frequence Emission (Unlock)")
1637
        fmradio = RadioSettingGroup("fmradio", "Radio FM")
1638
        calibration = RadioSettingGroup("calibration", "Calibration")
1639
        help_user = RadioSettingGroup("help_user", "Aide d\'utilisation")
1640

    
1641
        roinfo = RadioSettingGroup("roinfo", "Information General et Lien pour mise a jour F4HWN")
1642
        top = RadioSettings()
1643
        top.append(basic)
1644
        top.append(advanced)
1645
        top.append(keya)
1646
        top.append(dtmf)
1647
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
1648
            top.append(dtmfc)
1649
        top.append(scanl)
1650
        top.append(unlock)
1651
        if _mem.BUILD_OPTIONS.ENABLE_FMRADIO:
1652
            top.append(fmradio)
1653
        top.append(roinfo)
1654
        top.append(calibration)
1655
        top.append(help_user)
1656
        
1657
        # helper function
1658
        def append_label(radio_setting, label, descr=""):
1659
            if not hasattr(append_label, 'idx'):
1660
                append_label.idx = 0
1661

    
1662
            val = RadioSettingValueString(len(descr), len(descr), descr)
1663
            val.set_mutable(False)
1664
            rs = RadioSetting("label" + str(append_label.idx), label, val)
1665
            append_label.idx += 1
1666
            radio_setting.append(rs)
1667

    
1668
        # Programmable keys
1669
        def get_action(action_num):
1670
            """"get actual key action"""
1671
            has_alarm = self._memobj.BUILD_OPTIONS.ENABLE_ALARM
1672
            has1750 = self._memobj.BUILD_OPTIONS.ENABLE_TX1750
1673
            has_flashlight = self._memobj.BUILD_OPTIONS.ENABLE_FLASHLIGHT
1674

    
1675
            has_backlight_off = self._memobj.BUILD_OPTIONS.ENABLE_BLMIN_TMP_OFF
1676
            
1677
            lst = KEYACTIONS_LIST.copy()
1678
            if not has_alarm:
1679
                lst.remove("ALARM")
1680
            if not has1750:
1681
                lst.remove("1750Hz TONE")
1682
            if not has_flashlight:
1683
                lst.remove("FLASHLIGHT")
1684
            if not has_backlight_off:
1685
                lst.remove("Eteindre l\'ecran (BLMIN TMP OFF)")
1686

    
1687
            action_num = int(action_num)
1688
            if action_num >= len(KEYACTIONS_LIST) or \
1689
               KEYACTIONS_LIST[action_num] not in lst:
1690
                action_num = 0
1691
            return lst, KEYACTIONS_LIST[action_num]
1692

    
1693
        val1s = RadioSettingValueList(*get_action(_mem.key1_shortpress_action))
1694
        rs = RadioSetting("key1_shortpress_action",
1695
                          "Fonction appuie COURT sur BL1  (F1Shrt)", val1s)
1696
        rs.set_doc('F1Shrt : Fonction que vous voulez lorsque vous faites ' + \
1697
                   'un appui COURT sur BL1 (Bouton Lateral : PTT, BL1, BL2)')
1698
        keya.append(rs)
1699

    
1700
        val1l = RadioSettingValueList(*get_action(_mem.key1_longpress_action))
1701
        rs = RadioSetting("key1_longpress_action",
1702
                          "Fonction appuie LONG sur BL1  (F1Long)", val1l)
1703
        rs.set_doc('F1Long : Fonction que vous voulez lorsque vous faites ' + \
1704
                   'un appui LONG sur BL1 (Bouton Lateral : PTT, BL1, BL2)')                          
1705
        keya.append(rs)
1706

    
1707
        val2s = RadioSettingValueList(*get_action(_mem.key2_shortpress_action))
1708
        rs = RadioSetting("key2_shortpress_action",
1709
                          "Fonction appuie COURT sur BL2  (F2Shrt)", val2s)
1710
        rs.set_doc('F2Shrt : Fonction que vous voulez lorsque vous faites ' + \
1711
                   'un appui COURT sur BL2 (Bouton Lateral : PTT, BL1, BL2) ')                          
1712
        keya.append(rs)
1713

    
1714
        val2l = RadioSettingValueList(*get_action(_mem.key2_longpress_action))
1715
        rs = RadioSetting("key2_longpress_action",
1716
                          "Fonction appuie LONG sur BL2  (F2Long)", val2l)
1717
        rs.set_doc('F2Long : Fonction que vous voulez lorsque vous faites ' + \
1718
                   'un appui LONG sur BL2 (Bouton Lateral : PTT, BL1, BL2) ')
1719
        keya.append(rs)
1720

    
1721
        valm = RadioSettingValueList(*get_action(_mem.keyM_longpress_action))
1722
        rs = RadioSetting("keyM_longpress_action",
1723
                          "Fonction appuie LONG sur [M]  (M Long)", valm)
1724
        rs.set_doc('M Long : Fonction que vous voulez lorsque vous faites ' + \
1725
                   'un appui LONG sur la touche Menu [M]  (juste sous l\'ecran a gauche)')
1726
        keya.append(rs)
1727

    
1728
        # ----------------- DTMF settings
1729
        tmpval = str(_mem.dtmf.separate_code)
1730
        if tmpval not in DTMF_CODE_CHARS:
1731
            tmpval = '*'
1732
        val = RadioSettingValueString(1, 1, tmpval)
1733
        val.set_charset(DTMF_CODE_CHARS)
1734
        sep_code_setting = RadioSetting("dtmf_separate_code", "Code de Separation", val)
1735
        sep_code_setting.set_doc('Separate Code : Caractere de separation DTMF')
1736

    
1737
        tmpval = str(_mem.dtmf.group_call_code)
1738
        if tmpval not in DTMF_CODE_CHARS:
1739
            tmpval = '#'
1740
        val = RadioSettingValueString(1, 1, tmpval)
1741
        val.set_charset(DTMF_CODE_CHARS)
1742
        group_code_setting = RadioSetting("dtmf_group_call_code", "Code appel de groupe", val)
1743
        group_code_setting.set_doc('Group Call Code : Code d\'appel de groupe DTMF')
1744

    
1745
        tmpval = min_max_def(_mem.dtmf.first_code_persist_time * 10, 30, 1000, 300)
1746
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1747
        first_code_per_setting = RadioSetting("dtmf_first_code_persist_time", "Duree 1er code DTMF (ms)", val)
1748
        first_code_per_setting.set_doc('First code persist time : Duree d\'emission en milliseconde du 1er code DTMF.')
1749

    
1750
        tmpval = min_max_def(_mem.dtmf.hash_persist_time * 10, 30, 1000, 300)
1751
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1752
        spec_per_setting = RadioSetting("dtmf_hash_persist_time", "Duree des codes #/* (ms)", val)
1753
        spec_per_setting.set_doc('#/* persist time : Duree d\'emission des codes DTMF "#" ou "/" ou "*" en milliseconde ')
1754
        
1755
        tmpval = min_max_def(_mem.dtmf.code_persist_time * 10, 30, 1000, 300)
1756
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1757
        code_per_setting = RadioSetting("dtmf_code_persist_time", "Duree Code (ms)", val)
1758
        code_per_setting.set_doc('Code persist time : Duree en milliseconde d\'envoie d\'un code DTMF.')
1759

    
1760
        tmpval = min_max_def(_mem.dtmf.code_interval_time * 10, 30, 1000, 300)
1761
        val = RadioSettingValueInteger(30, 1000, tmpval, 10)
1762
        code_int_setting = RadioSetting("dtmf_code_interval_time", "Intervale de temps (ms)", val)
1763
        code_int_setting.set_doc('Code interval time : Temps de pause en milliseconde entre chaque code DTMF. ')
1764

    
1765
        tmpval = str(_mem.dtmf.local_code).upper().strip("\x00\xff\x20")
1766
        for i in tmpval:
1767
            if i in DTMF_CHARS_ID:
1768
                continue
1769
            tmpval = "103"
1770
            break
1771

    
1772
        val = RadioSettingValueString(3, 3, tmpval)
1773
        val.set_charset(DTMF_CHARS_ID)
1774
        ani_id_setting = RadioSetting("dtmf_dtmf_local_code", "Code DTMF de la radio (3 characteres 0-9 ABCD)  (ANI ID)", val)
1775
        ani_id_setting.set_doc('ANI ID : Code DTMF d\'identification de la radio.')                   
1776

    
1777
        tmpval = str(_mem.dtmf.up_code).upper().strip("\x00\xff\x20")
1778
        for i in tmpval:
1779
            if i in DTMF_CHARS_UPDOWN or i == "":
1780
                continue
1781
            else:
1782
                tmpval = "123"
1783
                break
1784
        val = RadioSettingValueString(1, 16, tmpval)
1785
        val.set_charset(DTMF_CHARS_UPDOWN)
1786
        up_code_setting = RadioSetting("dtmf_dtmf_up_code", "Code UP (1-16 chars 0-9 ABCD*#)  (UPCode)", val)
1787
        up_code_setting.set_doc('UPCode : Code DTMF qui sera envoyer en debut de transmission.')
1788

    
1789
        tmpval = str(_mem.dtmf.down_code).upper().strip(
1790
                "\x00\xff\x20")
1791
        for i in tmpval:
1792
            if i in DTMF_CHARS_UPDOWN:
1793
                continue
1794
            else:
1795
                tmpval = "456"
1796
                break
1797
        val = RadioSettingValueString(1, 16, tmpval)
1798
        val.set_charset(DTMF_CHARS_UPDOWN)
1799
        dw_code_setting = \
1800
            RadioSetting("dtmf_dtmf_down_code",
1801
                         "Code DOWN (1-16 chars 0-9 ABCD*#)  (DWCode)", val)
1802
        dw_code_setting.set_doc('DWCode : Code DTMF qui sera envoyer en fin de transmission (lorsque on relache PTT).')
1803

    
1804
        val = RadioSettingValueBoolean(_mem.dtmf.side_tone)
1805
        dtmf_side_tone_setting = \
1806
            RadioSetting("dtmf_side_tone",
1807
                         "Ecoute DTMF  (D ST)", val)
1808
        dtmf_side_tone_setting.set_doc('D ST : Active/Desactive l\'ecoute dans le Haut-Parleur des codes DTMF ' + \
1809
                                       ' durant leurs emissions.')
1810

    
1811
        tmpval = list_def(_mem.dtmf.decode_response,
1812
                          DTMF_DECODE_RESPONSE_LIST, 0)
1813
        val = RadioSettingValueList(DTMF_DECODE_RESPONSE_LIST, None, tmpval)
1814
        dtmf_resp_setting = RadioSetting("dtmf_decode_response",
1815
                                         "Decode Response  (D Resp)", val)
1816
        dtmf_resp_setting.set_doc('D Resp : Decodage DTMF de la reponse.')
1817

    
1818
        tmpval = min_max_def(_mem.dtmf.auto_reset_time, 5, 60, 10)
1819
        val = RadioSettingValueInteger(5, 60, tmpval)
1820
        d_hold_setting = RadioSetting("dtmf_auto_reset_time",
1821
                                      "Duree d\'affichage DTMF  (D Hold)", val)
1822
        d_hold_setting.set_doc('D Hold : Duree (en seconde) d\'affichage des codes DTMF a leur reception.')
1823

    
1824
        # D Prel
1825
        tmpval = min_max_def(_mem.dtmf.preload_time * 10, 30, 990, 300)
1826
        val = RadioSettingValueInteger(30, 990, tmpval, 10)
1827
        d_prel_setting = RadioSetting("dtmf_preload_time",
1828
                                      "DTMF Pre-load (ms)  (D Prel)", val)
1829
        d_prel_setting.set_doc('D Prel : Duree d\'attente avant l\'envoie du ou des code(s) DTMF\n' + \
1830
                            'lorsqu\'on passe en emission (Appuie sur PTT).')
1831
        
1832
        # D LIVE
1833
        val = RadioSettingValueBoolean(_mem.live_DTMF_decoder)
1834
        d_live_setting = \
1835
            RadioSetting("live_DTMF_decoder", "Affiche les codes DTMF a l'ecran  (D Live)", val)
1836
        d_live_setting.set_doc('D Live : Active/Desactive l\'affichage des codes DTMF recu et decoder.')
1837
        
1838
        val = RadioSettingValueBoolean(_mem.dtmf.permit_remote_kill)
1839
        perm_kill_setting = RadioSetting("dtmf_permit_remote_kill",
1840
                                         "Permit remote kill", val)
1841

    
1842
        tmpval = str(_mem.dtmf.kill_code).upper().strip(
1843
                "\x00\xff\x20")
1844
        for i in tmpval:
1845
            if i in DTMF_CHARS_KILL:
1846
                continue
1847
            else:
1848
                tmpval = "77777"
1849
                break
1850
        if not len(tmpval) == 5:
1851
            tmpval = "77777"
1852
        val = RadioSettingValueString(5, 5, tmpval)
1853
        val.set_charset(DTMF_CHARS_KILL)
1854
        kill_code_setting = RadioSetting("dtmf_kill_code",
1855
                                         "Kill codes (5 chars 0-9 ABCD)", val)
1856

    
1857
        tmpval = str(_mem.dtmf.revive_code).upper().strip(
1858
                "\x00\xff\x20")
1859
        for i in tmpval:
1860
            if i in DTMF_CHARS_KILL:
1861
                continue
1862
            else:
1863
                tmpval = "88888"
1864
                break
1865
        if not len(tmpval) == 5:
1866
            tmpval = "88888"
1867
        val = RadioSettingValueString(5, 5, tmpval)
1868
        val.set_charset(DTMF_CHARS_KILL)
1869
        rev_code_setting = RadioSetting("dtmf_revive_code", "Revive code (5 chars 0-9 ABCD)", val)
1870

    
1871
        val = RadioSettingValueBoolean(_mem.int_KILLED)
1872
        killed_setting = RadioSetting("int_KILLED", "DTMF kill lock", val)
1873

    
1874
        # ----------------- DTMF Contacts
1875
        append_label(dtmfc, "Liste des contacts DTMF  (D List)",
1876
                     "Liste les contacts DTMF (3 caracteres) "
1877
                     "(valide : 0-9 * # ABCD), "
1878
                     "ou une chaine vide")
1879

    
1880
        for i in range(1, 17):
1881
            varname = "DTMF_"+str(i)
1882
            varnumname = "DTMFNUM_"+str(i)
1883
            vardescr = "DTMF Contact "+str(i)+" name"
1884
            varinumdescr = "DTMF Contact "+str(i)+" number"
1885

    
1886
            cntn = str(_mem.dtmfcontact[i-1].name).strip("\x20\x00\xff")
1887
            cntnum = str(_mem.dtmfcontact[i-1].number).strip("\x20\x00\xff")
1888

    
1889
            val = RadioSettingValueString(0, 8, cntn)
1890
            rs = RadioSetting(varname, vardescr, val)
1891
            dtmfc.append(rs)
1892

    
1893
            val = RadioSettingValueString(0, 3, cntnum)
1894
            val.set_charset(DTMF_CHARS)
1895
            rs = RadioSetting(varnumname, varinumdescr, val)
1896
            dtmfc.append(rs)
1897

    
1898
        # ----------------- Scan Lists
1899

    
1900
        tmpscanl = list_def(_mem.slDef, SCANLIST_SELECT_LIST, 0)
1901
        val = RadioSettingValueList(SCANLIST_SELECT_LIST, None, tmpscanl)
1902
        rs = RadioSetting("slDef", "Selection Liste SCAN  (SList)", val)
1903
        rs.set_doc('SList : Selectionnez quel liste sera utilise pour le mode SCAN Memoire\n' + \
1904
                    '* ALL : Toutes les memoires des 2 LIST\n' + \
1905
                    '* LIST1 : Toutes les memoires de la LIST 1\n' + \
1906
                    '* LIST2 : Toutes les memoires de la LIST 2')
1907
        scanl.append(rs)
1908

    
1909
        val = RadioSettingValueBoolean(_mem.sl1PriorEnab)
1910
        rs = RadioSetting("sl1PriorEnab", "List 1 prioriter channel scan", val)
1911
        rs.set_doc('List 1 prioriter channel scan : Es-ce que cette liste est prioritaire')
1912
        scanl.append(rs)
1913

    
1914
        ch_list = ["None"]
1915
        for ch in range(1, 201):
1916
            ch_list.append("Channel M" + str(ch))
1917

    
1918
        tmpch = list_def(_mem.sl1PriorCh1 + 1, ch_list, 0)
1919
        val = RadioSettingValueList(ch_list, None, tmpch)
1920
        rs = RadioSetting("sl1PriorCh1", "List 1 prioriter channel 1", val)
1921
        rs.set_doc('List 1 prioriter channel 1 : Selectionner une memoire qui sera prioritaire dans la LIST 1.')
1922
        scanl.append(rs)
1923

    
1924
        tmpch = list_def(_mem.sl1PriorCh2 + 1, ch_list, 0)
1925
        val = RadioSettingValueList(ch_list, None, tmpch)
1926
        rs = RadioSetting("sl1PriorCh2", "List 1 prioriter channel 2", val)
1927
        rs.set_doc('List 1 prioriter channel 2 : Selectionner une memoire qui sera prioritaire dans la LIST 1')
1928
        scanl.append(rs)
1929

    
1930
        val = RadioSettingValueBoolean(_mem.sl2PriorEnab)
1931
        rs = RadioSetting("sl2PriorEnab", "List 2 prioriter channel scan", val)
1932
        rs.set_doc('List 2 prioriter channel scan : Es-ce que cette liste est prioritaire')
1933
        scanl.append(rs)
1934

    
1935
        tmpch = list_def(_mem.sl2PriorCh1 + 1, ch_list, 0)
1936
        val = RadioSettingValueList(ch_list, None, tmpch)
1937
        rs = RadioSetting("sl2PriorCh1", "List 2 prioriter channel 1", val)
1938
        rs.set_doc('List 2 prioriter channel 1 : Selectionner une memoire qui sera prioritaire dans la LIST 2')
1939
        scanl.append(rs)
1940

    
1941
        tmpch = list_def(_mem.sl2PriorCh2 + 1, ch_list, 0)
1942
        val = RadioSettingValueList(ch_list, None, tmpch)
1943
        rs = RadioSetting("sl2PriorCh2", "List 2 prioriter channel 2", val)
1944
        rs.set_doc('List 2 prioriter channel 2 : Selectionner une memoire qui sera prioritaire dans la LIST 2')
1945
        scanl.append(rs)
1946

    
1947
        # ----------------- Basic settings
1948

    
1949
        ch_list = []
1950
        for ch in range(1, 201):
1951
            ch_list.append("Memoire M" + str(ch))
1952
        for bnd in range(1, 8):
1953
            ch_list.append("Bande F" + str(bnd))
1954
        if _mem.BUILD_OPTIONS.ENABLE_NOAA:
1955
            for bnd in range(1, 11):
1956
                ch_list.append("NOAA N" + str(bnd))
1957

    
1958
        tmpfreq0 = list_def(_mem.ScreenChannel_A, ch_list, 0)
1959
        val = RadioSettingValueList(ch_list, None, tmpfreq0)
1960
        freq0_setting = RadioSetting("VFO_A_chn",
1961
                                     "Reglage courant du VFO A", val)
1962
        freq0_setting.set_doc('VFO A reglage courant Memoire/Bande : Selectionner ce qui est afficher ' + \
1963
                              'sur le VFO A (Haut) a l\'allumage de la radio \n' + \
1964
                              '* MEMOIRE : M1 a M200\n' + \
1965
                              '* BANDE : F1-F7.\n' + \
1966
                              'Voir l\'onglet des memoires pour determiner quel memoires sont deja programmer.')
1967

    
1968
        tmpfreq1 = list_def(_mem.ScreenChannel_B, ch_list, 0)
1969
        val = RadioSettingValueList(ch_list, None, tmpfreq1)
1970
        freq1_setting = RadioSetting("VFO_B_chn",
1971
                                     "Reglage courant du VFO B", val)
1972
        freq1_setting.set_doc('VFO B reglage courant Memoire/Bande: Selectionner ce qui est afficher ' + \
1973
                              'sur le VFO B (Bas) a l\'allumage de la radio \n' + \
1974
                              '* MEMOIRE : M1 a M200\n' + \
1975
                              '* BANDE : F1-F7.\n' + \
1976
                              'Voir l\'onglet des memoires pour determiner quel memoires sont deja programmer.')
1977

    
1978
        tmptxvfo = list_def(_mem.TX_VFO, TX_VFO_LIST, 0)
1979
        val = RadioSettingValueList(TX_VFO_LIST, None, tmptxvfo)
1980
        tx_vfo_setting = RadioSetting("TX_VFO", "Main VFO", val)
1981
        tx_vfo_setting.set_doc('VFO Courant: Selectionner quel VFO est selectionner.\n' + \
1982
                               'En haut VFO A\nEn bas VFO B ')
1983
        val = RadioSettingValueBoolean(False)
1984

    
1985
        def validate_upload_f4hwn(value):
1986
            return value
1987

    
1988
        val.set_validate_callback(validate_upload_f4hwn)
1989
        Upload_f4hwn = RadioSetting("upload_f4hwn",
1990
                                     "Pour envoyer uniquement les reglages de F4HWN ", val)
1991
        Upload_f4hwn.set_doc('Reglage F4HWN : Cette section regroupe les options et reglages du firmware de F4HWN. ' + \
1992
                             'Il faut les envoyer separement a la radio. Cocher la case pour ' + \
1993
                             'envoyer SEULEMENT et SEULEMENT la sections de reglages de F4HWN vers la radio. ' + \
1994
                             'Le transfert est tres rapide et une fois terminer, la radio redemarrera. ' + \
1995
                             'Apres l\'envois, decocher la case pour envoyer tout les autres reglages ')
1996
        self.upload_f4hwn = val
1997
                                     
1998
        # Set_Low_Power f4hwn
1999
        tmpsetlow = list_def(_mem.set_low, SET_LOW_LIST, 0)
2000
        val = RadioSettingValueList(SET_LOW_LIST, SET_LOW_LIST[tmpsetlow])
2001
        SetLowSetting = RadioSetting("set_low", "Puissance LOW  (SetLow)", val)
2002
        SetLowSetting.set_doc('SetLow : Puissance utiliser lorsque (TxPwr) est regler sur LOW.\n' + \
2003
                              'Voir aussi dans l\'onglet Memoires dans la colonne POWER pour connaitre la ' +\
2004
                              'puissance utiliser pour chaque memoire.\n' + \
2005
                              'Le niveau LOW utilisera cette puissance.')
2006
        
2007
        # Set_Ptt f4hwn
2008
        tmpsetptt = list_def(_mem.set_ptt, SET_PTT_LIST, 0)
2009
        val = RadioSettingValueList(SET_PTT_LIST, SET_PTT_LIST[tmpsetptt])
2010
        SetPttSetting = RadioSetting("set_ptt", "Mode Boutton PTT  (SetPtt)", val)
2011
        SetPttSetting.set_doc('SetPtt :\n' + \
2012
                              '* CLASSIC : Appuie = Passe en emission, RELACHE = Revient en reception.\n' + \
2013
                              '* ONEPUSH : Appuie puis relache = Passe en emission, \n' + \
2014
                              '                       Appuie puis relache = Passe en reception.\n' + \
2015
                              ' Terminer les crampes au doigt durant l\'appuie sur le PTT :)... ')
2016

    
2017
        # Set_tot f4hwn
2018
        tmpsettot = list_def(_mem.set_tot, SET_TOT_EOT_LIST, 0)
2019
        val = RadioSettingValueList(SET_TOT_EOT_LIST, SET_TOT_EOT_LIST[tmpsettot])
2020
        SetTotSetting = RadioSetting("set_tot", "Fonction indicateur de l\'Anti-Bavard  (SetTot)", val)
2021
        SetTotSetting.set_doc('SetTot : Indicateur de fin de l\'anti-bavard\n' + \
2022
                                '* NONE : Aucune indication\n' + \
2023
                                '* SOUND : indication sonore\n' + \
2024
                                '* VISUAL : Fait clignoter l\'ecran\n' + \
2025
                                '* ALL : Indicateur sonore et clignotement de l\'ecran.')
2026

    
2027
        # Set_eot f4hwn
2028
        tmpseteot = list_def(_mem.set_eot, SET_TOT_EOT_LIST, 0)
2029
        val = RadioSettingValueList(SET_TOT_EOT_LIST, SET_TOT_EOT_LIST[tmpseteot])
2030
        SetEotSetting = RadioSetting("set_eot", "Indicateur de fin de transmission  (SetEot)", val)
2031
        SetEotSetting.set_doc('SetEot : Affiche un indicateur de fin d\'emission\n' + \
2032
                                '* NONE : Aucune indication\n' + \
2033
                                '* SOUND : Indication sonore\n' + \
2034
                                '* VISUAL : Fait clignote l\'ecran\n' + \
2035
                                '* ALL : Indicateur sonore et clignotement de l\'ecran.')
2036

    
2037
        # Set_contrast f4hwn
2038
        tmpcontrast = min_max_def(_mem.set_contrast, 0, 15, 11)
2039
        val = RadioSettingValueInteger(0, 15, tmpcontrast)
2040
        contrastSetting = RadioSetting("set_contrast", "Reglage niveau contrast  (SetCtr)", val)
2041
        contrastSetting.set_doc('SetCtr : Permet de regler le niveau de contrast (0 a 15)\nDefaut: 10 ')
2042
       
2043
        # Set_inv f4hwn
2044
        tmpsetinv = list_def(_mem.set_inv, SET_OFF_ON_LIST, 0)
2045
        val = RadioSettingValueList(SET_OFF_ON_LIST, SET_OFF_ON_LIST[tmpsetinv])
2046
        SetInvSetting = RadioSetting("set_inv", "Inversion affichage  (SetInv)", val)
2047
        SetInvSetting.set_doc('SetInv : Active/Desactive l\'affichage inverse.\n' + \
2048
                            'Ecriture Noir sur Blanc OU \nEcriture Blanc sur noir.\n' + \
2049
                            'Defaut : Noir sur Blanc ')
2050

    
2051
        # Set_lck, uses
2052
        tmpsetlck = list_def(_mem.set_lck, SET_LCK_LIST, 0)
2053
        val = RadioSettingValueList(SET_LCK_LIST, SET_LCK_LIST[tmpsetlck])
2054
        SetLckSetting = RadioSetting("set_lck", "Verouillage PTT avec verouillage Clavier  (SetLck)", val)
2055
        SetLckSetting.set_doc('SetLck : Lorsque le clavier est verouiller, \n' + \
2056
                              'vous pouvez aussi verouiller le boutton PTT \n' + \
2057
                              'en meme temp si vous le desirer. ')
2058
        
2059
        # Set_met f4hwn
2060
        tmpsetmet = list_def(_mem.set_met, SET_MET_LIST, 0)
2061
        val = RadioSettingValueList(SET_MET_LIST, SET_MET_LIST[tmpsetmet])
2062
        SetMetSetting = RadioSetting("set_met", "Style d\'affichage du S-Metre  (SetMet)", val)
2063
        SetMetSetting.set_doc('SetMet : Change le style d\'affichage du S-Metre entre CLASSIC (Egzumer) et TINY (F4HWN) ')
2064

    
2065
        # Set_gui f4hwn
2066
        tmpsetgui = list_def(_mem.set_gui, SET_MET_LIST, 0)
2067
        val = RadioSettingValueList(SET_MET_LIST, SET_MET_LIST[tmpsetgui])
2068
        SetGuiSetting = RadioSetting("set_gui", "Style Affichage General  (SetGui)", val)
2069
        SetGuiSetting.set_doc('SetGui : Change le style d\'affichage principal entre CLASSIC (Grand) et TINY (Petit) ')
2070

    
2071
        tmpsq = min_max_def(_mem.squelch, 0, 9, 1)
2072
        val = RadioSettingValueInteger(0, 9, tmpsq)
2073
        squelch_setting = RadioSetting("squelch", "Squelch  (Sql)", val)
2074
        squelch_setting.set_doc('Sql : Reglage la sensibilite du Squelch.\nNiveau de 0 a 9\n0 = On\'utilise plus le squelch. ')
2075
        
2076
        ch_list = []
2077
        for ch in range(1, 201):
2078
            ch_list.append("Channel M" + str(ch))
2079

    
2080
        tmpc = list_def(_mem.call_channel, ch_list, 0)
2081
        val = RadioSettingValueList(ch_list, None, tmpc)
2082
        call_channel_setting = RadioSetting("call_channel", "Memoire Rapide  (1 Call)", val)
2083
        call_channel_setting.set_doc('1 Call : Permet de definir une memoire pour rappel rapide avec une seule touche.\n' + \
2084
                                    'L\'appel est fait avec [F] puis [9] ou appuie LONG sur [9]')
2085

    
2086
        val = RadioSettingValueBoolean(_mem.key_lock)
2087
        keypad_lock_setting = RadioSetting("key_lock", "Verouillage Clavier  (KeyLck)", val)
2088
        keypad_lock_setting.set_doc('Keypad locked : Verouillage du clavier maintenant.')
2089
        
2090
        tmpval = list_def(_mem.auto_keypad_lock, AUTO_KEYPAD_LOCK_LIST, 1)
2091
        val = RadioSettingValueList(AUTO_KEYPAD_LOCK_LIST, None, tmpval)
2092
        auto_keypad_lock_setting = RadioSetting("auto_keypad_lock",
2093
                         "Verouillage auto. du Clavier  (KeyLck)", val)
2094
        auto_keypad_lock_setting.set_doc('KeyLck : Active/Desactive le verouillage automatique du clavier \n' + \
2095
                                         '* AUTO : Apres un temps d\'inactiviter ~15sec. \n' + \
2096
                                         '* OFF : Pas de verouillage automatique.')
2097
        
2098
        tmptot = list_def(_mem.max_talk_time,  TALK_TIME_LIST, 1)
2099
        val = RadioSettingValueList(TALK_TIME_LIST, None, tmptot)
2100
        tx_t_out_setting = RadioSetting("tot", "Duree max. d\'emission  (TxTOut)", val)
2101
        tx_t_out_setting.set_doc('TxTOut : Selectionne le temps maximal d\'emission (Anti-Bavard).\n' + \
2102
                                    'Un fois ce temps ecouler, la radio repasse automatiquement en reception.\n' + \
2103
                                    'Voir aussi l\'option (SetTot) de F4HWN')                                        
2104

    
2105
        tmpbatsave = list_def(_mem.battery_save, BATSAVE_LIST, 4)
2106
        val = RadioSettingValueList(BATSAVE_LIST, None, tmpbatsave)
2107
        bat_save_setting = RadioSetting("battery_save",
2108
                                        "Economie Batterie  (BatSav)", val)
2109
        bat_save_setting.set_doc('BatSav : Option d\'economie Batterie. Un ratio est definie enter le temps d\'activiter de la radio ' + \
2110
                                ' et le temps de repos.')
2111
        
2112
        val = RadioSettingValueBoolean(_mem.noaa_autoscan)
2113
        noaa_auto_scan_setting = RadioSetting("noaa_autoscan",
2114
                                              "Autoscan NOAA  (NOAA-S)", val)
2115
        noaa_auto_scan_setting.set_doc('NOAA-S : ')
2116
        
2117
        tmpmicgain = list_def(_mem.mic_gain, MIC_GAIN_LIST, 2)
2118
        val = RadioSettingValueList(MIC_GAIN_LIST, None, tmpmicgain)
2119
        mic_gain_setting = RadioSetting("mic_gain", "Gain Micro  (Mic)", val)
2120
        mic_gain_setting.set_doc('Mic : Selectionner le gain du Micro')
2121
        
2122
        val = RadioSettingValueBoolean(_mem.mic_bar)
2123
        mic_bar_setting = RadioSetting("mic_bar", "Affiche le Niveau du Micro  (MicBar)", val)
2124
        mic_bar_setting.set_doc('MicBar : Active/Desactive l\'affichage d\'un barre graph durant l\'emission.')
2125

    
2126
        tmpchdispmode = list_def(_mem.channel_display_mode,
2127
                                 CHANNELDISP_LIST, 0)
2128
        val = RadioSettingValueList(CHANNELDISP_LIST, None, tmpchdispmode)
2129
        ch_disp_setting = RadioSetting("channel_display_mode", "Affichage en mode Memoire  (ChDisp)", val)
2130
        ch_disp_setting.set_doc('ChDisp : Definie les informations des memoires afficher en mode memoires.\n' + \
2131
                                '* Numero memoire\n' + \
2132
                                '* Le nom de la memoire\n' + \
2133
                                '* Numero et Nom de la memoire')
2134

    
2135
        tmpdispmode = list_def(_mem.power_on_dispmode, WELCOME_LIST, 0)
2136
        val = RadioSettingValueList(WELCOME_LIST, None, tmpdispmode)
2137
        p_on_msg_setting = RadioSetting("welcome_mode", "Message au Demarrage  (POnMsg)", val)
2138
        p_on_msg_setting.set_doc('POnMsg : Definie le message affiche lorsque on allume la radio. \n' + \
2139
                                 '* ALL : VOTRE Message 1 + Tension de la batterie + Son\n' + \
2140
                                 '* SOUND : bip bip 2 fois\n' + \
2141
                                 '* MESSAGE : VOTRE Message 1 et Message 2.\n' + \
2142
                                 '* VOLTAGE : Tension de la batterie\n' + \
2143
                                 '* NONE : Rien ')
2144

    
2145
        logo1 = str(_mem.logo_line1).strip("\x20\x00\xff") + "\x00"
2146
        logo1 = _getstring(logo1.encode('ascii', errors='ignore'), 0, 12)
2147
        val = RadioSettingValueString(0, 12, logo1)
2148
        logo1_setting = RadioSetting("logo1", "Message 1 ( MAX 12 characteres ) ", val)
2149
        logo1_setting.set_doc('Message 1 : Message qui s\'affiche au demarrage de la radio, ' + \
2150
                              'Ce message ne doit pas depasser 12 characteres.\n' + \
2151
                              'Voir l\'option (POnMsg) pour son affichage')
2152

    
2153
        logo2 = str(_mem.logo_line2).strip("\x20\x00\xff") + "\x00"
2154
        logo2 = _getstring(logo2.encode('ascii', errors='ignore'), 0, 12)
2155
        val = RadioSettingValueString(0, 12, logo2)
2156
        logo2_setting = RadioSetting("logo2", "Message 2 ( MAX 12 characteres )", val)
2157
        logo2_setting.set_doc('Message 2 : Second message qui est afficher au demarrage de la radio.\n' + \
2158
                              'Ce message ne doit pas depasser 12 characteres.\n' + \
2159
                              'Voir l\'option (POnMsg) pour son affichage')
2160

    
2161
        tmpbattxt = list_def(_mem.battery_text, BAT_TXT_LIST, 2)
2162
        val = RadioSettingValueList(BAT_TXT_LIST, None, tmpbattxt)
2163
        bat_txt_setting = RadioSetting("battery_text", "Texte Batterie  (BatTXT) ", val)
2164
        bat_txt_setting.set_doc('BatTXT : Texte en clair affiche a cote du symbole de la batterie (en haut a droite)\n' + \
2165
                                    'Affichage en pourcentage (%) ou en Volt (v)')
2166

    
2167
        tmpback = list_def(_mem.backlight_time, BACKLIGHT_LIST, 0)
2168
        val = RadioSettingValueList(BACKLIGHT_LIST, None, tmpback)
2169
        back_lt_setting = RadioSetting("backlight_time", "Extinction automatique  (BackLt)", val)
2170
        back_lt_setting.set_doc('BackLt : Duree d\'allumage de l\'ecran avant son extinction automatique apres ' + \
2171
                                'la derniere action (clavier ou boutton) ou allumage avec l\'option [BltTRX].')
2172

    
2173
        tmpback = list_def(_mem.backlight_min, BACKLIGHT_LVL_LIST, 0)
2174
        val = RadioSettingValueList(BACKLIGHT_LVL_LIST, None, tmpback)
2175
        bl_min_setting = RadioSetting("backlight_min", "Niveau Minimun du retroeclairage  (BLMin)", val)
2176
        bl_min_setting.set_doc('BLMin : Lorsque le retroeclairage passe a OFF, cette valeur lui est donnee.')
2177

    
2178
        tmpback = list_def(_mem.backlight_max, BACKLIGHT_LVL_LIST, 10)
2179
        val = RadioSettingValueList(BACKLIGHT_LVL_LIST, None, tmpback)
2180
        bl_max_setting = RadioSetting("backlight_max", "Niveau Maximun du retroeclairage  (BLMax)", val)
2181
        bl_max_setting.set_doc('BLMax : Lorsque le retroeclairage s\'allume, cette valeur lui est donnee. ')
2182
        
2183
        tmpback = list_def(_mem.backlight_on_TX_RX, BACKLIGHT_TX_RX_LIST, 0)
2184
        val = RadioSettingValueList(BACKLIGHT_TX_RX_LIST, None, tmpback)
2185
        blt_trx_setting = RadioSetting("backlight_on_TX_RX", "Retroeclairage TRX  (BltTRX)", val)
2186
        blt_trx_setting.set_doc('BltTRX : Gestion retroeclairage en Emission/Reception\n' + \
2187
                                '* OFF : Reste eteint\n' + \
2188
                                '* TX : s\'allumage au passage en emission\n' + \
2189
                                '* RX : s\'allume a l\'ouverture du squelch (reception)\n' + \
2190
                                '* TX/RX : s\'allume au passage en emission et a l\'ouverture du squelch (reception)')
2191
        
2192
        val = RadioSettingValueBoolean(_mem.button_beep)
2193
        beep_setting = RadioSetting("button_beep", "Bip Clavier  (Beep)", val)
2194
        beep_setting.set_doc('Beep : Bip sonnore lorsqu\'on appuie sur une touche ou un bouton.')
2195

    
2196
        tmpalarmmode = list_def(_mem.roger_beep, ROGER_LIST, 0)
2197
        val = RadioSettingValueList(ROGER_LIST, None, tmpalarmmode)
2198
        roger_setting = RadioSetting("roger_beep", "Bip de fin d\'emssion  (Roger)", val)
2199
        roger_setting.set_doc('Roger : Bip de fin d\'emission qui est entendu par le correspondant. ')
2200

    
2201
        val = RadioSettingValueBoolean(_mem.ste)
2202
        ste_setting = RadioSetting("ste", "Squelch tail elimination  (STE)", val)
2203
        ste_setting.set_doc('STE : Active/Desactive l\'elimination de bruit d\'ouverture de Squelch en fin d\'emission. ')
2204

    
2205
        tmprte = list_def(_mem.rp_ste, RTE_LIST, 0)
2206
        val = RadioSettingValueList(RTE_LIST, None, tmprte)
2207
        rp_ste_setting = RadioSetting("rp_ste", "Repeater squelch tail elimination  (RP STE)", val)
2208
        rp_ste_setting.set_doc('RP STE : Repeater squelch tail eliminator ')
2209

    
2210
        val = RadioSettingValueBoolean(_mem.AM_fix)
2211
        am_fix_setting = RadioSetting("AM_fix", "Fixe Reception AM  (AM Fix)", val)
2212
        am_fix_setting.set_doc('AM Fix : Active/Desactive l\'autogain en reception AM.\n' + \
2213
                                'Devrait toujours etre sur ON.')
2214

    
2215
        tmpvox = min_max_def((_mem.vox_level + 1) * _mem.vox_switch, 0, 10, 0)
2216
        val = RadioSettingValueList(VOX_LIST, None, tmpvox)
2217
        vox_setting = RadioSetting("vox", "Fonction VOX  (VOX)", val)
2218
        vox_setting.set_doc('VOX : regle le niveau de detection pour la fonction VOX.')
2219
        
2220
        tmprxmode = list_def((bool(_mem.crossband) << 1) + bool(_mem.dual_watch), RXMODE_LIST, 0)
2221
        val = RadioSettingValueList(RXMODE_LIST, None, tmprxmode)
2222
        rx_mode_setting = RadioSetting("rx_mode", "Mode Reception  (RxMode)", val)
2223
        rx_mode_setting.set_doc('RxMode :\n' + \
2224
                                '* MAIN ONLY : Emission et reception sur un seul VFO.\n' + \
2225
                                '* DUAL RX RESPOND : Ecoute les 2 VFOs. Si un signal est recu sur le second VFO, celui qui' + \
2226
                                'n\'est pas selectionner, on bascule sur ce VFO quelques secondes. Vous passez en emission. (Ligne Status DWR)\n' + \
2227
                                '* CROSS BAND : l\'emission est TOUJOURS sur le VFO du HAUT (A) et la transmission TOUJOURS sur le VFO du BAS (B) (Ligne Status XB)\n' + \
2228
                                '* MAIN TX DUAL RX : Transmission TOUJOURS sur le VFO du HAUT et ecoute des 2 VFOS (Ligne Status DW). ')
2229

    
2230
        val = RadioSettingValueBoolean(_mem.freq_mode_allowed)
2231
        freq_mode_allowed_setting = RadioSetting("freq_mode_allowed", "Frequency mode allowed", val)
2232
        freq_mode_allowed_setting.set_doc('Frequence mode autoriser ')
2233

    
2234
        tmpscanres = list_def(_mem.scan_resume_mode, SCANRESUME_LIST, 0)
2235
        val = RadioSettingValueList(SCANRESUME_LIST, None, tmpscanres)
2236
        scn_rev_setting = RadioSetting("scan_resume_mode", "Mode reprise SCAN  (ScnRev)", val)
2237
        scn_rev_setting.set_doc('ScnRev : Option possible\n' + \
2238
                                '* CARRIER : Reprend le SCAN apres disparition du signal (Le squelch se ferme)\n' + \
2239
                                '* TIMEOUT : Reprend le SCAN apres 5 seconds d\'ecoute.\n' + \
2240
                                '* STOP : Ne reprend jamais le SCAN. Reste sur la memoire ou la Frequence.')
2241

    
2242
        tmpvoice = list_def(_mem.voice, VOICE_LIST, 0)
2243
        val = RadioSettingValueList(VOICE_LIST, None, tmpvoice)
2244
        voice_setting = RadioSetting("voice", "Voice", val)
2245
        voice_setting.set_doc('Voice : Active les annonces vocale :\n' + \
2246
                                '* NONE : Desactive les annonces\n' + \
2247
                                '* CHINA : Annonces en Chinois.\n' + \
2248
                                '* ENGLISH : Annonces en Anglais')
2249

    
2250
        tmpalarmmode = list_def(_mem.alarm_mode, ALARMMODE_LIST, 0)
2251
        val = RadioSettingValueList(ALARMMODE_LIST, None, tmpalarmmode)
2252
        alarm_setting = RadioSetting("alarm_mode", "Mode Alarme", val)
2253

    
2254
        # ----------------- Extra settings
2255

    
2256
        # S-meter
2257
        tmp_s0 = -int(_mem.s0_level)
2258
        tmp_s9 = -int(_mem.s9_level)
2259

    
2260
        if tmp_s0 not in range(-200, -91) or tmp_s9 not in range(-160, -51) \
2261
           or tmp_s9 < tmp_s0+9:
2262

    
2263
            tmp_s0 = -130
2264
            tmp_s9 = -76
2265
        val = RadioSettingValueInteger(-200, -90, tmp_s0)
2266
        s0_level_setting = RadioSetting("s0_level",
2267
                                        "Niveau S-meter S0 [dBm]", val)
2268
        s0_level_setting.set_doc('Niveau S-meter S0 [dBm] : Pour regler la calibration de S0 en dBm.')
2269

    
2270
        val = RadioSettingValueInteger(-160, -50, tmp_s9)
2271
        s9_level_setting = RadioSetting("s9_level",
2272
                                        "Niveau S-meter S9 [dBm]", val)
2273
        s9_level_setting.set_doc('Niveau S-meter S9 [dBm] : pour regler la calibration de S9 en dBm.')
2274

    
2275
        # Battery Type
2276
        tmpbtype = list_def(_mem.Battery_type, BATTYPE_LIST, 0)
2277
        val = RadioSettingValueList(BATTYPE_LIST, BATTYPE_LIST[tmpbtype])
2278
        bat_type_setting = RadioSetting("Battery_type",
2279
                                        "Type de la Batterie  (BatTyp)", val)
2280
        bat_type_setting.set_doc('BatTyp : Quel type de batterie equipe la radio. Cette valeur modifie ' + \
2281
                                 'le niveau de batterie afficher.')
2282

    
2283
        # Power on password
2284
#        def validate_password(value):
2285
#            value = value.strip(" ")
2286
#            if value.isdigit():
2287
#                return value.zfill(6)
2288
#            if value != "":
2289
#                raise InvalidValueError("Power on password "
2290
#                                        "can only have digits")
2291
#            return ""
2292

    
2293
#        pswd_str = str(int(_mem.password)).zfill(6) \
2294
#            if _mem.password < 1000000 else ""
2295
#        val = RadioSettingValueString(0, 6, pswd_str)
2296
#        val.set_validate_callback(validate_password)
2297
#        pswd_setting = RadioSetting("password", "Power on password", val)
2298

    
2299
        # ----------------- FM radio
2300

    
2301
        append_label(fmradio, "Memoire Radio FM (MR)", "Frequency [MHz]")
2302

    
2303
        for i in range(1, 21):
2304
            fmfreq = _mem.fmfreq[i-1]/10.0
2305
            freq_name = str(fmfreq)
2306
            if fmfreq < FMMIN or fmfreq > FMMAX:
2307
                freq_name = ""
2308
            rs = RadioSetting("FM_" + str(i), "Ch " + str(i),
2309
                              RadioSettingValueString(0, 5, freq_name))
2310
            rs.set_doc('Radio Frequency : Enter les frequences vos radios locale en MHZ,\nexample : 96.9 ' + \
2311
                       'Pour ecouter la radio FM, faire un appuie long sur le bouton [0 FM].\n' + \
2312
                       'Si vous voulez lancer un scan avec un appuie sur [* SCAN], les 20 premieres\n' + \
2313
                       'station seront mise en memoires.\nATTENTION, l\'ancienne liste sera EFFACER.')
2314
            fmradio.append(rs)
2315

    
2316
        # ----------------- Unlock settings
2317

    
2318
        # F-LOCK
2319
        def validate_int_flock(value):
2320
            mem_val = self._memobj.int_flock
2321
            if mem_val != 7 and value == FLOCK_LIST[7]:
2322
                msg = "\"" + value + "\" Activer UNIQUEMENT dans le menu de la radio"
2323
                raise InvalidValueError(msg)
2324
            return value
2325

    
2326
        tmpflock = list_def(_mem.int_flock, FLOCK_LIST, 0)
2327
        val = RadioSettingValueList(FLOCK_LIST, None, tmpflock)
2328
        val.set_validate_callback(validate_int_flock)
2329
        f_lock_setting = RadioSetting("int_flock",
2330
                                      "Frequence Emission Autoriser  (F Lock)", val)
2331
        f_lock_setting.set_doc('F Lock : Regle le plan de frequence d\'emission.')                                  
2332

    
2333
        val = RadioSettingValueBoolean(_mem.int_200tx)
2334
        tx200_setting = RadioSetting("int_200tx",
2335
                                     "Deverrouille l'emission entre 174-350MHz  (Tx 200)", val)
2336
        tx200_setting.set_doc('Active la transmission sur le 200MHz ')
2337

    
2338
        val = RadioSettingValueBoolean(_mem.int_350tx)
2339
        tx350_setting = RadioSetting("int_350tx",
2340
                                     "Deverrouille l'emission entre 350-400MHz  (Tx 350)", val)
2341
        tx350_setting.set_doc('Active la transmission sur le 350MHz ')
2342
        
2343
        val = RadioSettingValueBoolean(_mem.int_500tx)
2344
        tx500_setting = RadioSetting("int_500tx",
2345
                                     "Deverrouille l'emission entre 500-600MHz  (Tx 500)", val)
2346
        tx500_setting.set_doc('Active la transmission sur le 500MHz ')
2347
        
2348
        val = RadioSettingValueBoolean(_mem.int_350en)
2349
        en350_setting = RadioSetting("int_350en",
2350
                                     "Deverrouille la reception entre 350-400MHz  (350 En)", val)
2351
        en350_setting.set_doc('Active la reception sur 350MHz ')
2352
        
2353
        val = RadioSettingValueBoolean(_mem.int_scren)
2354
        en_scrambler_setting = RadioSetting("int_scren",
2355
                                            "Activation du Scrambler  (ScraEn)", val)
2356
        en_scrambler_setting.set_doc('Active ou Desactive le Scrambler.')
2357
        
2358
        # ----------------- Driver Info
2359

    
2360
        if self.FIRMWARE_VERSION == "":
2361
            firmware = "Pour connaitre la version du firmware, telecharger une image de votre radio."
2362
        else:
2363
            firmware = self.FIRMWARE_VERSION
2364

    
2365
        append_label(roinfo,
2366
                     "=" * 6 + " Firmware F4HWN " + "=" * 300, "=" * 300)
2367

    
2368
        append_label(roinfo, "Version Firmware", firmware)
2369
        val = RadioSettingValueString(0,75,FIRMWARE_DRIVER_VERSION_UPDATE)
2370
        rs = RadioSetting("Update","Dernier Firmware F4HWN (Faire un Copier/Coller du lien)", val)                      
2371
        rs.set_doc('Assurez vous d\'avoir la derniere version disponible.')
2372
        roinfo.append(rs)
2373
        
2374
        append_label(roinfo,
2375
                     "=" * 6 + " Chirp Driver F4HWN " + "=" * 300, "=" * 300)
2376
                     
2377
        append_label(roinfo, "Version Pilote Chirp", DRIVER_VERSION)
2378
        val = RadioSettingValueString(0,75,CHIRP_DRIVER_VERSION_UPDATE)
2379
        rs = RadioSetting("Update1","Dernier driver " + self.MODEL + ", (Faire un Copier/Coller du lien)", val)                      
2380
        rs.set_doc('Assurez vous d\'avoir la derniere version du driver disponible.')
2381
        roinfo.append(rs)
2382

    
2383
        append_label(roinfo,
2384
                     "=" * 6 + "=" * 300, "=" * 300)
2385

    
2386
        append_label(roinfo, "Le module driver chirp est fait par VE2ZJM", )
2387

    
2388
        append_label(roinfo,
2389
                     "=" * 6 + " Merci Special a : " + "=" * 300, "=" * 300)
2390

    
2391
        append_label(roinfo, "La traduction francaise a ete realiser par FLIXX", )
2392
 
2393
       
2394
        # ----------------- Calibration
2395

    
2396
        val = RadioSettingValueBoolean(False)
2397

    
2398
        def validate_upload_calibration(value):
2399
            if value and not self.upload_calibration:
2400
                msg = "Cette option peut bloquer votre Radio!!!\n" \
2401
                    "Vous allez faire ca a VOS risques.\n" \
2402
                    "Assurez vous d\'avoir une sauvegarde de la calibration.\n" \
2403
                    "NE PAS UTILISER si vous n\'etes pas sur de savoir ce que vous faites."
2404
                ret = wx.MessageBox(msg, "ATTENTION", wx.OK | wx.CANCEL |
2405
                                    wx.CANCEL_DEFAULT | wx.ICON_WARNING)
2406
                value = ret == wx.OK
2407
            self.upload_calibration = value
2408
            return value
2409

    
2410
        val.set_validate_callback(validate_upload_calibration)
2411
        radio_setting = RadioSetting("upload_calibration",
2412
                                     "Envoi Calibration", val)
2413
        radio_setting.set_doc('Pour envoyer uniquement les reglages de calibration a votre radio, ' + \
2414
                              'Vous devez cocher la case a cote puis envoye a votre Radio.')
2415
        calibration.append(radio_setting)
2416

    
2417
        radio_setting_group = RadioSettingGroup("squelch_calibration", "Squelch")
2418
        calibration.append(radio_setting_group)
2419

    
2420
        bands = {"sqlBand1_3": "Bande de Frequence 1-3",
2421
                 "sqlBand4_7": "Bande de Frequence 4-7"}
2422
        for bnd, bndn in bands.items():
2423
            append_label(radio_setting_group,
2424
                         "=" * 6 + " " + bndn + " " + "=" * 300, "=" * 300)
2425
            for sql in range(0, 10):
2426
                prefix = "_mem.cal." + bnd + "."
2427
                postfix = "[" + str(sql) + "]"
2428
                append_label(radio_setting_group, "Squelch " + str(sql))
2429

    
2430
                name = prefix + "openRssiThr" + postfix
2431
                tempval = min_max_def(eval(name), 0, 255, 0)
2432
                val = RadioSettingValueInteger(0, 255, tempval)
2433
                radio_setting = RadioSetting(name, "Seuil RSSI d\'ouverture du Squelch", val)
2434
                radio_setting_group.append(radio_setting)
2435

    
2436
                name = prefix + "closeRssiThr" + postfix
2437
                tempval = min_max_def(eval(name), 0, 255, 0)
2438
                val = RadioSettingValueInteger(0, 255, tempval)
2439
                radio_setting = RadioSetting(name, "Seuil RSSI d\'activation du Squelch", val)
2440
                radio_setting_group.append(radio_setting)
2441

    
2442
                name = prefix + "openNoiseThr" + postfix
2443
                tempval = min_max_def(eval(name), 0, 127, 0)
2444
                val = RadioSettingValueInteger(0, 127, tempval)
2445
                radio_setting = RadioSetting(name, "Seuil de Bruit d\'ouverture du Squelch", val)
2446
                radio_setting_group.append(radio_setting)
2447

    
2448
                name = prefix + "closeNoiseThr" + postfix
2449
                tempval = min_max_def(eval(name), 0, 127, 0)
2450
                val = RadioSettingValueInteger(0, 127, tempval)
2451
                radio_setting = RadioSetting(name, "Seuil de Bruit d\'activation Squelch", val)
2452
                radio_setting_group.append(radio_setting)
2453

    
2454
                name = prefix + "openGlitchThr" + postfix
2455
                tempval = min_max_def(eval(name), 0, 255, 0)
2456
                val = RadioSettingValueInteger(0, 255, tempval)
2457
                radio_setting = RadioSetting(name, "Seuil de Glitch (parasite) d\'ouverture du Squelch", val)
2458
                radio_setting_group.append(radio_setting)
2459

    
2460
                name = prefix + "closeGlitchThr" + postfix
2461
                tempval = min_max_def(eval(name), 0, 255, 0)
2462
                val = RadioSettingValueInteger(0, 255, tempval)
2463
                radio_setting = RadioSetting(name, "Sueil de Glitch (parasite) d\'activation du Squelch)",
2464
                                             val)
2465
                radio_setting_group.append(radio_setting)
2466

    
2467
        radio_setting_group = RadioSettingGroup("rssi_level_calibration", "Niveau RSSI")
2468
        calibration.append(radio_setting_group)
2469

    
2470
        bands = {"rssiLevelsBands1_2": "1-2 ", "rssiLevelsBands3_7": "3-7 "}
2471
        for bnd, bndn in bands.items():
2472
            append_label(radio_setting_group,
2473
                         "=" * 6 +
2474
                         " Niveau RSSI pour le QS original petit bar graph. Bandes "
2475
                         + bndn + "=" * 300, "=" * 300)
2476
            for lvl in [1, 2, 4, 6]:
2477
                name = "_mem.cal." + bnd + ".level" + str(lvl)
2478
                tempval = min_max_def(eval(name), 0, 65535, 0)
2479
                val = RadioSettingValueInteger(0, 65535, tempval)
2480
                radio_setting = RadioSetting(name, "Niveau " + str(lvl), val)
2481
                radio_setting_group.append(radio_setting)
2482

    
2483
#
2484

    
2485
        radio_setting_group = RadioSettingGroup("tx_power_calibration", "Puissance TX")
2486
        calibration.append(radio_setting_group)
2487

    
2488
        for bnd in range(0, 7):
2489
            append_label(radio_setting_group, "=" * 6 + " Puissance TX -  Bande "
2490
                         + str(bnd+1) + " " + "=" * 300, "=" * 300)
2491
            powers = {"low" : "Bas", "mid" : "Moyen", "hi" : "Haut"}
2492
            for pwr, pwrn in powers.items():
2493
                append_label(radio_setting_group, pwrn)
2494
                bounds = ["lower", "center", "upper"]
2495
                for bound in bounds:
2496
                    name = f"_mem.cal.txp[{bnd}].{pwr}.{bound}"
2497
                    tempval = min_max_def(eval(name), 0, 255, 0)
2498
                    val = RadioSettingValueInteger(0, 255, tempval)
2499
                    radio_setting = RadioSetting(name, bound.capitalize(), val)
2500
                    radio_setting_group.append(radio_setting)
2501

    
2502
#
2503

    
2504
        radio_setting_group = RadioSettingGroup("battery_calibration", "Batterie")
2505
        calibration.append(radio_setting_group)
2506

    
2507
        for lvl in range(0, 6):
2508
            name = "_mem.cal.batLvl[" + str(lvl) + "]"
2509
            temp_val = min_max_def(eval(name), 0, 4999, 4999)
2510
            val = RadioSettingValueInteger(0, 4999, temp_val)
2511
            radio_setting = \
2512
                RadioSetting(name, "Niveau " + str(lvl) +
2513
                             (" (Tension de calibration)" if lvl == 3 else ""),
2514
                             val)
2515
            radio_setting_group.append(radio_setting)
2516

    
2517
        radio_setting_group = RadioSettingGroup("vox_calibration", "VOX")
2518
        calibration.append(radio_setting_group)
2519

    
2520
        for lvl in range(0, 10):
2521
            append_label(radio_setting_group, "Level " + str(lvl + 1))
2522

    
2523
            name = "_mem.cal.vox1Thr[" + str(lvl) + "]"
2524
            val = RadioSettingValueInteger(0, 65535, eval(name))
2525
            radio_setting = RadioSetting(name, "On", val)
2526
            radio_setting_group.append(radio_setting)
2527

    
2528
            name = "_mem.cal.vox0Thr[" + str(lvl) + "]"
2529
            val = RadioSettingValueInteger(0, 65535, eval(name))
2530
            radio_setting = RadioSetting(name, "Off", val)
2531
            radio_setting_group.append(radio_setting)
2532

    
2533
        radio_setting_group = RadioSettingGroup("mic_calibration", "Sensibilite micro")
2534
        calibration.append(radio_setting_group)
2535

    
2536
        for lvl in range(0, 5):
2537
            name = "_mem.cal.micLevel[" + str(lvl) + "]"
2538
            tempval = min_max_def(eval(name), 0, 31, 31)
2539
            val = RadioSettingValueInteger(0, 31, tempval)
2540
            radio_setting = RadioSetting(name, "Niveau " + str(lvl), val)
2541
            radio_setting_group.append(radio_setting)
2542

    
2543
        radio_setting_group = RadioSettingGroup("other_calibration", "Autre(s)")
2544
        calibration.append(radio_setting_group)
2545

    
2546
        name = "_mem.cal.xtalFreqLow"
2547
        temp_val = min_max_def(eval(name), -1000, 1000, 0)
2548
        val = RadioSettingValueInteger(-1000, 1000, temp_val)
2549
        radio_setting = RadioSetting(name, "Frequence Basse Quartz", val)
2550
        radio_setting_group.append(radio_setting)
2551

    
2552
        name = "_mem.cal.volumeGain"
2553
        temp_val = min_max_def(eval(name), 0, 63, 58)
2554
        val = RadioSettingValueInteger(0, 63, temp_val)
2555
        radio_setting = RadioSetting(name, "Gain Volume", val)
2556
        radio_setting_group.append(radio_setting)
2557

    
2558
        name = "_mem.cal.dacGain"
2559
        temp_val = min_max_def(eval(name), 0, 15, 8)
2560
        val = RadioSettingValueInteger(0, 15, temp_val)
2561
        radio_setting = RadioSetting(name, "Gain DAC (Convertisseur Digital/Analogique)", val)
2562
        radio_setting_group.append(radio_setting)
2563

    
2564

    
2565

    
2566
        # ----------------- Help User
2567

    
2568
        help_group = RadioSettingGroup("Explain_Display_HELP", "Documentation Affichage")
2569
        help_user.append(help_group)
2570

    
2571
        append_label(help_group, "=" * 6 + " Explication affichage " + "=" * 50, "=" * 6  + " Emplacement sur l\'ecran " + "=" * 50)
2572
                         
2573
        append_label(help_group,"Ligne Status " , "C'est la 1er ligne afficher TOUT en haut de l\'ecran " )
2574
        append_label(help_group,"(PS) = Economie d\'energie " , "Ligne Status " )
2575
        append_label(help_group,"(S) = SCAN actif " , "Ligne Status" )
2576
        append_label(help_group,"(I) = SCAN Memoire dans liste 1 " , "Ligne Status" )
2577
        append_label(help_group,"(II) = SCAN Memoire dans liste 2 " , "Ligne Status " )
2578
        append_label(help_group,"(INFINI) = SCAN toutes les memoires " , "Ligne status " )
2579
        append_label(help_group,"(MO) = Main Only - Affiche un seul VFO " , "Ligne status " )
2580
        append_label(help_group,"(DWR) = Double veille " , "Ligne status " )
2581
        append_label(help_group,"(DW) = Double veille " , "Ligne status " )
2582
        append_label(help_group,"(XD) = Cross Bande " , "Ligne status " )
2583
        append_label(help_group,"(VX) = VOX activer " , "Ligne status " )
2584
        append_label(help_group,"(CL) = PTT Classique " , "Ligne status " )
2585
        append_label(help_group,"(OP) = PTT mode ONE PUSH " , "Ligne status " )
2586
        append_label(help_group,"(F) = La double fonction des boutons clavier activer " , "Ligne status " )
2587

    
2588
        append_label(help_group,"" , "" )
2589

    
2590
        append_label(help_group,"(Dark Arrow) = Indique le VFO actif" , "Sur le cote gauche du VFO " )
2591
        append_label(help_group,"(Mxxx) = Numero Memoire M1-M200", "Sur la gauche de chaque VFO " )        
2592
        append_label(help_group,"(Fx) = Numero de bande F1-F7 " , "Sur la gauche de chaque VFO " )
2593
        append_label(help_group,"(TX) = VFO utilise en cours d\'emision" , "Sur la gauche du VFO valide " )
2594
        append_label(help_group,"(RX) = Radio en reception" , "Sur la gauche de chaque VFO " )
2595

    
2596
        append_label(help_group,"" , "" )
2597

    
2598
        append_label(help_group, "=" * 6 + " Si(SetGui) est selectioner a CLASSIC " + "=" * 50, "=" * 6  + "=" * 100)
2599

    
2600
        append_label(help_group,"(FM,USB,AM) = Mode de reception" , "Affichable sous chaque VFO " )
2601
        append_label(help_group,"(DC) = RX DCS activer" , "Affichable sous chaque VFO " )
2602
        append_label(help_group,"(CT) = RX CTCSS activer" , "Affichable sous chaque VFO " )
2603
        append_label(help_group,"(H) = Puissance HAUTE" , "Affichable sous chaque VFO " )
2604
        append_label(help_group,"(M) = Puissance MOYEN" , "Affichable sous chaque VFO " )
2605
        append_label(help_group,"(Lx) = Puissance LOW (Basse), x = Niveau de 1 a 5" , "Affichable sous chaque VFO " )
2606
        append_label(help_group,"(W) = Filtre BF Large : 12.5kHz (WIDE)" , "Affichable sous chaque VFO " )
2607
        append_label(help_group,"(N) = Filtre BF Etroite : 6.25kHz (NARROW)" , "Affichable sous chaque VFO " )
2608

    
2609
        append_label(help_group,"" , "" )
2610

    
2611
        append_label(help_group, "=" * 6 + " Si (SetGui) est selectioner a TINY " + "=" * 50, "=" * 6  + "=" * 100)
2612

    
2613
        append_label(help_group,"(FM,USB,AM) = Mode de reception" , "Affichable sous chaque VFO " )
2614
        append_label(help_group,"(HIGH) = Puissance HAUTE" , "Affichable sous chaque VFO " )
2615
        append_label(help_group,"(MID) = Puissance MOYEN" , "Affichable sous chaque VFO " )
2616
        append_label(help_group,"(LOWx) = Puissance LOW (Basse) x = Niveau de 1 a 5" , "Affichable sous chaque VFO " )
2617
        append_label(help_group,"(CT x) = RX CTCSS activer, x valeur" , "Affichable sous chaque VFO " )
2618
        append_label(help_group,"(DC x) = RX CTCSS activer, x valeur" , "Affichable sous chaque VFO " )
2619
        append_label(help_group,"(x.xxK) = Pas incrementation de la Frequence" , "Affichable sous chaque VFO " )
2620
        append_label(help_group,"(WIDE) = Filtre BF Large : 12.5kHz (WIDE)" , "Affichable sous chaque VFO " )
2621
        append_label(help_group,"(NAR) = Filtre Etroite : 6.25kHz (NARROW)" , "Affichable sous chaque VFO " )
2622
       
2623
        append_label(help_group,"" , "" )
2624

    
2625
        append_label(help_group, "=" * 6 + "=" * 100, "=" * 6  + "=" * 100)
2626
       
2627
        append_label(help_group,"(SQLx) = Niveau du Squelch, x = valeur de 1 a 9" , "Affichable sous chaque VFO " )
2628
        append_label(help_group,"(MONI) = Reception Moniteur (Squelch desactive)" , "Affichable sous chaque VFO " )
2629
       
2630

    
2631
        help_group = RadioSettingGroup("Explain_Keyboard_HELP", "Documentation Clavier" )
2632
        help_user.append(help_group)
2633

    
2634
        append_label(help_group, "=" * 6 + " Fonction du clavier et boutons " + "=" * 50, "=" * 6  + " Lorsqu'on appuie sur (F #) avant la touche " + "=" * 50)
2635
                                                   
2636
        append_label(help_group,"(1 BAND) = Appuie LONG - Change de bande de frequence (F1 to F7)" , "Idem qu\'un appue LONG " )
2637
        append_label(help_group,"(1 BAND) = Appuie COURT, Chiffre 1  "," " )
2638

    
2639
        append_label(help_group,"","" )
2640

    
2641
        append_label(help_group,"(2 A/B) = Appuie LONG, change le VFO actif - A(Haut) / B(Bas) " , "Idem qu\'un appuie LONG " )
2642
        append_label(help_group,"(2 A/B) = Appuie COURT, Chiffre 2  "," " )
2643

    
2644
        append_label(help_group,"","" )
2645

    
2646
        append_label(help_group,"(3 VFO MR) = Appuie LONG, change le mode du VFO : VFO/Memoire " , "Idem qu\'un appuie LONG" )
2647
        append_label(help_group,"(3 VFO MR) = Appuie COURT, Chiffre 3  "," " )
2648

    
2649
        append_label(help_group,"","" )
2650
        
2651
        append_label(help_group,"(* SCAN) = Appuie LONG, si VFO en mode Frequence, lance le SCAN ", "" )
2652
        append_label(help_group,"(* SCAN) = Appuie LONG, si VFO en mode Memoire, change le scna LIST (1,2,ALL) ", " " )
2653
        append_label(help_group,"(* SCAN) = Appuie COURT, Affiche la saisie d\'un sequence DTMF (S) ", "Lance la recherche CTCSS de sur la frequence courante" )
2654

    
2655
        append_label(help_group,"","" )
2656

    
2657
        append_label(help_group,"(4 FC) = Appuie LONG, lance la detection Frequence et CTCSS " , "Idem qu\'un appuie LONG " )
2658
        append_label(help_group,"(4 FC) = Appuie COURT, Chiffre 4  "," " )
2659

    
2660
        append_label(help_group,"","" )
2661

    
2662
        append_label(help_group,"(5 NOAA) = Appuie LONG, si VFO = Frequence, le ScnRnG se lance" , "Affiche le Bande SCOPE de fagci " )
2663
        append_label(help_group,"(5 NOAA) = Appuie LONG, si VFO = Memoire, Change la Liste de Scan " , " ")
2664
        append_label(help_group,"(5 NOAA) = Appuie COURT, Chiffre 5"," " )
2665

    
2666
        append_label(help_group,"","" )
2667
        
2668
        append_label(help_group,"(6 H/M/L) = Appuie LONG, change la puissance d\'emission (High/Medium/Low) sur le VFO actif " , "Idem qu\'un appuie LONG " )
2669
        append_label(help_group,"(6 H/M/L) = Appuie COURT, Chiffre 6"," " )
2670

    
2671
        append_label(help_group,"","" )
2672
        
2673
        append_label(help_group,"(0 FM) = Appuie LONG, bascule sur la Bande de Radio FM ", "idem qu\'un appuie LONG " )
2674
        append_label(help_group,"(0 FM) = Appuie COURT, Chiffre 0"," " )
2675

    
2676
        append_label(help_group,"","" )
2677

    
2678
        append_label(help_group,"(7 VOX) = Appuie LONG, Change l'activation VOX (VX) " , "idem qu'un appuie LONG " )
2679
        append_label(help_group,"(7 VOX) = Appuie COURT, Chiffre 7"," " )
2680

    
2681
        append_label(help_group,"","" )
2682
        
2683
        append_label(help_group,"(8 R) = Appuie LONG, Reverse (si utilisation d\'un shift) (R)" , "Idem qu\'un appuie LONG")
2684
        append_label(help_group,"(8 R) = Appuie COURT, Chiffre 8  ","Force l'allumage ou l'extinction du retro eclairage." )
2685
        append_label(help_group,"","L'auto extinction n'est plus pris en compte. Ne tient plus compte de (BackLt)")
2686

    
2687
        append_label(help_group,"","" )
2688
        
2689
        append_label(help_group,"(9 CALL) = Appuie LONG, bascule sur la memoire rapide 1-Call " , "Idem qu\'un appuie LONG")
2690
        append_label(help_group,"(9 CALL) = Appuie COURT, Chiffre 9  ","Revient au mode normale du retroeclairage (BackLt)" )
2691

    
2692
        append_label(help_group,"","" )
2693

    
2694
        append_label(help_group,"(F #) = Appuie LONG, Verouillage/Deverouillage du clavier", " ")
2695
        append_label(help_group,"(F #) = Appuie COURT, pour active la seconde fonction des touches", " ")
2696

    
2697
        append_label(help_group,"","" )
2698

    
2699
        append_label(help_group,"Appuie LONG, (M A)  = Bouton programable par l'utilisateur","" )
2700
        append_label(help_group,"Appuie COURT, (M A)  = Accede au menu de la radio","" )
2701

    
2702
        append_label(help_group,"","" )
2703

    
2704
        append_label(help_group,"Appuie LONG, (Fleche HAUT B) = Change le VFO ou MR actif rapidement vers le haut","" )
2705
        append_label(help_group,"Appuie COURT, (Fleche HAUT B) = Change le VFO ou MR actif vers le haut ","Monte la sensibilite du squelch" )
2706

    
2707
        append_label(help_group,"","" )
2708

    
2709
        append_label(help_group,"Appuie LONG, (Fleche BAS C) = Change le VFO ou MR actif rapidement vers le bas", )
2710
        append_label(help_group,"Appuie COURT, (Fleche BAS C) = Change le VFO ou MR actif vers le bas","Baisse la sensibilite du squelch" )
2711

    
2712
        append_label(help_group,"","" )
2713
        
2714
        append_label(help_group, "=" * 6 + " Bouton sur le coter " + "=" * 50, "=" * 6  + "" + "=" * 50)
2715

    
2716
        append_label(help_group,"","" )
2717
        
2718
        append_label(help_group,"Appuie LONG, (PTT)  = Transmet sur le TX actif","" )
2719
        append_label(help_group,"Appuie COURT, (PTT)  = Transmet sur le TX actif","" )
2720

    
2721
        append_label(help_group,"","" )
2722
        
2723
        append_label(help_group,"Appuie LONG, (.)  = Bouton programable par l'utilisateur","" )
2724
        append_label(help_group,"Appuie COURT, (.)  = Bouton programable par l'utilisateur","" )
2725

    
2726
        append_label(help_group,"","" )
2727

    
2728
        append_label(help_group,"Appuie LONG, (..)  = Bouton programable par l'utilisateur","" )
2729
        append_label(help_group,"Appuie COURT, (..)  = Bouton programable par l'utilisateur","" )
2730

    
2731
        append_label(help_group,"","" )
2732
        
2733
        append_label(help_group, "=" * 6 + " Fonction SPECIAL " + "=" * 50, "=" * 6  + "" + "=" * 50)
2734

    
2735
        append_label(help_group,"","" )
2736
        
2737
        append_label(help_group,"Pour mettre la radio en mode programation du firmware","Tenir le bouton (PTT) puis allumer la radio, La Flashlight allumera" )        
2738

    
2739
        append_label(help_group,"","" )
2740
        
2741
        append_label(help_group,"Pour activer Le MENU cacher", "Tenir le bouton (PTT) et le bouton (.) puis allumer la radio" )
2742
        append_label(help_group,"", "les menus supplementaire sont a la fin du menu regulier" )        
2743

    
2744
        help_group = RadioSettingGroup("How_To_Upload_F4HWN_HELP", "Envoyer les reglages specifique de F4HWN")
2745
        help_user.append(help_group)
2746

    
2747
        append_label(help_group,"Un envoie special doit etre fait pour les parametres du firmware de F4HWN.",)
2748
        append_label(help_group,"Parce que les parametres de F4HWN sont placer dans une zone memoire differente.",)
2749
        append_label(help_group,"Dans 'Settings', choisir 'Reglage General'. La premiere section contient ", )
2750
        append_label(help_group,"les parametres specifique au firmware de F4HWN.", )
2751
        append_label(help_group,"Effectuez les changements desirer dans la section F4HWN.", )
2752
        append_label(help_group,"Puis envoyer les donnees au radio. Pour cela :", )
2753
        append_label(help_group,"Cocher la case sur la premiere ligne des parametres, ", )
2754
        append_label(help_group,"Allez dans le menu Radio, Upload to Radio, accepter le transfert. ( c'est tres rapide )", )
2755
        append_label(help_group,"Maintenant les parametres F4HWN ont ete envoyer.", )
2756
        append_label(help_group,"Effectuez les changements desirer dans la section F4HWN.", )
2757
        append_label(help_group,"Puis envoyer les donnees au radio. Pour cela :", )
2758
        append_label(help_group,"", )
2759
        append_label(help_group,"Continuer le parametrage de votre radio comme d\'habitude.", )
2760
        append_label(help_group,"Pour envoyer toutes les autres parametres au radio.", )
2761
        append_label(help_group,"Allez dans le menu Radio, Upload to Radio, accepter le transfert.", )
2762
        append_label(help_group,"Maintenant tout les parametres ont ete envoyer.", )
2763
        
2764
        help_group = RadioSettingGroup("Chirp_Language_HELP", "Comment changer la langue dans de Chirp ")
2765
        help_user.append(help_group)
2766
        
2767
        append_label(help_group,"Il n'est pas possible de changer de language directement dans Chirp.", )
2768
        append_label(help_group,"Vous devez changer les parametres de language dans Windows.", )
2769
        append_label(help_group,"Cela demande trop d'explication pour ici, reportez vous au fichier doc inclu dans le release ", )
2770
        append_label(help_group,"ou sur github... ",CHIRP_DRIVER_VERSION_UPDATE )
2771

    
2772

    
2773

    
2774
        # -------- LAYOUT
2775
        append_label(basic,
2776
                     "=" * 6 + " Debut des Parametres de F4HWN  (Fonction special voir aide d'utilisation)" + "=" * 300, "=" * 300)
2777
        basic.append(Upload_f4hwn)
2778
        basic.append(SetLowSetting)
2779
        basic.append(SetPttSetting)
2780
        basic.append(SetTotSetting)
2781
        basic.append(SetEotSetting)
2782
        basic.append(contrastSetting)
2783
        basic.append(SetInvSetting)
2784
        basic.append(SetLckSetting)
2785
        basic.append(SetMetSetting)
2786
        basic.append(SetGuiSetting)
2787
        append_label(basic,
2788
                     "=" * 6 + " Fin des Parametres de F4HWN " + "=" * 300, "=" * 300)
2789

    
2790
        append_label(basic,
2791
                     "=" * 6 + " Parametres General " + "=" * 300, "=" * 300)
2792

    
2793
        basic.append(squelch_setting)
2794
        basic.append(rx_mode_setting)
2795
        basic.append(call_channel_setting)
2796
        basic.append(auto_keypad_lock_setting)
2797
        basic.append(tx_t_out_setting)
2798
        basic.append(bat_save_setting)
2799
        basic.append(scn_rev_setting)
2800
        if _mem.BUILD_OPTIONS.ENABLE_NOAA:
2801
            basic.append(noaa_auto_scan_setting)
2802
        if _mem.BUILD_OPTIONS.ENABLE_AM_FIX:
2803
            basic.append(am_fix_setting)
2804

    
2805
        append_label(basic,
2806
                     "=" * 6 + " Parametres Affichage " + "=" * 300, "=" * 300)
2807

    
2808
        basic.append(bat_txt_setting)
2809
        basic.append(mic_bar_setting)
2810
        basic.append(ch_disp_setting)
2811
        basic.append(p_on_msg_setting)
2812
        basic.append(logo1_setting)
2813
        basic.append(logo2_setting)
2814

    
2815
        append_label(basic, "=" * 6 + " Parametres Retroeclairage "
2816
                     + "=" * 300, "=" * 300)
2817

    
2818
        basic.append(back_lt_setting)
2819
        basic.append(bl_min_setting)
2820
        basic.append(bl_max_setting)
2821
        basic.append(blt_trx_setting)
2822

    
2823
        append_label(basic, "=" * 6 + " Parametres Audio "
2824
                     + "=" * 300, "=" * 300)
2825

    
2826
        if _mem.BUILD_OPTIONS.ENABLE_VOX:
2827
            basic.append(vox_setting)
2828
        basic.append(mic_gain_setting)
2829
        basic.append(beep_setting)
2830
        basic.append(roger_setting)
2831
        basic.append(ste_setting)
2832
        basic.append(rp_ste_setting)
2833
        if _mem.BUILD_OPTIONS.ENABLE_VOICE:
2834
            basic.append(voice_setting)
2835
        if _mem.BUILD_OPTIONS.ENABLE_ALARM:
2836
            basic.append(alarm_setting)
2837

    
2838
        append_label(basic, "=" * 6 + " Etat de la Radio " + "=" * 300, "=" * 300)
2839

    
2840
        basic.append(freq0_setting)
2841
        basic.append(freq1_setting)
2842
        basic.append(tx_vfo_setting)
2843
        basic.append(keypad_lock_setting)
2844

    
2845
        advanced.append(freq_mode_allowed_setting)
2846
        advanced.append(bat_type_setting)
2847
        advanced.append(s0_level_setting)
2848
        advanced.append(s9_level_setting)
2849
#        if _mem.BUILD_OPTIONS.ENABLE_PWRON_PASSWORD:
2850
#            advanced.append(pswd_setting)
2851

    
2852
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2853
            dtmf.append(sep_code_setting)
2854
            dtmf.append(group_code_setting)
2855
        dtmf.append(first_code_per_setting)
2856
        dtmf.append(spec_per_setting)
2857
        dtmf.append(code_per_setting)
2858
        dtmf.append(code_int_setting)
2859
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2860
            dtmf.append(ani_id_setting)
2861
        dtmf.append(up_code_setting)
2862
        dtmf.append(dw_code_setting)
2863
        dtmf.append(d_prel_setting)
2864
        dtmf.append(dtmf_side_tone_setting)
2865
        if _mem.BUILD_OPTIONS.ENABLE_DTMF_CALLING:
2866
            dtmf.append(dtmf_resp_setting)
2867
            dtmf.append(d_hold_setting)
2868
            dtmf.append(d_live_setting)
2869
            dtmf.append(perm_kill_setting)
2870
            dtmf.append(kill_code_setting)
2871
            dtmf.append(rev_code_setting)
2872
            dtmf.append(killed_setting)
2873

    
2874
        unlock.append(f_lock_setting)
2875
        unlock.append(tx200_setting)
2876
        unlock.append(tx350_setting)
2877
        unlock.append(tx500_setting)
2878
        unlock.append(en350_setting)      
2879
# desable by f4hwn
2880
#       unlock.append(en_scrambler_setting)
2881

    
2882
        return top
2883

    
2884
    def set_memory(self, memory):
2885
        """
2886
        Store details about a high-level memory to the memory map
2887
        This is called when a user edits a memory in the UI
2888
        """
2889
        number = memory.number-1
2890
        att_num = number if number < 200 else 200 + int((number - 200) / 2)
2891

    
2892
        # Get a low-level memory object mapped to the image
2893
        _mem_chan = self._memobj.channel[number]
2894
        _mem_attr = self._memobj.ch_attr[att_num]
2895

    
2896
        _mem_attr.is_scanlist1 = 0
2897
        _mem_attr.is_scanlist2 = 0
2898
        _mem_attr.compander = 0
2899
        _mem_attr.is_free = 1
2900
        _mem_attr.band = 0x7
2901

    
2902
        # empty memory
2903
        if memory.empty:
2904
            _mem_chan.set_raw("\xFF" * 16)
2905
            if number < 200:
2906
                _mem_chname = self._memobj.channelname[number]
2907
                _mem_chname.set_raw("\xFF" * 16)
2908
            return memory
2909

    
2910
        # find band
2911
        band = self._find_band(memory.freq)
2912

    
2913
        # mode
2914
        tmp_mode = self.get_features().valid_modes.index(memory.mode)
2915
        _mem_chan.modulation = tmp_mode / 2
2916
        _mem_chan.bandwidth = tmp_mode % 2
2917
        if memory.mode == "USB":
2918
            _mem_chan.bandwidth = 1  # narrow
2919

    
2920
        # frequency/offset
2921
        _mem_chan.freq = memory.freq/10
2922
        _mem_chan.offset = memory.offset/10
2923

    
2924
        if memory.duplex == "":
2925
            _mem_chan.offset = 0
2926
            _mem_chan.offsetDir = 0
2927
        elif memory.duplex == '-':
2928
            _mem_chan.offsetDir = FLAGS1_OFFSET_MINUS
2929
        elif memory.duplex == '+':
2930
            _mem_chan.offsetDir = FLAGS1_OFFSET_PLUS
2931
        elif memory.duplex == 'off':
2932
            # we fake tx disable by setting the tx freq to 0 MHz
2933
            _mem_chan.offsetDir = FLAGS1_OFFSET_MINUS
2934
            _mem_chan.offset = _mem_chan.freq
2935
        # set band
2936

    
2937
        _mem_attr.is_free = 0
2938
        _mem_attr.band = band
2939

    
2940
        # channels >200 are the 14 VFO chanells and don't have names
2941
        if number < 200:
2942
            _mem_chname = self._memobj.channelname[number]
2943
            tag = memory.name.ljust(10) + "\x00"*6
2944
            _mem_chname.name = tag  # Store the alpha tag
2945

    
2946
        # tone data
2947
        self._set_tone(memory, _mem_chan)
2948

    
2949
        # step
2950
        _mem_chan.step = STEPS.index(memory.tuning_step)
2951

    
2952
        # tx power
2953
        if str(memory.power) == str(UVK5_POWER_LEVELS[2]):
2954
            _mem_chan.txpower = POWER_HIGH
2955
        elif str(memory.power) == str(UVK5_POWER_LEVELS[1]):
2956
            _mem_chan.txpower = POWER_MEDIUM
2957
        else:
2958
            _mem_chan.txpower = POWER_LOW
2959

    
2960
        # -------- EXTRA SETTINGS
2961

    
2962
        def get_setting(name, def_val):
2963
            if name in memory.extra:
2964
                return int(memory.extra[name].value)
2965
            return def_val
2966

    
2967
        _mem_chan.busyChLockout = get_setting("busyChLockout", False)
2968
        _mem_chan.dtmf_pttid = get_setting("pttid", 0)
2969
        _mem_chan.freq_reverse = get_setting("frev", False)
2970
        _mem_chan.dtmf_decode = get_setting("dtmfdecode", False)
2971
        _mem_chan.scrambler = get_setting("scrambler", 0)
2972
        _mem_attr.compander = get_setting("compander", 0)
2973
        if number < 200:
2974
            tmp_val = get_setting("scanlists", 0)
2975
            _mem_attr.is_scanlist1 = bool(tmp_val & 1)
2976
            _mem_attr.is_scanlist2 = bool(tmp_val & 2)
2977

    
2978
        return memory
(2-2/2)