Project

General

Profile

Bug #11370 » uvk5_CEC_05_HF_DO4KLA_fix20240604.py

Fixed driver that doesn't hard-code a Unix path - Alexandre J. Raymond, 06/04/2024 10:15 AM

 
1
# Quansheng UV-K5 driver (c) 2023 Jacek Lipkowski <sq5bpf@lipkowski.org>
2
#
3
# based on template.py Copyright 2012 Dan Smith <dsmith@danplanet.com>
4
#
5
#
6
# This is a preliminary version of a driver for the UV-K5
7
# It is based on my reverse engineering effort described here:
8
# https://github.com/sq5bpf/uvk5-reverse-engineering
9
#
10
# Warning: this driver is experimental, it may brick your radio,
11
# eat your lunch and mess up your configuration.
12
#
13
#
14
# This program is free software: you can redistribute it and/or modify
15
# it under the terms of the GNU General Public License as published by
16
# the Free Software Foundation, either version 2 of the License, or
17
# (at your option) any later version.
18
#
19
# This program is distributed in the hope that it will be useful,
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
# GNU General Public License for more details.
23
#
24
# You should have received a copy of the GNU General Public License
25
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
#
27
# Adapted partially to IJV firmware v2.9 by Julian Lilov (LZ1JDL)
28
# http://www.hamskey.com/2024/04/introduction-to-uv-k5-hf-05-fullband.html#more
29
#
30
# Adapted full to IJV  by Francesco IK8JHL 
31
# FIX:  QRA , Beacon/CQ CAll Message, Selettive , TX Enable, PTTID, Squelch A/B, Band TX, Band A/B TX , Singol Band enable,  Satcom , Upconverter etc etc
32
# eliminatte funzioni non attive nel FW IJV
33
#
34
# Adapted partially to CEC firmware v0.5 by KD8CEC
35
# https://www.universirius.com/en_gb/preppers/quansheng-uv-k5-manuale-del-firmware-ijv/#Firmware-IJV
36
#
37
#JH11i
38

    
39

    
40
import os
41
import tempfile
42
import struct
43
import logging
44

    
45
from chirp import chirp_common, directory, bitwise, memmap, errors, util
46
from chirp.settings import RadioSetting, RadioSettingGroup, \
47
    RadioSettingValueBoolean, RadioSettingValueList, \
48
    RadioSettingValueInteger, RadioSettingValueString, \
49
    RadioSettings
50

    
51
LOG = logging.getLogger(__name__)
52

    
53
# Show the obfuscated version of commands. Not needed normally, but
54
# might be useful for someone who is debugging a similar radio
55
DEBUG_SHOW_OBFUSCATED_COMMANDS = False
56

    
57
# Show the memory being written/received. Not needed normally, because
58
# this is the same information as in the packet hexdumps, but
59
# might be useful for someone debugging some obscure memory issue
60
DEBUG_SHOW_MEMORY_ACTIONS = True
61

    
62
DEBUG_WRITE_RADIO_HEXDUMP = True
63

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

    
71
    // 0x08
72
    u8 rx_freq_Code;
73
    u8 tx_offset_Code;
74
    struct {
75
        u8 rx_CodeType:4,
76
           tx_CodeType:4;
77
    } tx_rx_CodeType;
78
    struct {
79
        u8 TX_OFFSET_FREQUENCY_DIRECTION:4,
80
           Modulation:4;
81
    } mod_tx_off;
82
    struct {
83
        u8 FrequencyReverse:1,
84
           CHANNEL_BANDWIDTH:1,
85
           OUTPUT_POWER:2,
86
           BUSY_CHANNEL_LOCK:4;
87
    } bcl_op_cb;
88
    struct{
89
        u8 unknown1:1,
90
        DTMF_PTT_ID_TX_MODE:7;
91
    } DTMF_PTT_ID_TX_MODE;
92
    u8 STEP_SETTING;
93
    u8 SCRAMBLING_TYPE;
94

    
95
} channel[207];
96

    
97
#seekto 0x0D60;
98
struct {
99
// ChannelAttributes_t start
100
    u8 band;
101
    u8 compander;
102
    u8 is_scanlist2;
103
    u8 is_scanlist1;
104
// end
105
} channel_attributes[207];
106

    
107
#seekto 0x0E70;
108
u8 CHAN_1_CALL;
109
u8 SQUELCH_LEVEL;
110
u8 TX_TIMEOUT_TIMER;
111
u8 NOAA_AUTO_SCAN;
112
u8 KEY_LOCK;
113
u8 VOX_SWITCH;
114
u8 VOX_LEVEL;
115
u8 MIC_SENSITIVITY;
116

    
117
#seekto 0x0E78;
118
struct {
119
    u8 BACKLIGHT_MAX:4, BACKLIGHT_MIN:4;
120
} backlight;
121
u8 CHANNEL_DISPLAY_MODE;
122
u8 CROSS_BAND_RX_TX;
123
u8 BATTERY_SAVE;
124
u8 DUAL_WATCH;
125
u8 BACKLIGHT_TIME;
126
u8 TAIL_TONE_ELIMINATION;
127
u8 VFO_OPEN;
128

    
129
#seekto 0x0E80;
130
u8 ScreenChannel_0;
131
u8 ScreenChannel_1;
132
u8 MrChannel_0;
133
u8 MrChannel_1;
134
u8 FreqChannel_0;
135
u8 FreqChannel_1;
136
u8 NoaaChannel_0;
137
u8 NoaaChannel_1;
138

    
139
#seekto 0x0E90;
140
struct {
141
    u8 BEEP_CONTROL:1, KEY_M_LONG_PRESS_ACTION: 7;
142
} beep_key_long_press;
143
u8 KEY_1_SHORT_PRESS_ACTION;
144
u8 KEY_1_LONG_PRESS_ACTION;
145
u8 KEY_2_SHORT_PRESS_ACTION;
146
u8 KEY_2_LONG_PRESS_ACTION;
147
u8 SCAN_RESUME_MODE;
148
u8 AUTO_KEYPAD_LOCK;
149
u8 POWER_ON_DISPLAY_MODE;
150

    
151
#seekto 0x0E98;
152
u32 POWER_ON_PASSWORD;
153

    
154
#seekto 0x0EA0;
155
u8 VOICE_PROMPT;
156
u8 S0_LEVEL;
157
u8 S9_LEVEL;
158

    
159
#seekto 0x0EA8;
160
u8 ALARM_MODE;
161
u8 ROGER;
162
u8 REPEATER_TAIL_TONE_ELIMINATION;
163
u8 TX_VFO;
164
u8 BATTERY_TYPE;
165

    
166
#seekto 0x0EB0;
167
char EEPROM_WELCOMESTRING1[16];
168
char EEPROM_WELCOMESTRING2[16];
169

    
170
#seekto 0x0ED0;
171
u8 DTMF_SIDE_TONE;
172
char DTMF_SEPARATE_CODE;
173
char DTMF_GROUP_CALL_CODE;
174
u8 DTMF_DECODE_RESPONSE;
175
u8 DTMF_auto_reset_time;
176
u8 DTMF_PRELOAD_TIME;
177
u8 DTMF_FIRST_CODE_PERSIST_TIME;
178
u8 DTMF_HASH_CODE_PERSIST_TIME;
179
u8 DTMF_CODE_PERSIST_TIME;
180
u8 DTMF_CODE_INTERVAL_TIME;
181
u8 PERMIT_REMOTE_KILL;
182

    
183
#seekto 0x0EE0;
184
char ANI_DTMF_ID[8];
185

    
186
#seekto 0x0EE8;
187
char KILL_CODE[8];
188

    
189
#seekto 0x0EF0;
190
char REVIVE_CODE[8];
191

    
192
#seekto 0x0EF8;
193
char DTMF_UP_CODE[16];
194

    
195
#seekto 0x0F08;
196
char DTMF_DOWN_CODE[16];
197

    
198
#seekto 0x0F18;
199
u8 SCANLIST_DEFAULT;
200
u8 SCANLIST1_ENABLED;
201
u8 SCANLIST1_PRIORITY_CH1;
202
u8 SCANLIST1_PRIORITY_CH2;
203
u8 SCANLIST2_ENABLED;
204
u8 SCANLIST2_PRIORITY_CH1;
205
u8 SCANLIST2_PRIORITY_CH2;
206

    
207
#seekto 0x0F30;
208
u8 bHasCustomAesKey;
209

    
210
#seekto 0x0F40;
211
u8 F_LOCK;
212
u8 350TX;
213
u8 KILLED;
214
u8 200TX;
215
u8 350EN;
216
u8 en350;
217
u8 ScrambleEnable;
218
struct {
219
    u8 Setting_TX_EN:1,
220
       live_DTMF_decoder: 1,
221
       batery_txt:2,
222
       micbar:1,
223
       am_fix:1,
224
       bl_txrx:2;
225
} settings_0f40;
226

    
227
#seekto 0x0F50;
228
struct{
229
    char name[16];
230
} channel_name[207];
231

    
232
// CEC_EEPROM_START1
233
#seekto 0x1D50;
234
u8 CEC_LiveSeekMode;
235
u8 CW_KEYTYPE;
236
u8 CW_SPEED;
237
u8 CW_TONE;
238

    
239
// DTMF contacts
240
#seekto 0x1C00;
241
struct {
242
    char name[8];
243
    char number[3];
244
    char unused_00[5];
245
} dtmfcontact[16];
246
"""
247

    
248
# bits that we will save from the channel structure (mostly unknown)
249
SAVE_MASK_0A = 0b11001100
250
SAVE_MASK_0B = 0b11101100
251
SAVE_MASK_0C = 0b11100000
252
SAVE_MASK_0D = 0b11111000
253
SAVE_MASK_0E = 0b11110001
254
SAVE_MASK_0F = 0b11110000
255

    
256
# flags1
257
FLAGS1_OFFSET_NONE = 0b00
258
FLAGS1_OFFSET_MINUS = 0b10
259
FLAGS1_OFFSET_PLUS = 0b01
260

    
261
# flags2
262

    
263
POWER_HIGH = 0b10
264
POWER_MEDIUM = 0b01
265
POWER_LOW = 0b00
266

    
267
BANDWIDTH_WIDE = 0b00
268
BANDWIDTH_WIDE_PLUS = 0b11
269
BANDWIDTH_NARROW = 0b01
270
BANDWIDTH_NARROW_MINUS = 0b10
271
# bandwidth
272
BANDWIDTH_LIST = ["W", "N"]
273

    
274
# dtmf_flags
275
PTTID_LIST = ["off", "TX UP", "TX DOWN", "BOTH", "APOLLO" ]
276

    
277
# power
278
UVK5_POWER_LEVELS = [chirp_common.PowerLevel("Low",  watts=1.00),
279
                     chirp_common.PowerLevel("Med",  watts=2.50),
280
                     chirp_common.PowerLevel("High", watts=5.00)
281
                    ]
282

    
283
# scrambler // TODO Gibt es das in der Firmware?
284
SCRAMBLER_LIST = ["Off", "2600Hz", "2700Hz", "2800Hz", "2900Hz", "3000Hz", "3100Hz", "3200Hz", "3300Hz", "3400Hz", "3500Hz"]
285

    
286
# channel display mode
287
CHANNELDISP_LIST = ["Frequency", "Channel No", "Channel Name", "Name_S Freq_L"]
288

    
289
BATSAVE_LIST = ["Off", "50%", "67%", "75%", "80%"]
290

    
291
# compander // nur 0 1 2 als Werte Möglich, siehe bk4819.c Zeile 868
292
COMPANDER_LIST = ["Off", "TX", "RX"]
293

    
294
# mic gain
295
MICGAIN_LIST = ["+1.1dB","+4.0dB","+8.0dB","+12.0dB","+15.1dB"]
296

    
297
# Talk Time
298
TALKTIME_LIST = ["30s","1min","2min","3min","4min","5min","6min","7min","8min","9min","15min"]
299

    
300
# Backlight auto mode
301
BACKLIGHT_MIN_LIST = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
302
BACKLIGHT_MAX_LIST = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
303
                  
304
BACKLIGHT_TX = ["Off", "TX", "RX", "TX/RX"]
305

    
306
# Crossband receiving/transmitting
307
CROSSBAND_LIST = ["Off", "VFO A", "VFO B"]
308
DUALWATCH_LIST = ["Off", "VFO A", "VFO B"]
309
# battery save
310
BATTERY_SAFE_LIST = ["Off", "1:4", "1:3", "1:2", "1:1"]
311

    
312
# steps
313
STEPS = [0.01, 0.05, 0.10, 0.25, 0.50, 1.00, 1.25, 2.50, 5.00, 6.25, 8.33, 9.00, 10.00, 12.50, 15.00, 20.00, 25.00, 30.00, 50.00, 100.00, 25.00, 125.00, 200.00, 250.00, 500.00]
314

    
315
# TODO *TCS???
316
# ctcss/dcs codes
317
TMODES = ["", "Tone", "DTCS", "DTCS"]
318
TONE_NONE = 0
319
TONE_CTCSS = 1
320
TONE_DCS = 2
321
TONE_RDCS = 3
322

    
323

    
324
CTCSS_TONES = [
325
    67.0, 69.3, 71.9, 74.4, 77.0, 79.7, 82.5, 85.4,
326
    88.5, 91.5, 94.8, 97.4, 100.0, 103.5, 107.2, 110.9,
327
    114.8, 118.8, 123.0, 127.3, 131.8, 136.5, 141.3, 146.2,
328
    151.4, 156.7, 159.8, 162.2, 165.5, 167.9, 171.3, 173.8,
329
    177.3, 179.9, 183.5, 186.2, 189.9, 192.8, 196.6, 199.5,
330
    203.5, 206.5, 210.7, 218.1, 225.7, 229.1, 233.6, 241.8,
331
    250.3, 254.1
332
]
333

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

    
348
# TODO required?
349
#FLOCK_LIST = ["Off", "FCC", "CE", "GB", "430", "438"]
350

    
351
SCANRESUME_LIST = ["TIME: Resume after 5 seconds",
352
                   "CARRIER: Resume after signal disappears",
353
                   "SEARCH: Stop scanning after receiving a signal",
354
                   "LOG"]
355

    
356
WELCOME_LIST = ["None", "FW Mod", "Message"]
357
KEYPADTONE_LIST = ["Off", "Chinese", "English"]
358
LANGUAGE_LIST = ["Chinese", "English"]
359
ALARMMODE_LIST = ["SITE", "TONE"]
360
REMENDOFTALK_LIST = ["Off", "Single", "Roger", "MDC 1200", "Apollo Quindar", "Digital Code ID"]
361
RTE_LIST = ["Off", 
362
            "1*100ms", "2*100ms", "3*100ms", "4*100ms", "5*100ms",
363
            "6*100ms", "7*100ms", "8*100ms", "9*100ms", "10*100ms",
364
            "11*100ms", "12*100ms", "13*100ms", "14*100ms", "15*100ms",
365
            "16*100ms", "17*100ms", "18*100ms", "19*100ms", "20*100ms"]
366

    
367
MEM_SIZE = 0x2000  # size of all memory
368
PROG_SIZE = 0x1d00  # size of the memory that we will write
369
MEM_BLOCK = 0x80  # largest block of memory that we can reliably write
370

    
371
# fm radio supported frequencies
372
FMMIN = 76.0
373
FMMAX = 108.0
374

    
375
# bands supported by the UV-K5
376
BANDS = {
377
        0: [15.0, 107.9999],
378
        1: [108.0, 135.9999],
379
        2: [136.0, 173.9990],
380
        3: [174.0, 349.9999],
381
        4: [350.0, 399.9999],
382
        5: [400.0, 469.9999],
383
        6: [470.0, 1299.9999]
384
        }
385

    
386
# for radios with modified firmware:
387
BANDS_NOLIMITS = {
388
        0: [15.0, 107.9999],
389
        1: [108.0, 135.9999],
390
        2: [136.0, 173.9990],
391
        3: [174.0, 349.9999],
392
        4: [350.0, 399.9999],
393
        5: [400.0, 469.9999],
394
        6: [470.0, 1299.9999]
395
        }
396

    
397
SPECIALS = {
398
        # "VFO A1(15-108)": 200,
399
        # "VFO B1(15-108)": 201,
400
        # "VFO A2(108-136)": 202,
401
        # "VFO B2(108-136)": 203,
402
        # "VFO A3(136-174)": 204,
403
        # "VFO B3(136-174)": 205,
404
        # "VFO A4(174-350)": 206,
405
        # "VFO B4(174-350)": 207,
406
        # "VFO A5(350-400)": 208,
407
        # "VFO B5(350-400)": 209,
408
        # "VFO A6(400-470)": 210,
409
        # "VFO B6(400-470)": 211,
410
        # "VFO A7(470-1300)": 212,
411
        # "VFO B7(470-1300)": 213
412
        }
413

    
414
VFO_CHANNEL_NAMES = ["F1(50M-76M)A", "F1(50M-76M)B",
415
                     "F2(108M-136M)A", "F2(108M-136M)B",
416
                     "F3(136M-174M)A", "F3(136M-174M)B",
417
                     "F4(174M-350M)A", "F4(174M-350M)B",
418
                     "F5(350M-400M)A", "F5(350M-400M)B",
419
                     "F6(400M-470M)A", "F6(400M-470M)B",
420
                     "F7(470M-600M)A", "F7(470M-600M)B"]
421

    
422
SCANLIST_LIST = ["None", "1", "2", "1+2"]
423

    
424
DTMF_CHARS = "0123456789ABCD*# "
425
DTMF_CHARS_ID = "0123456789ABCDabcd#* "
426
DTMF_CHARS_KILL = "0123456789ABCDabcd"
427
DTMF_CHARS_UPDOWN = "0123456789ABCDabcd#* "
428
DTMF_CODE_CHARS = "ABCD*# "
429
DTMF_DECODE_RESPONSE_LIST = ["None", "Ring", "Reply", "Both"]
430

    
431
KEYACTIONS_LIST = ["None", "Flashlight", "TX Power",
432
                   "Monitor", "Scan on/off", "VOX on/off",
433
                   "FM radio on/off", "VFO Change", "VFO Swap", 
434
                   "SQL +", "SQL -", "REGA Test", "REGA Alarm", "CW Call CQ"]
435

    
436
UPCONV_LIST = ["Off","50", "125"]
437

    
438
# the communication is obfuscated using this fine mechanism
439
def xorarr(data: bytes):
440
    tbl = [22, 108, 20, 230, 46, 145, 13, 64, 33, 53, 213, 64, 19, 3, 233, 128]
441
    x = b""
442
    r = 0
443
    for byte in data:
444
        x += bytes([byte ^ tbl[r]])
445
        r = (r+1) % len(tbl)
446
    return x
447

    
448

    
449
# if this crc was used for communication to AND from the radio, then it
450
# would be a measure to increase reliability.
451
# but it's only used towards the radio, so it's for further obfuscation
452
def calculate_crc16_xmodem(data: bytes):
453
    poly = 0x1021
454
    crc = 0x0
455
    for byte in data:
456
        crc = crc ^ (byte << 8)
457
        for i in range(8):
458
            crc = crc << 1
459
            if (crc & 0x10000):
460
                crc = (crc ^ poly) & 0xFFFF
461
    return crc & 0xFFFF
462

    
463

    
464
def _send_command(serport, data: bytes):
465
    """Send a command to UV-K5 radio"""
466
    LOG.debug("Sending command (unobfuscated) len=0x%4.4x:\n%s" %
467
              (len(data), util.hexprint(data)))
468

    
469
    crc = calculate_crc16_xmodem(data)
470
    data2 = data + struct.pack("<H", crc)
471

    
472
    command = struct.pack(">HBB", 0xabcd, len(data), 0) + \
473
        xorarr(data2) + \
474
        struct.pack(">H", 0xdcba)
475
    if DEBUG_SHOW_OBFUSCATED_COMMANDS:
476
        LOG.debug("Sending command (obfuscated):\n%s" % util.hexprint(command))
477
    try:
478
        result = serport.write(command)
479
    except Exception:
480
        raise errors.RadioError("Error writing data to radio")
481
    return result
482

    
483

    
484
def _receive_reply(serport):
485
    header = serport.read(4)
486
    if len(header) != 4:
487
        LOG.warning("Header short read: [%s] len=%i" %
488
                    (util.hexprint(header), len(header)))
489
        raise errors.RadioError("Header short read")
490
    if header[0] != 0xAB or header[1] != 0xCD or header[3] != 0x00:
491
        LOG.warning("Bad response header: %s len=%i" %
492
                    (util.hexprint(header), len(header)))
493
        raise errors.RadioError("Bad response header")
494

    
495
    cmd = serport.read(int(header[2]))
496
    if len(cmd) != int(header[2]):
497
        LOG.warning("Body short read: [%s] len=%i" %
498
                    (util.hexprint(cmd), len(cmd)))
499
        raise errors.RadioError("Command body short read")
500

    
501
    footer = serport.read(4)
502

    
503
    if len(footer) != 4:
504
        LOG.warning("Footer short read: [%s] len=%i" %
505
                    (util.hexprint(footer), len(footer)))
506
        raise errors.RadioError("Footer short read")
507

    
508
    if footer[2] != 0xDC or footer[3] != 0xBA:
509
        LOG.debug(
510
                "Reply before bad response footer (obfuscated)"
511
                "len=0x%4.4x:\n%s" % (len(cmd), util.hexprint(cmd)))
512
        LOG.warning("Bad response footer: %s len=%i" %
513
                    (util.hexprint(footer), len(footer)))
514
        raise errors.RadioError("Bad response footer")
515

    
516
    if DEBUG_SHOW_OBFUSCATED_COMMANDS:
517
        LOG.debug("Received reply (obfuscated) len=0x%4.4x:\n%s" %
518
                  (len(cmd), util.hexprint(cmd)))
519

    
520
    cmd2 = xorarr(cmd)
521

    
522
    LOG.debug("Received reply (unobfuscated) len=0x%4.4x:\n%s" %
523
              (len(cmd2), util.hexprint(cmd2)))
524

    
525
    return cmd2
526

    
527

    
528
def _getstring(data: bytes, begin, maxlen):
529
    tmplen = min(maxlen+1, len(data))
530
    s = [data[i] for i in range(begin, tmplen)]
531
    for key, val in enumerate(s):
532
        if val < ord(' ') or val > ord('~'):
533
            break
534
    return ''.join(chr(x) for x in s[0:key])
535

    
536

    
537
def _sayhello(serport):
538
    hellopacket = b"\x14\x05\x04\x00\x6a\x39\x57\x64"
539

    
540
    tries = 5
541
    while True:
542
        LOG.debug("Sending hello packet")
543
        _send_command(serport, hellopacket)
544
        o = _receive_reply(serport)
545
        if (o):
546
            break
547
        tries -= 1
548
        if tries == 0:
549
            LOG.warning("Failed to initialise radio")
550
            raise errors.RadioError("Failed to initialize radio")
551
    firmware = _getstring(o, 4, 16)
552
    LOG.info("Found firmware: %s" % firmware)
553
    return firmware
554

    
555

    
556
def _readmem(serport, offset, length):
557
    LOG.debug("Sending readmem offset=0x%4.4x len=0x%4.4x" % (offset, length))
558

    
559
    readmem = b"\x1b\x05\x08\x00" + \
560
        struct.pack("<HBB", offset, length, 0) + \
561
        b"\x6a\x39\x57\x64"
562
    _send_command(serport, readmem)
563
    o = _receive_reply(serport)
564
    if DEBUG_SHOW_MEMORY_ACTIONS:
565
        LOG.debug("readmem Received data len=0x%4.4x:\n%s" %
566
                  (len(o), util.hexprint(o)))
567
    return o[8:]
568

    
569

    
570
def _writemem(serport, data, offset):
571
    LOG.debug("Sending writemem offset=0x%4.4x len=0x%4.4x" %
572
              (offset, len(data)))
573

    
574
    if DEBUG_SHOW_MEMORY_ACTIONS:
575
        LOG.debug("writemem sent data offset=0x%4.4x len=0x%4.4x:\n%s" %
576
                  (offset, len(data), util.hexprint(data)))
577

    
578
    dlen = len(data)
579
    writemem = b"\x1d\x05" + \
580
        struct.pack("<BBHBB", dlen+8, 0, offset, dlen, 1) + \
581
        b"\x6a\x39\x57\x64"+data
582

    
583
    _send_command(serport, writemem)
584
    o = _receive_reply(serport)
585

    
586
    LOG.debug("writemem Received data: %s len=%i" % (util.hexprint(o), len(o)))
587

    
588
    if (o[0] == 0x1e
589
            and
590
            o[4] == (offset & 0xff)
591
            and
592
            o[5] == (offset >> 8) & 0xff):
593
        return True
594
    else:
595
        LOG.warning("Bad data from writemem")
596
        raise errors.RadioError("Bad response to writemem")
597

    
598

    
599
def _resetradio(serport):
600
    resetpacket = b"\xdd\x05\x00\x00"
601
    _send_command(serport, resetpacket)
602

    
603

    
604
def do_download(radio):
605
    serport = radio.pipe
606
    serport.timeout = 0.5
607
    status = chirp_common.Status()
608
    status.cur = 0
609
    status.max = MEM_SIZE
610
    status.msg = "Downloading from radio"
611
    radio.status_fn(status)
612

    
613
    eeprom = b""
614
    f = _sayhello(serport)
615
    if f:
616
        radio.FIRMWARE_VERSION = f
617
    else:
618
        raise errors.RadioError('Unable to determine firmware version')
619

    
620
    addr = 0
621
    while addr < MEM_SIZE:
622
        o = _readmem(serport, addr, MEM_BLOCK)
623
        status.cur = addr
624
        radio.status_fn(status)
625

    
626
        if o and len(o) == MEM_BLOCK:
627
            eeprom += o
628
            addr += MEM_BLOCK
629
        else:
630
            raise errors.RadioError("Memory download incomplete")
631

    
632
    if DEBUG_WRITE_RADIO_HEXDUMP:
633
        hex_data = _convert_to_intel_hex(eeprom)
634
        _save_to_hex_file(hex_data, os.path.join(tempfile.gettempdir(), "radio_dump.hex"))
635

    
636
    return memmap.MemoryMapBytes(eeprom)
637

    
638

    
639
def _convert_to_intel_hex(data):
640
    hex_data = ""
641
    for i in range(0, len(data), 16):
642
        chunk = data[i:i+16]
643
        hex_data += ":"
644
        hex_data += format(len(chunk), "02X")
645
        hex_data += format(i >> 8, "04X")
646
        hex_data += format(i & 0xFF, "02X")
647
        hex_data += "00"
648
        for byte in chunk:
649
            hex_data += format(byte, "02X")
650
        checksum = (len(chunk) + (i >> 8) + (i & 0xFF)) & 0xFF
651
        checksum = ((~checksum) + 1) & 0xFF
652
        hex_data += format(checksum, "02X")
653
        hex_data += "\n"
654
    hex_data += ":00000001FF"  # End of file record
655
    return hex_data
656

    
657

    
658
def _save_to_hex_file(data, filename):
659
    with open(filename, "w") as f:
660
        f.write(data)
661

    
662

    
663
def do_upload(radio):
664
    serport = radio.pipe
665
    serport.timeout = 0.5
666
    status = chirp_common.Status()
667
    status.cur = 0
668
    status.max = PROG_SIZE
669
    status.msg = "Uploading to radio"
670
    radio.status_fn(status)
671

    
672
    f = _sayhello(serport)
673
    if f:
674
        radio.FIRMWARE_VERSION = f
675
    else:
676
        return False
677

    
678
    addr = 0
679
    while addr < PROG_SIZE:
680
        o = radio.get_mmap()[addr:addr+MEM_BLOCK]
681
        _writemem(serport, o, addr)
682
        status.cur = addr
683
        radio.status_fn(status)
684
        if o:
685
            addr += MEM_BLOCK
686
        else:
687
            raise errors.RadioError("Memory upload incomplete")
688
    status.msg = "Uploaded OK"
689

    
690
    _resetradio(serport)
691

    
692
    return True
693

    
694

    
695
def _find_band(nolimits, hz):
696
    mhz = hz/1000000.0
697
    if nolimits:
698
        B = BANDS_NOLIMITS
699
    else:
700
        B = BANDS
701

    
702
    # currently the hacked firmware sets band=1 below 50 MHz
703
    if nolimits and mhz < 50.0:
704
        return 1
705

    
706
    for a in B:
707
        if mhz >= B[a][0] and mhz <= B[a][1]:
708
            return a
709
    return False
710

    
711

    
712
@directory.register
713
class UVK5Radio(chirp_common.CloneModeRadio):
714
    """Quansheng UV-K5"""
715
    VENDOR = "Quansheng"
716
    MODEL = "UV-K5-CEC"
717
    BAUD_RATE = 38400
718
    NEEDS_COMPAT_SERIAL = False
719
    FIRMWARE_VERSION = ""
720
    _expanded_limits = True
721

    
722
    def get_prompts(x=None):
723
        rp = chirp_common.RadioPrompts()
724
        rp.experimental = _(
725
            'This is an experimental driver for the Quansheng UV-K5. '
726
            'It may harm your radio, or worse. Use at your own risk.\n\n'
727
            'Before attempting to do any changes please download '
728
            'the memory image from the radio with chirp '
729
            'and keep it. This can be later used to recover the '
730
            'original settings. \n\n'
731
            'some details are not yet implemented')
732
        rp.pre_download = _(
733
            "1. Turn radio on.\n"
734
            "2. Connect cable to mic/spkr connector.\n"
735
            "3. Make sure connector is firmly connected.\n"
736
            "4. Click OK to download image from device.\n\n"
737
            "It will may not work if you turn on the radio "
738
            "with the cable already attached\n")
739
        rp.pre_upload = _(
740
            "1. Turn radio on.\n"
741
            "2. Connect cable to mic/spkr connector.\n"
742
            "3. Make sure connector is firmly connected.\n"
743
            "4. Click OK to upload the image to device.\n\n"
744
            "It will may not work if you turn on the radio "
745
            "with the cable already attached")
746
        return rp
747

    
748
    # Return information about this radio's features, including
749
    # how many memories it has, what bands it supports, etc
750
    def get_features(self):
751
        rf = chirp_common.RadioFeatures()
752
        rf.has_bank = False
753
        rf.valid_dtcs_codes = DTCS_CODES
754
        rf.has_rx_dtcs = True
755
        rf.has_ctone = True
756
        rf.has_settings = True
757
        rf.has_comment = False
758
        rf.valid_name_length = 10
759
        rf.valid_power_levels = UVK5_POWER_LEVELS
760
        rf.valid_special_chans = list(SPECIALS.keys())
761
        rf.valid_duplexes = ["", "-", "+", "off"]
762

    
763
        # hack so we can input any frequency,
764
        # the 0.1 and 0.01 steps don't work unfortunately
765
        # rf.valid_tuning_steps = [0.01, 0.1, 1.0] + STEPS
766
        rf.valid_tuning_steps = STEPS
767

    
768
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
769
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
770
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
771

    
772
        rf.valid_characters = chirp_common.CHARSET_ASCII
773
        rf.valid_modes = ["FM", "AM", "USB", "CW"]
774

    
775
        rf.valid_skips = [""]
776

    
777
        # This radio supports memories 1-200, 201-214 are the VFO memories
778
        rf.memory_bounds = (1, 200)
779

    
780
        rf.valid_bands = []
781
        for a in BANDS_NOLIMITS:
782
            rf.valid_bands.append(
783
                    (int(BANDS_NOLIMITS[a][0]*1000000),
784
                     int(BANDS_NOLIMITS[a][1]*1000000)))
785
        return rf
786

    
787
    # Do a download of the radio from the serial port
788
    def sync_in(self):
789
        self._mmap = do_download(self)
790
        self.process_mmap()
791

    
792
    # Do an upload of the radio to the serial port
793
    def sync_out(self):
794
        do_upload(self)
795

    
796
    # Convert the raw byte array into a memory object structure
797
    def process_mmap(self):
798
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
799

    
800
    # Return a raw representation of the memory object, which
801
    # is very helpful for development
802
    def get_raw_memory(self, number):
803
        return repr(self._memobj.channel[number-1])
804

    
805
    def validate_memory(self, mem):
806
        msgs = super().validate_memory(mem)
807

    
808
        if mem.duplex == 'off':
809
            return msgs
810

    
811
        # find tx frequency
812
        if mem.duplex == '-':
813
            txfreq = mem.freq - mem.offset
814
        elif mem.duplex == '+':
815
            txfreq = mem.freq + mem.offset
816
        else:
817
            txfreq = mem.freq
818

    
819
        # find band
820
        band = _find_band(self._expanded_limits, txfreq)
821
        if band is False:
822
            msg = "Transmit frequency %.4f MHz is not supported by this radio"\
823
                   % (txfreq/1000000.0)
824
            msgs.append(chirp_common.ValidationError(msg))
825

    
826
        band = _find_band(self._expanded_limits, mem.freq)
827
        if band is False:
828
            msg = "The frequency %.4f MHz is not supported by this radio" \
829
                   % (mem.freq/1000000.0)
830
            msgs.append(chirp_common.ValidationError(msg))
831

    
832
        return msgs
833

    
834
    def _set_tone(self, mem, _mem):
835
        ((txmode, txtone, txpol),
836
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
837

    
838
        if txmode == "Tone":
839
            txtoval = CTCSS_TONES.index(txtone)
840
            txmoval = 0b01
841
        elif txmode == "DTCS":
842
            txmoval = txpol == "R" and 0b11 or 0b10
843
            txtoval = DTCS_CODES.index(txtone)
844
        else:
845
            txmoval = 0
846
            txtoval = 0
847

    
848
        if rxmode == "Tone":
849
            rxtoval = CTCSS_TONES.index(rxtone)
850
            rxmoval = 0b01
851
        elif rxmode == "DTCS":
852
            rxmoval = rxpol == "R" and 0b11 or 0b10
853
            rxtoval = DTCS_CODES.index(rxtone)
854
        else:
855
            rxmoval = 0
856
            rxtoval = 0
857

    
858
        _mem.rxcodeflag = rxmoval
859
        _mem.txcodeflag = txmoval
860
        _mem.unknown1 = 0
861
        _mem.unknown2 = 0
862
        _mem.rxcode = rxtoval
863
        _mem.txcode = txtoval
864

    
865
    def _get_tone(self, mem, _mem):
866
        rxtype = _mem.rxcodeflag
867
        txtype = _mem.txcodeflag
868
        rx_tmode = TMODES[rxtype]
869
        tx_tmode = TMODES[txtype]
870

    
871
        rx_tone = tx_tone = None
872

    
873
        if tx_tmode == "Tone":
874
            if _mem.txcode < len(CTCSS_TONES):
875
                tx_tone = CTCSS_TONES[_mem.txcode]
876
            else:
877
                tx_tone = 0
878
                tx_tmode = ""
879
        elif tx_tmode == "DTCS":
880
            if _mem.txcode < len(DTCS_CODES):
881
                tx_tone = DTCS_CODES[_mem.txcode]
882
            else:
883
                tx_tone = 0
884
                tx_tmode = ""
885

    
886
        if rx_tmode == "Tone":
887
            if _mem.rxcode < len(CTCSS_TONES):
888
                rx_tone = CTCSS_TONES[_mem.rxcode]
889
            else:
890
                rx_tone = 0
891
                rx_tmode = ""
892
        elif rx_tmode == "DTCS":
893
            if _mem.rxcode < len(DTCS_CODES):
894
                rx_tone = DTCS_CODES[_mem.rxcode]
895
            else:
896
                rx_tone = 0
897
                rx_tmode = ""
898

    
899
        tx_pol = txtype == 0x03 and "R" or "N"
900
        rx_pol = rxtype == 0x03 and "R" or "N"
901

    
902
        chirp_common.split_tone_decode(mem, (tx_tmode, tx_tone, tx_pol),
903
                                       (rx_tmode, rx_tone, rx_pol))
904

    
905
    # Extract a high-level memory object from the low-level memory map
906
    # This is called to populate a memory in the UI
907
    def get_memory(self, number2):
908

    
909
        mem = chirp_common.Memory()
910

    
911
        if isinstance(number2, str):
912
            number = SPECIALS[number2]
913
            mem.extd_number = number2
914
        else:
915
            number = number2 - 1
916

    
917
        mem.number = number + 1
918

    
919
        _mem = self._memobj.channel[number]
920

    
921
        tmpcomment = ""
922

    
923
        is_empty = False
924
        # We'll consider any blank (i.e. 0 MHz frequency) to be empty
925
        if (_mem.freq == 0xffffffff) or (_mem.freq == 0):
926
            is_empty = True
927

    
928
        tmpscn = SCANLIST_LIST[0]
929

    
930
        # We'll also look at the channel attributes if a memory has them
931
        if number < 200:
932
            _mem3 = self._memobj.channel_attributes[number]
933
            # free memory bit
934
            if _mem3.is_free > 0:
935
                is_empty = True
936
            # scanlists
937
            if _mem3.is_scanlist1 > 0 and _mem3.is_scanlist2 > 0:
938
                tmpscn = SCANLIST_LIST[3]  # "1+2"
939
            elif _mem3.is_scanlist1 > 0:
940
                tmpscn = SCANLIST_LIST[1]  # "1"
941
            elif _mem3.is_scanlist2 > 0:
942
                tmpscn = SCANLIST_LIST[2]  # "2"
943

    
944
        if is_empty:
945
            mem.empty = True
946
            # set some sane defaults:
947
            mem.power = UVK5_POWER_LEVELS[2]
948
            mem.extra = RadioSettingGroup("Extra", "extra")
949
            
950
            rs = RadioSetting(
951
                "bandwidth", "Bandwidth",
952
                RadioSettingValueList(BANDWIDTH_LIST, BANDWIDTH_LIST[0]))
953
            mem.extra.append(rs)
954

    
955
            rs = RadioSetting(
956
                "bclo", "BCLO",
957
                RadioSettingValueBoolean(False))
958
            mem.extra.append(rs)
959
            rs = RadioSetting(
960
                "frev", "FreqRev",
961
                RadioSettingValueBoolean(False))
962
            mem.extra.append(rs)
963
            rs = RadioSetting(
964
                "pttid", "PTTID",
965
                RadioSettingValueList(PTTID_LIST, PTTID_LIST[0]))
966
            mem.extra.append(rs)
967
            rs = RadioSetting(
968
                "dtmfdecode", _("DTMF decode"),
969
                RadioSettingValueBoolean(False))
970
            mem.extra.append(rs)
971
            rs = RadioSetting(
972
                "scrambler", _("Scrambler"),
973
                RadioSettingValueList(SCRAMBLER_LIST, SCRAMBLER_LIST[0]))
974
            mem.extra.append(rs)
975

    
976
            rs = RadioSetting(
977
                "scanlists", _("Scanlists"),
978
                RadioSettingValueList(SCANLIST_LIST, SCANLIST_LIST[0]))
979
            mem.extra.append(rs)
980

    
981
            # actually the step and duplex are overwritten by chirp based on
982
            # bandplan. they are here to document sane defaults for IARU r1
983
            # mem.tuning_step = 25.0
984
            # mem.duplex = ""
985

    
986
            return mem
987

    
988
        if number > 199:
989
            mem.immutable = ["name", "scanlists"]
990
        else:
991
            _mem2 = self._memobj.channelname[number]
992
            for char in _mem2.name:
993
                if str(char) == "\xFF" or str(char) == "\x00":
994
                    break
995
                mem.name += str(char)
996
            mem.name = mem.name.rstrip()
997

    
998
        # Convert your low-level frequency to Hertz
999
        mem.freq = int(_mem.freq)*10
1000
        mem.offset = int(_mem.offset)*10
1001

    
1002
        if (mem.offset == 0):
1003
            mem.duplex = ''
1004
        else:
1005
            if _mem.shift == FLAGS1_OFFSET_MINUS:
1006
                if _mem.freq == _mem.offset:
1007
                    # fake tx disable by setting tx to 0 MHz
1008
                    mem.duplex = 'off'
1009
                    mem.offset = 0
1010
                else:
1011
                    mem.duplex = '-'
1012
            elif _mem.shift == FLAGS1_OFFSET_PLUS:
1013
                mem.duplex = '+'
1014
            else:
1015
                mem.duplex = ''
1016

    
1017
        # tone data
1018
        self._get_tone(mem, _mem)
1019
    
1020
        # mode
1021
        if _mem.enable_extramodes > 0:
1022
            if _mem.enable_am > 0:
1023
                mem.mode = "CW"
1024
            else:
1025
                mem.mode = "USB"
1026
        else:
1027
            if _mem.enable_am > 0:
1028
                mem.mode = "AM"
1029
            else:
1030
                mem.mode = "FM"
1031

    
1032

    
1033
        # tuning step
1034
        tstep = _mem.step & 0xF
1035
        if tstep < len(STEPS):
1036
            mem.tuning_step = STEPS[tstep]
1037
        else:
1038
            mem.tuning_step = 0.02
1039

    
1040
        # power
1041
        if _mem.txpower == POWER_HIGH:
1042
            mem.power = UVK5_POWER_LEVELS[2]
1043
        elif _mem.txpower == POWER_MEDIUM:
1044
            mem.power = UVK5_POWER_LEVELS[1]
1045
        else:
1046
            mem.power = UVK5_POWER_LEVELS[0]
1047

    
1048
        # We'll consider any blank (i.e. 0 MHz frequency) to be empty
1049
        if (_mem.freq == 0xffffffff) or (_mem.freq == 0):
1050
            mem.empty = True
1051
        else:
1052
            mem.empty = False
1053

    
1054
        mem.extra = RadioSettingGroup("Extra", "extra")
1055

    
1056
         # bandwidth
1057
        bwidth = _mem.bandwidth
1058
        rs = RadioSetting("bandwidth", "Bandwidth", RadioSettingValueList(
1059
            BANDWIDTH_LIST, BANDWIDTH_LIST[bwidth]))
1060
        mem.extra.append(rs)
1061
        tmpcomment += "bandwidth:"+BANDWIDTH_LIST[bwidth]+" "
1062

    
1063
        # BCLO
1064
        is_bclo = bool(_mem.bclo > 0)
1065
        rs = RadioSetting("bclo", "BCLO", RadioSettingValueBoolean(is_bclo))
1066
        mem.extra.append(rs)
1067
        tmpcomment += "BCLO:"+(is_bclo and "ON" or "off")+" "
1068

    
1069
        # Frequency reverse - whatever that means, don't see it in the manual
1070
        is_frev = bool(_mem.freq_reverse > 0)
1071
        rs = RadioSetting("frev", "FreqRev", RadioSettingValueBoolean(is_frev))
1072
        mem.extra.append(rs)
1073
        tmpcomment += "FreqReverse:"+(is_frev and "ON" or "off")+" "
1074

    
1075
        # PTTID
1076
        pttid = _mem.dtmf_pttid
1077
        rs = RadioSetting("pttid", "PTTID", RadioSettingValueList(
1078
            PTTID_LIST, PTTID_LIST[pttid]))
1079
        mem.extra.append(rs)
1080
        tmpcomment += "PTTid:"+PTTID_LIST[pttid]+" "
1081

    
1082
        # DTMF DECODE
1083
        is_dtmf = bool(_mem.dtmf_decode > 0)
1084
        rs = RadioSetting("dtmfdecode", _("DTMF decode"),
1085
                          RadioSettingValueBoolean(is_dtmf))
1086
        mem.extra.append(rs)
1087
        tmpcomment += "DTMFdecode:"+(is_dtmf and "ON" or "off")+" "
1088

    
1089
        # Scrambler
1090
        if _mem.scrambler & 0x0f < len(SCRAMBLER_LIST):
1091
            enc = _mem.scrambler & 0x0f
1092
        else:
1093
            enc = 0
1094

    
1095
        rs = RadioSetting("scrambler", _("Scrambler"), RadioSettingValueList(
1096
            SCRAMBLER_LIST, SCRAMBLER_LIST[enc]))
1097
        mem.extra.append(rs)
1098
        tmpcomment += "Scrambler:"+SCRAMBLER_LIST[enc]+" "
1099

    
1100
        rs = RadioSetting("scanlists", _("Scanlists"), RadioSettingValueList(
1101
            SCANLIST_LIST, tmpscn))
1102
        mem.extra.append(rs)
1103

    
1104
        return mem
1105

    
1106
    def set_settings(self, settings):
1107
        _mem = self._memobj
1108
        
1109
        s1 = False
1110
        s2 = False
1111
        s3 = False 
1112

    
1113
        sv1 = False
1114
        sv2 = False
1115
        sv3 = False
1116
        sv4 = False
1117

    
1118
        for element in settings:
1119
            if not isinstance(element, RadioSetting):
1120
                self.set_settings(element)
1121
                continue
1122

    
1123
            # basic settings
1124

    
1125
            # call channel
1126
            if element.get_name() == "call_channel":
1127
                _mem.call_channel = int(element.value)-1
1128

    
1129
            # squelch
1130
            if element.get_name() == "squelch_a":
1131
                _mem.squelch_a = int(element.value)
1132

    
1133
            if element.get_name() == "squelch_b":
1134
                _mem.squelch_b = int(element.value)    
1135

    
1136
            # TOT
1137
            if element.get_name() == "max_talk_time":
1138
                _mem.max_talk_time = TALKTIME_LIST.index(str(element.value))
1139

    
1140
            # Beacon
1141
            if element.get_name() == "beacon":
1142
                _mem.beacon = BEACON_LIST.index(str(element.value))
1143

    
1144

    
1145
            # NOAA autoscan
1146
#            if element.get_name() == "noaa_autoscan":
1147
#                _mem.noaa_autoscan = element.value and 1 or 0
1148

    
1149
            # VOX switch
1150
            if element.get_name() == "vox_switch":
1151
                _mem.vox_switch = element.value and 1 or 0
1152

    
1153
            # vox level
1154
            if element.get_name() == "vox_level":
1155
                _mem.vox_level = int(element.value)-1
1156

    
1157
            # mic gain
1158
            if element.get_name() == "mic_gain":
1159
                _mem.mic_gain = MICGAIN_LIST.index(str(element.value))
1160
############################################### 0xF47 #####################################################################
1161
            # MicBar
1162
            if element.get_name() == "micbar":
1163
                tmp_micbar = element.value and 0x10 or 0x00 #tmp_micbar = element.value and 0x10 or 0x00
1164
                s1 = True
1165

    
1166
            if element.get_name() == "bl_mode":
1167
                tmp_bl_mode = element.value and 0x20 or 0x00 #tmp_bl_mode = element.value and 0x20 or 0x00
1168
                s2 = True
1169

    
1170
            if element.get_name() == "tx_enable":
1171
               tmp_tx_enable = element.value and 0x01 or 0x00 
1172
               s3 = True
1173
            #tmp_tx_enable = 0x01
1174

    
1175
            if (s1 and s2)and s3:
1176
                _mem.micbar = tmp_micbar | tmp_bl_mode | tmp_tx_enable
1177
                _mem.bl_mode = tmp_micbar | tmp_bl_mode | tmp_tx_enable
1178
                _mem.tx_enable = tmp_micbar |tmp_bl_mode | tmp_tx_enable
1179

    
1180
######################################### 0xF4F ###########################################################################           
1181
            # Single vfo BIT 1
1182
            if element.get_name() == "single_vfo":
1183
                tmp_single_vfo = element.value and 0x02 or 0x00    
1184
                sv1 = True
1185

    
1186
            # Signal Meter BIT 2
1187
            if element.get_name() == "signal_meter":
1188
                tmp_signal_meter = element.value and 0x04 or 0x00 #_mem.signal_meter = element.value and 0x04 or 0x00
1189
                sv2 = True
1190

    
1191
            # Satcom BIT 3
1192
            if element.get_name() == "satcom":
1193
                tmp_satcom = element.value and 0x08 or 0x00 
1194
                sv3 = True 
1195

    
1196
             # Upconv BIT 4 e 5
1197
            if element.get_name() == "upconv":
1198
                tmp_upconv = (UPCONV_LIST.index(str(element.value)) << 4) #il risultato viene spostat di 4 posizioni a sinistra 
1199
                sv4 = True 
1200
 
1201
            
1202
             ### Sommo in OR tutti BIT
1203
            if ((sv1 and sv2) and sv3 ) and sv4:
1204
                _mem.single_vfo = tmp_single_vfo | tmp_signal_meter | tmp_satcom | tmp_upconv
1205
                _mem.signal_meter =  tmp_single_vfo | tmp_signal_meter  | tmp_satcom | tmp_upconv
1206
                _mem.satcom =  tmp_single_vfo | tmp_signal_meter | tmp_satcom | tmp_upconv
1207
                _mem.upconv =  tmp_single_vfo | tmp_signal_meter | tmp_satcom | tmp_upconv
1208

    
1209
####################################################################################################################
1210
            # Channel display mode
1211
            if element.get_name() == "channel_display_mode":
1212
                _mem.channel_display_mode = CHANNELDISP_LIST.index(
1213
                    str(element.value))
1214

    
1215
            # Crossband receiving/transmitting
1216
            if element.get_name() == "crossband":
1217
                _mem.crossband = CROSSBAND_LIST.index(str(element.value))
1218

    
1219
            # Battery Save
1220
            if element.get_name() == "battery_save":
1221
                _mem.battery_save = BATSAVE_LIST.index(str(element.value))
1222

    
1223
            # Compander
1224
            if element.get_name() == "compander":
1225
                _mem.compander = (COMPANDER_LIST.index(str(element.value)) << 4) | 0x03
1226

    
1227
            # Dual Watch
1228
            if element.get_name() == "dualwatch":
1229
                _mem.dual_watch = DUALWATCH_LIST.index(str(element.value))
1230

    
1231
            # Band TX  
1232
            if element.get_name() == "bands_tx":
1233
                _mem.bands_tx = BANDS_TX_LIST.index(str(element.value))      
1234

    
1235
            # Backlight auto mode
1236
            if element.get_name() == "backlight_auto_mode":
1237
                _mem.backlight_auto_mode = \
1238
                        BACKLIGHT_LIST.index(str(element.value))
1239

    
1240
            # Tail tone elimination
1241
            if element.get_name() == "tail_note_elimination":
1242
                _mem.tail_note_elimination = element.value and 1 or 0
1243

    
1244
            # VFO Open
1245
#            if element.get_name() == "vfo_open":
1246
#                _mem.vfo_open = element.value and 1 or 0
1247

    
1248
            # Beep control
1249
            if element.get_name() == "beep_control":
1250
                _mem.beep_control = element.value and 1 or 0
1251

    
1252
            # Scan resume mode
1253
            if element.get_name() == "scan_resume_mode":
1254
                _mem.scan_resume_mode = SCANRESUME_LIST.index(
1255
                    str(element.value))
1256

    
1257
            # Keypad lock
1258
            if element.get_name() == "key_lock":
1259
                _mem.key_lock = element.value and 1 or 0
1260

    
1261
            # Auto keypad lock
1262
            if element.get_name() == "auto_keypad_lock":
1263
                _mem.auto_keypad_lock = element.value and 1 or 0
1264

    
1265
            # Power on display mode
1266
            if element.get_name() == "welcome_mode":
1267
                _mem.power_on_dispmode = WELCOME_LIST.index(str(element.value))
1268

    
1269
            # Keypad Tone
1270
#            if element.get_name() == "keypad_tone":
1271
#                _mem.keypad_tone = KEYPADTONE_LIST.index(str(element.value))
1272

    
1273
            # Language
1274
#            if element.get_name() == "language":
1275
#                _mem.language = LANGUAGE_LIST.index(str(element.value))
1276

    
1277
            # Alarm mode
1278
#            if element.get_name() == "alarm_mode":
1279
#                _mem.alarm_mode = ALARMMODE_LIST.index(str(element.value))
1280

    
1281
            # Reminding of end of talk
1282
            if element.get_name() == "reminding_of_end_talk":
1283
                _mem.reminding_of_end_talk = REMENDOFTALK_LIST.index(
1284
                    str(element.value))
1285

    
1286
            # Repeater tail tone elimination
1287
            if element.get_name() == "repeater_tail_elimination":
1288
                _mem.repeater_tail_elimination = RTE_LIST.index(
1289
                    str(element.value))
1290

    
1291

    
1292

    
1293
            # Logo string 1
1294
            if element.get_name() == "logo1":
1295
                b = str(element.value).rstrip("\x20\xff\x00")+"\x00"*15
1296
                _mem.logo_line1 = b[0:15]+"\x00"
1297

    
1298
            # Logo string 2
1299
            if element.get_name() == "logo2":
1300
                b = str(element.value).rstrip("\x20\xff\x00")+"\x00"*15
1301
                _mem.logo_line2 = b[0:15]+"\x00"
1302

    
1303
            # QRZ label
1304
            if element.get_name() == "qrz_label":
1305
                b = str(element.value).rstrip("\x20\xff\x00")+"\x00"*8
1306
                _mem.qrz_label = b[0:8]
1307

    
1308
            # unlock settings
1309

    
1310
            # FLOCK
1311
            if element.get_name() == "flock":
1312
                _mem.lock.flock = FLOCK_LIST.index(str(element.value))
1313

    
1314
            # 350TX
1315
#            if element.get_name() == "tx350":
1316
#                _mem.lock.tx350 = element.value and 1 or 0
1317

    
1318
            # 200TX
1319
#            if element.get_name() == "tx200":
1320
#                _mem.lock.tx200 = element.value and 1 or 0
1321

    
1322
            # 500TX
1323
#            if element.get_name() == "tx500":
1324
#                _mem.lock.tx500 = element.value and 1 or 0
1325

    
1326
            # 350EN
1327
#            if element.get_name() == "en350":
1328
#                _mem.lock.en350 = element.value and 1 or 0
1329

    
1330
            # SCREN
1331
#            if element.get_name() == "enscramble":
1332
#                _mem.lock.enscramble = element.value and 1 or 0
1333

    
1334
            # KILLED
1335
            # if element.get_name() == "killed":
1336
            #    _mem.lock.killed = element.value and 1 or 0
1337

    
1338
            # fm radio
1339
            for i in range(1, 21):
1340
                freqname = "FM_" + str(i)
1341
                if element.get_name() == freqname:
1342
                    val = str(element.value).strip()
1343
                    try:
1344
                        val2 = int(float(val)*10)
1345
                    except Exception:
1346
                        val2 = 0xffff
1347

    
1348
                    if val2 < FMMIN*10 or val2 > FMMAX*10:
1349
                        val2 = 0xffff
1350
#                        raise errors.InvalidValueError(
1351
#                                "FM radio frequency should be a value "
1352
#                                "in the range %.1f - %.1f" % (FMMIN , FMMAX))
1353
                    _mem.fmfreq[i-1] = val2
1354

    
1355
            # dtmf settings
1356
            if element.get_name() == "dtmf_side_tone":
1357
                _mem.dtmf_settings.side_tone = \
1358
                        element.value and 1 or 0
1359

    
1360
            if element.get_name() == "dtmf_separate_code":
1361
                _mem.dtmf_settings.separate_code = str(element.value)
1362

    
1363
            if element.get_name() == "dtmf_group_call_code":
1364
                _mem.dtmf_settings.group_call_code = element.value
1365

    
1366
            if element.get_name() == "dtmf_decode_response":
1367
                _mem.dtmf_settings.decode_response = \
1368
                        DTMF_DECODE_RESPONSE_LIST.index(str(element.value))
1369

    
1370
            if element.get_name() == "dtmf_auto_reset_time":
1371
                _mem.dtmf_settings.auto_reset_time = \
1372
                        int(int(element.value)/10)
1373

    
1374
            if element.get_name() == "dtmf_preload_time":
1375
                _mem.dtmf_settings.preload_time = \
1376
                        int(int(element.value)/10)
1377

    
1378
            if element.get_name() == "dtmf_first_code_persist_time":
1379
                _mem.dtmf_settings.first_code_persist_time = \
1380
                        int(int(element.value)/10)
1381

    
1382
            if element.get_name() == "dtmf_hash_persist_time":
1383
                _mem.dtmf_settings.hash_persist_time = \
1384
                        int(int(element.value)/10)
1385

    
1386
            if element.get_name() == "dtmf_code_persist_time":
1387
                _mem.dtmf_settings.code_persist_time = \
1388
                        int(int(element.value)/10)
1389

    
1390
            if element.get_name() == "dtmf_code_interval_time":
1391
                _mem.dtmf_settings.code_interval_time = \
1392
                        int(int(element.value)/10)
1393

    
1394
           # if element.get_name() == "dtmf_permit_remote_kill":
1395
           #     _mem.dtmf_settings.permit_remote_kill = \
1396
           #             element.value and 1 or 0
1397

    
1398
            if element.get_name() == "dtmf_dtmf_local_code":
1399
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8 
1400
                _mem.dtmf_settings_numbers.dtmf_local_code = k[0:8]  
1401

    
1402
            if element.get_name() == "dtmf_dtmf_up_code":
1403
                k = str(element.value).strip("\x20\xff\x00") + "\x00"*8
1404
                _mem.dtmf_settings_numbers.dtmf_up_code = k[0:8]
1405

    
1406
            if element.get_name() == "dtmf_dtmf_down_code":
1407
                k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8
1408
                _mem.dtmf_settings_numbers.dtmf_down_code = k[0:8]
1409

    
1410
            # if element.get_name() == "dtmf_kill_code":
1411
            #    k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1412
            #    _mem.dtmf_settings_numbers.kill_code = k[0:5]
1413

    
1414
            #if element.get_name() == "dtmf_revive_code":
1415
            #    k = str(element.value).strip("\x20\xff\x00") + "\x00"*5
1416
            #    _mem.dtmf_settings_numbers.revive_code = k[0:5]
1417

    
1418
            # dtmf contacts
1419
            for i in range(1, 17):
1420
                varname = "DTMF_" + str(i)
1421
                if element.get_name() == varname:
1422
                    k = str(element.value).rstrip("\x20\xff\x00") + "\x00"*8
1423
                    _mem.dtmfcontact[i-1].name = k[0:8]
1424

    
1425
                varnumname = "DTMFNUM_" + str(i)
1426
                if element.get_name() == varnumname:
1427
                    k = str(element.value).rstrip("\x20\xff\x00") + "\xff"*3
1428
                    _mem.dtmfcontact[i-1].number = k[0:3]
1429

    
1430
            # scanlist stuff
1431
            if element.get_name() == "scanlist_default":
1432
                val = (int(element.value) == 2) and 1 or 0
1433
                _mem.scanlist_default = val
1434

    
1435
            if element.get_name() == "scanlist1_priority_scan":
1436
                _mem.scanlist1_priority_scan = \
1437
                        element.value and 1 or 0
1438

    
1439
            if element.get_name() == "scanlist2_priority_scan":
1440
                _mem.scanlist2_priority_scan = \
1441
                        element.value and 1 or 0
1442

    
1443
            if element.get_name() == "scanlist1_priority_ch1" or \
1444
                    element.get_name() == "scanlist1_priority_ch2" or \
1445
                    element.get_name() == "scanlist2_priority_ch1" or \
1446
                    element.get_name() == "scanlist2_priority_ch2":
1447

    
1448
                val = int(element.value)
1449

    
1450
                if val > 200 or val < 1:
1451
                    val = 0xff
1452
                else:
1453
                    val -= 1
1454

    
1455
                if element.get_name() == "scanlist1_priority_ch1":
1456
                    _mem.scanlist1_priority_ch1 = val
1457
                if element.get_name() == "scanlist1_priority_ch2":
1458
                    _mem.scanlist1_priority_ch2 = val
1459
                if element.get_name() == "scanlist2_priority_ch1":
1460
                    _mem.scanlist2_priority_ch1 = val
1461
                if element.get_name() == "scanlist2_priority_ch2":
1462
                    _mem.scanlist2_priority_ch2 = val
1463

    
1464
            if element.get_name() == "key1_shortpress_action":
1465
                _mem.key1_shortpress_action = KEYACTIONS_LIST.index(
1466
                        str(element.value))
1467

    
1468
            if element.get_name() == "key1_longpress_action":
1469
                _mem.key1_longpress_action = KEYACTIONS_LIST.index(
1470
                        str(element.value))
1471

    
1472
            if element.get_name() == "key2_shortpress_action":
1473
                _mem.key2_shortpress_action = KEYACTIONS_LIST.index(
1474
                        str(element.value))
1475

    
1476
            if element.get_name() == "key2_longpress_action":
1477
                _mem.key2_longpress_action = KEYACTIONS_LIST.index(
1478
                        str(element.value))
1479

    
1480
            if element.get_name() == "nolimits":
1481
                LOG.warning("User expanded band limits")
1482
                self._expanded_limits = bool(element.value)
1483

    
1484
    def get_settings(self):
1485
        _mem = self._memobj
1486
        basic = RadioSettingGroup("basic", "Basic Settings")
1487
        keya = RadioSettingGroup("keya", "Programmable keys")
1488
        dtmf = RadioSettingGroup("dtmf", "DTMF/Selcall Settings")
1489
        dtmfc = RadioSettingGroup("dtmfc", "DTMF Contacts")
1490
        scanl = RadioSettingGroup("scn", "Scan Lists")
1491
        unlock = RadioSettingGroup("unlock", "Unlock Settings")
1492
        fmradio = RadioSettingGroup("fmradio", _("FM Radio"))
1493

    
1494
        roinfo = RadioSettingGroup("roinfo", _("Driver information"))
1495

    
1496
        top = RadioSettings(
1497
                basic, keya, dtmf, dtmfc, scanl, unlock, fmradio, roinfo)
1498

    
1499
        # Programmable keys
1500
        tmpval = int(_mem.key1_shortpress_action)
1501
        if tmpval >= len(KEYACTIONS_LIST):
1502
            tmpval = 0
1503
        rs = RadioSetting("key1_shortpress_action", "Side key 1 short press",
1504
                          RadioSettingValueList(
1505
                              KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
1506
        keya.append(rs)
1507

    
1508
        tmpval = int(_mem.key1_longpress_action)
1509
        if tmpval >= len(KEYACTIONS_LIST):
1510
            tmpval = 0
1511
        rs = RadioSetting("key1_longpress_action", "Side key 1 long press",
1512
                          RadioSettingValueList(
1513
                              KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
1514
        keya.append(rs)
1515

    
1516
        tmpval = int(_mem.key2_shortpress_action)
1517
        if tmpval >= len(KEYACTIONS_LIST):
1518
            tmpval = 0
1519
        rs = RadioSetting("key2_shortpress_action", "Side key 2 short press",
1520
                          RadioSettingValueList(
1521
                              KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
1522
        keya.append(rs)
1523

    
1524
        tmpval = int(_mem.key2_longpress_action)
1525
        if tmpval >= len(KEYACTIONS_LIST):
1526
            tmpval = 0
1527
        rs = RadioSetting("key2_longpress_action", "Side key 2 long press",
1528
                          RadioSettingValueList(
1529
                              KEYACTIONS_LIST, KEYACTIONS_LIST[tmpval]))
1530
        keya.append(rs)
1531

    
1532
        # DTMF settings
1533
        tmppr = bool(_mem.dtmf_settings.side_tone > 0)
1534
        rs = RadioSetting(
1535
                "dtmf_side_tone",
1536
                "DTMF/SELCALL Sidetone",
1537
                RadioSettingValueBoolean(tmppr))
1538
        dtmf.append(rs)
1539

    
1540
        tmpval = str(_mem.dtmf_settings.separate_code)
1541
        if tmpval not in DTMF_CODE_CHARS:
1542
            tmpval = '*'
1543
        val = RadioSettingValueString(1, 1, tmpval)
1544
        val.set_charset(DTMF_CODE_CHARS)
1545
        rs = RadioSetting("dtmf_separate_code", "Separate Code", val)
1546
        dtmf.append(rs)
1547

    
1548
        tmpval = str(_mem.dtmf_settings.group_call_code)
1549
        if tmpval not in DTMF_CODE_CHARS:
1550
            tmpval = '#'
1551
        val = RadioSettingValueString(1, 1, tmpval)
1552
        val.set_charset(DTMF_CODE_CHARS)
1553
        rs = RadioSetting("dtmf_group_call_code", "Group Call Code", val)
1554
        dtmf.append(rs)
1555

    
1556
        tmpval = _mem.dtmf_settings.decode_response
1557
        if tmpval >= len(DTMF_DECODE_RESPONSE_LIST):
1558
            tmpval = 0
1559
        rs = RadioSetting("dtmf_decode_response", "Decode Response",
1560
                          RadioSettingValueList(
1561
                              DTMF_DECODE_RESPONSE_LIST,
1562
                              DTMF_DECODE_RESPONSE_LIST[tmpval]))
1563
        dtmf.append(rs)
1564

    
1565
        tmpval = _mem.dtmf_settings.auto_reset_time
1566
        if tmpval > 60 or tmpval < 5:
1567
            tmpval = 5
1568
        rs = RadioSetting("dtmf_auto_reset_time",
1569
                          "Auto reset time (s)",
1570
                          RadioSettingValueInteger(5, 60, tmpval))
1571
        dtmf.append(rs)
1572

    
1573
        tmpval = int(_mem.dtmf_settings.preload_time)
1574
        if tmpval > 100 or tmpval < 3:
1575
            tmpval = 30
1576
        tmpval *= 10
1577
        rs = RadioSetting("dtmf_preload_time",
1578
                          "Pre-load time (ms)",
1579
                          RadioSettingValueInteger(30, 1000, tmpval, 10))
1580
        dtmf.append(rs)
1581

    
1582
        tmpval = int(_mem.dtmf_settings.first_code_persist_time)
1583
        if tmpval > 100 or tmpval < 3:
1584
            tmpval = 30
1585
        tmpval *= 10
1586
        rs = RadioSetting("dtmf_first_code_persist_time",
1587
                          "First code persist time (ms)",
1588
                          RadioSettingValueInteger(30, 1000, tmpval, 10))
1589
        dtmf.append(rs)
1590

    
1591
        tmpval = int(_mem.dtmf_settings.hash_persist_time)
1592
        if tmpval > 100 or tmpval < 3:
1593
            tmpval = 30
1594
        tmpval *= 10
1595
        rs = RadioSetting("dtmf_hash_persist_time",
1596
                          "#/* persist time (ms)",
1597
                          RadioSettingValueInteger(30, 1000, tmpval, 10))
1598
        dtmf.append(rs)
1599

    
1600
        tmpval = int(_mem.dtmf_settings.code_persist_time)
1601
        if tmpval > 100 or tmpval < 3:
1602
            tmpval = 30
1603
        tmpval *= 10
1604
        rs = RadioSetting("dtmf_code_persist_time",
1605
                          "Code persist time (ms)",
1606
                          RadioSettingValueInteger(30, 1000, tmpval, 10))
1607
        dtmf.append(rs)
1608

    
1609
        tmpval = int(_mem.dtmf_settings.code_interval_time)
1610
        if tmpval > 100 or tmpval < 3:
1611
            tmpval = 30
1612
        tmpval *= 10
1613
        rs = RadioSetting("dtmf_code_interval_time",
1614
                          "Code interval time (ms)",
1615
                          RadioSettingValueInteger(30, 1000, tmpval, 10))
1616
        dtmf.append(rs)
1617

    
1618
        # tmpval = bool(_mem.dtmf_settings.permit_remote_kill > 0)
1619
        # rs = RadioSetting(
1620
        #        "dtmf_permit_remote_kill",
1621
        #        "Permit remote kill",
1622
        #        RadioSettingValueBoolean(tmpval))
1623
        # dtmf.append(rs)
1624

    
1625
        tmpval = str(_mem.dtmf_settings_numbers.dtmf_local_code).upper().strip(
1626
                "\x00\xff\x20")
1627
        for i in tmpval:
1628
            if i in DTMF_CHARS_ID:
1629
                continue
1630
            else:
1631
                tmpval = "103"
1632
                break
1633
        val = RadioSettingValueString(1, 8, tmpval)
1634
        val.set_charset(DTMF_CHARS_ID)
1635
        rs = RadioSetting("dtmf_dtmf_local_code",
1636
                          "Own ID (1-8 chars 0-9 ABCD)", val)
1637
        dtmf.append(rs)
1638

    
1639
        tmpval = str(_mem.dtmf_settings_numbers.dtmf_up_code).upper().strip(
1640
                "\x00\xff\x20")
1641
        for i in tmpval:
1642
            if i in DTMF_CHARS_UPDOWN or i == "":
1643
                continue
1644
            else:
1645
                tmpval = "123"
1646
                break
1647
        val = RadioSettingValueString(1, 8, tmpval)
1648
        val.set_charset(DTMF_CHARS_UPDOWN)
1649
        rs = RadioSetting("dtmf_dtmf_up_code",
1650
                          "Up code (1-8 chars 0-9 ABCD*#)", val)
1651
        dtmf.append(rs)
1652

    
1653
        tmpval = str(_mem.dtmf_settings_numbers.dtmf_down_code).upper().strip(
1654
                "\x00\xff\x20")
1655
        for i in tmpval:
1656
            if i in DTMF_CHARS_UPDOWN:
1657
                continue
1658
            else:
1659
                tmpval = "456"
1660
                break
1661
        val = RadioSettingValueString(1, 8, tmpval)
1662
        val.set_charset(DTMF_CHARS_UPDOWN)
1663
        rs = RadioSetting("dtmf_dtmf_down_code",
1664
                          "Down code (1-8 chars 0-9 ABCD*#)", val)
1665
        dtmf.append(rs)
1666

    
1667
        tmpval = str(_mem.dtmf_settings_numbers.kill_code).upper().strip(
1668
                "\x00\xff\x20")
1669
        # for i in tmpval:
1670
        #    if i in DTMF_CHARS_KILL:
1671
        #        continue
1672
        #    else:
1673
        #        tmpval = "77777"
1674
        #        break
1675
        # if not len(tmpval) == 5:
1676
        #    tmpval = "77777"
1677
        # val = RadioSettingValueString(5, 5, tmpval)
1678
        # val.set_charset(DTMF_CHARS_KILL)
1679
        # rs = RadioSetting("dtmf_kill_code",
1680
        #                  "Kill code (5 chars 0-9 ABCD)", val)
1681
        # dtmf.append(rs)
1682

    
1683
        # tmpval = str(_mem.dtmf_settings_numbers.revive_code).upper().strip(
1684
        #        "\x00\xff\x20")
1685
        #for i in tmpval:
1686
        #    if i in DTMF_CHARS_KILL:
1687
        #        continue
1688
        #    else:
1689
        #        tmpval = "88888"
1690
        #        break
1691
        #if not len(tmpval) == 5:
1692
        #    tmpval = "88888"
1693
        #val = RadioSettingValueString(5, 5, tmpval)
1694
        #val.set_charset(DTMF_CHARS_KILL)
1695
        #rs = RadioSetting("dtmf_revive_code",
1696
        #                  "Revive code (5 chars 0-9 ABCD)", val)
1697
        #dtmf.append(rs)
1698

    
1699
        val = RadioSettingValueString(0, 80,
1700
                                      "All DTMF Contacts are 3 codes "
1701
                                      "(valid: 0-9 * # ABCD), "
1702
                                      "or an empty string")
1703
        val.set_mutable(False)
1704
        rs = RadioSetting("dtmf_descr1", "DTMF Contacts", val)
1705
        dtmfc.append(rs)
1706

    
1707
        for i in range(1, 17):
1708
            varname = "DTMF_"+str(i)
1709
            varnumname = "DTMFNUM_"+str(i)
1710
            vardescr = "DTMF Contact "+str(i)+" name"
1711
            varinumdescr = "DTMF Contact "+str(i)+" number"
1712

    
1713
            cntn = str(_mem.dtmfcontact[i-1].name).strip("\x20\x00\xff")
1714
            cntnum = str(_mem.dtmfcontact[i-1].number).strip("\x20\x00\xff")
1715

    
1716
            val = RadioSettingValueString(0, 8, cntn)
1717
            rs = RadioSetting(varname, vardescr, val)
1718
            dtmfc.append(rs)
1719

    
1720
            val = RadioSettingValueString(0, 3, cntnum)
1721
            val.set_charset(DTMF_CHARS)
1722
            rs = RadioSetting(varnumname, varinumdescr, val)
1723
            dtmfc.append(rs)
1724

    
1725
        # scanlists
1726
        if _mem.scanlist_default == 1:
1727
            tmpsc = 2
1728
        else:
1729
            tmpsc = 1
1730
        rs = RadioSetting("scanlist_default",
1731
                          "Default scanlist",
1732
                          RadioSettingValueInteger(1, 2, tmpsc))
1733
        scanl.append(rs)
1734

    
1735
        tmppr = bool((_mem.scanlist1_priority_scan & 1) > 0)
1736
        rs = RadioSetting(
1737
                "scanlist1_priority_scan",
1738
                "Scanlist 1 priority channel scan",
1739
                RadioSettingValueBoolean(tmppr))
1740
        scanl.append(rs)
1741

    
1742
        tmpch = _mem.scanlist1_priority_ch1 + 1
1743
        if tmpch > 200:
1744
            tmpch = 0
1745
        rs = RadioSetting("scanlist1_priority_ch1",
1746
                          "Scanlist 1 priority channel 1 (0 - off)",
1747
                          RadioSettingValueInteger(0, 200, tmpch))
1748
        scanl.append(rs)
1749

    
1750
        tmpch = _mem.scanlist1_priority_ch2 + 1
1751
        if tmpch > 200:
1752
            tmpch = 0
1753
        rs = RadioSetting("scanlist1_priority_ch2",
1754
                          "Scanlist 1 priority channel 2 (0 - off)",
1755
                          RadioSettingValueInteger(0, 200, tmpch))
1756
        scanl.append(rs)
1757

    
1758
        tmppr = bool((_mem.scanlist2_priority_scan & 1) > 0)
1759
        rs = RadioSetting(
1760
                "scanlist2_priority_scan",
1761
                "Scanlist 2 priority channel scan",
1762
                RadioSettingValueBoolean(tmppr))
1763
        scanl.append(rs)
1764

    
1765
        tmpch = _mem.scanlist2_priority_ch1 + 1
1766
        if tmpch > 200:
1767
            tmpch = 0
1768
        rs = RadioSetting("scanlist2_priority_ch1",
1769
                          "Scanlist 2 priority channel 1 (0 - off)",
1770
                          RadioSettingValueInteger(0, 200, tmpch))
1771
        scanl.append(rs)
1772

    
1773
        tmpch = _mem.scanlist2_priority_ch2 + 1
1774
        if tmpch > 200:
1775
            tmpch = 0
1776
        rs = RadioSetting("scanlist2_priority_ch2",
1777
                          "Scanlist 2 priority channel 2 (0 - off)",
1778
                          RadioSettingValueInteger(0, 200, tmpch))
1779
        scanl.append(rs)
1780

    
1781
        # basic settings
1782

    
1783
        # squelch
1784
        tmpsq = _mem.squelch_a
1785
        if tmpsq > 9:
1786
            tmpsq = 1
1787
        rs = RadioSetting("squelch_a", "Squelch A",
1788
                          RadioSettingValueInteger(0, 9, tmpsq))
1789
        basic.append(rs)
1790

    
1791
        tmpsq = _mem.squelch_b
1792
        if tmpsq > 9:
1793
            tmpsq = 1
1794
        rs = RadioSetting("squelch_b", "Squelch B",
1795
                          RadioSettingValueInteger(0, 9, tmpsq))
1796
        basic.append(rs)
1797

    
1798
        # TOT
1799
        tmptot = _mem.max_talk_time
1800
        if tmptot >= len(TALKTIME_LIST):
1801
            tmptot = TALKTIME_LIST.index("2min")
1802
        rs = RadioSetting(
1803
                "max_talk_time",
1804
                "Max talk time (Tx TOT)",
1805
                RadioSettingValueList(
1806
                    TALKTIME_LIST,
1807
                    TALKTIME_LIST[tmptot]))
1808
        basic.append(rs)
1809

    
1810
        # Channel display mode
1811
        tmpchdispmode = _mem.channel_display_mode
1812
        if tmpchdispmode >= len(CHANNELDISP_LIST):
1813
            tmpchdispmode = 0
1814
        rs = RadioSetting(
1815
                "channel_display_mode",
1816
                "Channel Display mode",
1817
                RadioSettingValueList(
1818
                    CHANNELDISP_LIST,
1819
                    CHANNELDISP_LIST[tmpchdispmode]))
1820
        basic.append(rs)
1821

    
1822
        # Backlight auto mode
1823
        tmpback = _mem.backlight_auto_mode
1824
        if tmpback >= len(BACKLIGHT_LIST):
1825
            tmpback = 0
1826
        rs = RadioSetting("backlight_auto_mode",
1827
                          "BackLightTtime",
1828
                          RadioSettingValueList(
1829
                              BACKLIGHT_LIST,
1830
                              BACKLIGHT_LIST[tmpback]))
1831
        basic.append(rs)
1832

    
1833
        # BLMode
1834
        rs = RadioSetting(
1835
                "bl_mode",
1836
                "BLmode (TX/RX)", RadioSettingValueBoolean(bool((_mem.bl_mode & 0x20) > 0)))
1837
        basic.append(rs)
1838

    
1839
        # TX Enable
1840
        rs = RadioSetting(
1841
                "tx_enable",
1842
                "TX Enable", RadioSettingValueBoolean(bool((_mem.tx_enable & 0x01) > 0)))
1843
        unlock.append(rs)
1844

    
1845
        # Beep control
1846
        rs = RadioSetting(
1847
                "beep_control",
1848
                "Beep control",
1849
                RadioSettingValueBoolean(bool(_mem.beep_control > 0)))
1850
        basic.append(rs)
1851

    
1852
        # Scan resume mode
1853
        tmpscanres = _mem.scan_resume_mode
1854
        if tmpscanres >= len(SCANRESUME_LIST):
1855
            tmpscanres = 0
1856
        rs = RadioSetting(
1857
                "scan_resume_mode",
1858
                "Scan resume mode (Sc REV)",
1859
                RadioSettingValueList(
1860
                    SCANRESUME_LIST,
1861
                    SCANRESUME_LIST[tmpscanres]))
1862
        basic.append(rs)
1863

    
1864
        # Keypad locked
1865
        rs = RadioSetting(
1866
                "key_lock",
1867
                "Keypad Lock",
1868
                RadioSettingValueBoolean(bool(_mem.key_lock > 0)))
1869
        basic.append(rs)
1870

    
1871
        # Auto keypad lock
1872
        rs = RadioSetting(
1873
                "auto_keypad_lock",
1874
                "Auto keypad lock",
1875
                RadioSettingValueBoolean(bool(_mem.auto_keypad_lock > 0)))
1876
        basic.append(rs)
1877

    
1878
        # Tail tone elimination
1879
        rs = RadioSetting(
1880
                "tail_note_elimination",
1881
                "Tail tone elimination",
1882
                RadioSettingValueBoolean(
1883
                    bool(_mem.tail_note_elimination > 0)))
1884
        basic.append(rs)
1885

    
1886
        # Repeater tail tone elimination
1887
        tmprte = _mem.repeater_tail_elimination
1888
        if tmprte >= len(RTE_LIST):
1889
            tmprte = 0
1890
        rs = RadioSetting(
1891
                "repeater_tail_elimination",
1892
                "Repeater Tail Tone Elimination",
1893
                RadioSettingValueList(RTE_LIST, RTE_LIST[tmprte]))
1894
        basic.append(rs)
1895

    
1896
        # Mic gain
1897
        tmpmicgain = _mem.mic_gain
1898
        if tmpmicgain >= len(MICGAIN_LIST):
1899
            tmpmicgain = MICGAIN_LIST.index("+12.0dB")
1900
        rs = RadioSetting(
1901
                "mic_gain",
1902
                "Mic Gain",
1903
                RadioSettingValueList(
1904
                    MICGAIN_LIST,
1905
                    MICGAIN_LIST[tmpmicgain]))
1906
        basic.append(rs)
1907
####################################### 0xF47 ############################################################
1908
        # MicBar
1909
        rs = RadioSetting(
1910
                "micbar",
1911
                "MicBar", RadioSettingValueBoolean(bool((_mem.micbar & 0x10) > 0)))
1912
        basic.append(rs)
1913

    
1914
        # Compander
1915
        tmpcompander = _mem.compander >> 4
1916
        if tmpcompander >= len(COMPANDER_LIST):
1917
            tmpcompander = COMPANDER_LIST.index("Off")
1918
        rs = RadioSetting(
1919
                "compander",
1920
                "Compander",
1921
                RadioSettingValueList(
1922
                    COMPANDER_LIST,
1923
                    COMPANDER_LIST[tmpcompander]))
1924
        basic.append(rs)
1925

    
1926
        # VOX switch
1927
        rs = RadioSetting(
1928
                "vox_switch",
1929
                "VOX enabled", RadioSettingValueBoolean(
1930
                    bool(_mem.vox_switch > 0)))
1931
        basic.append(rs)
1932

    
1933
        # VOX Level
1934
        tmpvox = _mem.vox_level+1
1935
        if tmpvox > 10:
1936
            tmpvox = 10
1937
        rs = RadioSetting("vox_level", "VOX Level",
1938
                          RadioSettingValueInteger(1, 10, tmpvox))
1939
        basic.append(rs)
1940

    
1941
        # call channel
1942
        tmpc = _mem.call_channel+1
1943
        if tmpc > 200:
1944
            tmpc = 1
1945
        rs = RadioSetting("call_channel", "One key call channel",
1946
                          RadioSettingValueInteger(1, 200, tmpc))
1947
        basic.append(rs)
1948

    
1949
        # Reminding of end of talk
1950
        tmpalarmmode = _mem.reminding_of_end_talk
1951
        if tmpalarmmode >= len(REMENDOFTALK_LIST):
1952
            tmpalarmmode = 0
1953
        rs = RadioSetting(
1954
                "reminding_of_end_talk",
1955
                "Reminding of end of talk (DigSRV)",
1956
                RadioSettingValueList(
1957
                    REMENDOFTALK_LIST,
1958
                    REMENDOFTALK_LIST[tmpalarmmode]))
1959
        basic.append(rs)
1960
        
1961
        # Beacon
1962
        tmpbeacon = _mem.beacon
1963
        if tmpbeacon >= len(BEACON_LIST):
1964
            tmpbeacon = BEACON_LIST.index("Off")
1965
        rs = RadioSetting(
1966
                "beacon",
1967
                "Beacon/CQ Call",
1968
                RadioSettingValueList(
1969
                    BEACON_LIST,
1970
                    BEACON_LIST[tmpbeacon]))
1971
        basic.append(rs)
1972

    
1973

    
1974
        # Battery save
1975
        tmpbatsave = _mem.battery_save
1976
        if tmpbatsave >= len(BATSAVE_LIST):
1977
            tmpbatsave = BATSAVE_LIST.index("80%")
1978
        rs = RadioSetting(
1979
                "battery_save",
1980
                "Battery Save",
1981
                RadioSettingValueList(
1982
                    BATSAVE_LIST,
1983
                    BATSAVE_LIST[tmpbatsave]))
1984
        basic.append(rs)
1985

    
1986
################################################# 0xF4F ##########################################################################
1987
        # Single VFO BIT 1 
1988
        rs = RadioSetting(
1989
                "single_vfo",
1990
                "Single VFO", RadioSettingValueBoolean(bool((_mem.single_vfo & 0x02) > 0)))
1991
        basic.append(rs)
1992

    
1993
        # RSSI / S Meter BIT 2
1994
        rs = RadioSetting(
1995
                "signal_meter",
1996
                "SMeter (instead of RSSI)", RadioSettingValueBoolean(bool((_mem.signal_meter & 0x04) > 0)))
1997
        basic.append(rs)
1998

    
1999
        # Satcom BIT 3
2000
        rs = RadioSetting(
2001
                "satcom",
2002
                "SATCOM", RadioSettingValueBoolean(bool((_mem.satcom & 0x08) > 0)))
2003
        basic.append(rs)
2004

    
2005
        # Upconv BIT 4 e 5
2006
        tmpupconv = _mem.upconv >> 4 # sposta il bit di 4 posizioni a destra 
2007
        if tmpupconv >= len(UPCONV_LIST):
2008
            tmpupconv = UPCONV_LIST.index("Off")
2009
        rs = RadioSetting(
2010
                "upconv",
2011
                "UP Converter", RadioSettingValueList(
2012
                    UPCONV_LIST,
2013
                    UPCONV_LIST[tmpupconv]))
2014
        basic.append(rs)
2015
        
2016
####################################################################################################################        
2017

    
2018
        # Crossband receiving/transmitting
2019
        tmpcross = _mem.crossband
2020
        if tmpcross >= len(CROSSBAND_LIST):
2021
            tmpcross = 0
2022
        rs = RadioSetting(
2023
                "crossband",
2024
                "Cross-band receiving/transmitting (Tx VFO)",
2025
                RadioSettingValueList(
2026
                    CROSSBAND_LIST,
2027
                    CROSSBAND_LIST[tmpcross]))
2028
        basic.append(rs)
2029

    
2030
        # Dual watch
2031
        tmpdual = _mem.dual_watch
2032
        if tmpdual >= len(DUALWATCH_LIST):
2033
            tmpdual = 0
2034
        rs = RadioSetting("dualwatch", "Dual Watch (DualRX)", RadioSettingValueList(
2035
            DUALWATCH_LIST, DUALWATCH_LIST[tmpdual]))
2036
        basic.append(rs)
2037

    
2038
        # Band TX
2039
        tmpbandstx = _mem.bands_tx
2040
        if tmpbandstx >= len(BANDS_TX_LIST):
2041
            tmpbandstx = 0
2042
        rs = RadioSetting("bands_tx", "Bands TX", RadioSettingValueList(
2043
            BANDS_TX_LIST, BANDS_TX_LIST[tmpbandstx]))
2044
        basic.append(rs)
2045

    
2046
        # Power on display mode
2047
        tmpdispmode = _mem.power_on_dispmode
2048
        if tmpdispmode >= len(WELCOME_LIST):
2049
            tmpdispmode = 0
2050
        rs = RadioSetting(
2051
                "welcome_mode",
2052
                "Power on display MSG",
2053
                RadioSettingValueList(
2054
                    WELCOME_LIST,
2055
                    WELCOME_LIST[tmpdispmode]))
2056
        basic.append(rs)
2057

    
2058
        # QRZ label
2059
        qrz_label = str(_mem.qrz_label).rstrip("\x20\x00\xff") + "\x00"
2060
        qrz_label = _getstring(qrz_label.encode('ascii', errors='ignore'), 0, 8)
2061
        rs = RadioSetting("qrz_label", _("QRA (8 characters)"),
2062
                          RadioSettingValueString(0, 8, qrz_label))
2063
        basic.append(rs)
2064

    
2065
        # Logo string 1
2066
        logo1 = str(_mem.logo_line1).strip("\x20\x00\xff") + "\x00"
2067
        logo1 = _getstring(logo1.encode('ascii', errors='ignore'), 0, 15)
2068
        rs = RadioSetting("logo1", _("Logo string 1 (15 characters)"),
2069
                          RadioSettingValueString(0, 15, logo1))
2070
        basic.append(rs)
2071

    
2072
        # Logo string 2
2073
        logo2 = str(_mem.logo_line2).strip("\x20\x00\xff") + "\x00"
2074
        logo2 = _getstring(logo2.encode('ascii', errors='ignore'), 0, 15)
2075
        rs = RadioSetting("logo2", _("Logo string 2 (15 characters)"),
2076
                          RadioSettingValueString(0, 15, logo2))
2077
        basic.append(rs)
2078

    
2079
        # NOAA autoscan
2080
#        rs = RadioSetting(
2081
#                "noaa_autoscan",
2082
#                "NOAA Autoscan (not implemented)", RadioSettingValueBoolean(
2083
#                    bool(_mem.noaa_autoscan > 0)))
2084
#        basic.append(rs)
2085

    
2086

    
2087
        # VFO open
2088
#        rs = RadioSetting("vfo_open", "VFO open (???)",
2089
#                          RadioSettingValueBoolean(bool(_mem.vfo_open > 0)))
2090
#        basic.append(rs)
2091

    
2092
        # Keypad Tone
2093
#        tmpkeypadtone = _mem.keypad_tone
2094
#        if tmpkeypadtone >= len(KEYPADTONE_LIST):
2095
#            tmpkeypadtone = 0
2096
#        rs = RadioSetting("keypad_tone", "Voice prompts (not implemented)", RadioSettingValueList(
2097
#            KEYPADTONE_LIST, KEYPADTONE_LIST[tmpkeypadtone]))
2098
#        basic.append(rs)
2099

    
2100
        # Language
2101
#        tmplanguage = _mem.language
2102
#        if tmplanguage >= len(LANGUAGE_LIST):
2103
#            tmplanguage = 0
2104
#        rs = RadioSetting("language", "Language (not implemented)", RadioSettingValueList(
2105
#            LANGUAGE_LIST, LANGUAGE_LIST[tmplanguage]))
2106
#        basic.append(rs)
2107

    
2108
        # Alarm mode
2109
#        tmpalarmmode = _mem.alarm_mode
2110
#        if tmpalarmmode >= len(ALARMMODE_LIST):
2111
#            tmpalarmmode = 0
2112
#        rs = RadioSetting("alarm_mode", "Alarm mode (not implemented)", RadioSettingValueList(
2113
#            ALARMMODE_LIST, ALARMMODE_LIST[tmpalarmmode]))
2114
#        basic.append(rs)
2115

    
2116
        # FM radio
2117
        for i in range(1, 21):
2118
            freqname = "FM_"+str(i)
2119
            fmfreq = _mem.fmfreq[i-1]/10.0
2120
            if fmfreq < FMMIN or fmfreq > FMMAX:
2121
                rs = RadioSetting(freqname, freqname,
2122
                                  RadioSettingValueString(0, 5, ""))
2123
            else:
2124
                rs = RadioSetting(freqname, freqname,
2125
                                  RadioSettingValueString(0, 5, str(fmfreq)))
2126

    
2127
            fmradio.append(rs)
2128

    
2129
        # unlock settings
2130

    
2131
        # F-LOCK
2132
        tmpflock = _mem.lock.flock
2133
        if tmpflock >= len(FLOCK_LIST):
2134
            tmpflock = 0
2135
        rs = RadioSetting(
2136
            "flock", "F-LOCK",
2137
            RadioSettingValueList(FLOCK_LIST, FLOCK_LIST[tmpflock]))
2138
        unlock.append(rs)
2139

    
2140
        # 350TX
2141
#        rs = RadioSetting("tx350", "350TX - unlock 350-400 MHz TX",
2142
#                          RadioSettingValueBoolean(
2143
#                              bool(_mem.lock.tx350 > 0)))
2144
#        unlock.append(rs)
2145

    
2146
        # Killed
2147
        # rs = RadioSetting("Killed", "KILLED Device was disabled (via DTMF)",
2148
        #                  RadioSettingValueBoolean(
2149
        #                      bool(_mem.lock.killed > 0)))
2150
        # unlock.append(rs)
2151

    
2152
        # 200TX
2153
#        rs = RadioSetting("tx200", "200TX - unlock 174-350 MHz TX",
2154
#                          RadioSettingValueBoolean(
2155
#                              bool(_mem.lock.tx200 > 0)))
2156
#        unlock.append(rs)
2157

    
2158
        # 500TX
2159
#        rs = RadioSetting("tx500", "500TX - unlock 500-600 MHz TX",
2160
#                          RadioSettingValueBoolean(
2161
#                              bool(_mem.lock.tx500 > 0)))
2162
#        unlock.append(rs)
2163

    
2164
        # 350EN
2165
#        rs = RadioSetting("en350", "350EN - unlock 350-400 MHz RX",
2166
#                          RadioSettingValueBoolean(
2167
#                              bool(_mem.lock.en350 > 0)))
2168
#        unlock.append(rs)
2169

    
2170
        # SCREEN
2171
#        rs = RadioSetting("scrambler", "SCREN - scrambler enable",
2172
#                          RadioSettingValueBoolean(
2173
#                              bool(_mem.lock.enscramble > 0)))
2174
#        unlock.append(rs)
2175

    
2176
        # readonly info
2177
        # Firmware
2178
        if self.FIRMWARE_VERSION == "":
2179
            firmware = "To get the firmware version please download"
2180
            "the image from the radio first"
2181
        else:
2182
            firmware = self.FIRMWARE_VERSION
2183

    
2184
        val = RadioSettingValueString(0, 128, firmware)
2185
        val.set_mutable(False)
2186
        rs = RadioSetting("fw_ver", "Firmware Version", val)
2187
        roinfo.append(rs)
2188

    
2189
        # No limits version for hacked firmware
2190
        val = RadioSettingValueBoolean(self._expanded_limits)
2191
        rs = RadioSetting("nolimits", "Limits disabled for modified firmware",
2192
                          val)
2193
        rs.set_warning(_(
2194
            'This should only be enabled if you are using modified firmware '
2195
            'that supports wider frequency coverage. Enabling this will cause '
2196
            'CHIRP not to enforce OEM restrictions and may lead to undefined '
2197
            'or unregulated behavior. Use at your own risk!'),
2198
            safe_value=False)
2199
        roinfo.append(rs)
2200

    
2201
        return top
2202

    
2203
    # Store details about a high-level memory to the memory map
2204
    # This is called when a user edits a memory in the UI
2205
    def set_memory(self, mem):
2206
        number = mem.number-1
2207

    
2208
        # Get a low-level memory object mapped to the image
2209
        _mem = self._memobj.channel[number]
2210
        _mem4 = self._memobj
2211
        # empty memory
2212
        if mem.empty:
2213
            _mem.set_raw("\xFF" * 16)
2214
            if number < 200:
2215
                _mem2 = self._memobj.channelname[number]
2216
                _mem2.set_raw("\xFF" * 16)
2217
                _mem4.channel_attributes[number].is_scanlist1 = 0
2218
                _mem4.channel_attributes[number].is_scanlist2 = 0
2219
                _mem4.channel_attributes[number].unknown1 = 0
2220
                _mem4.channel_attributes[number].unknown2 = 0
2221
                _mem4.channel_attributes[number].is_free = 1
2222
                _mem4.channel_attributes[number].band = 0x7
2223
            return mem
2224

    
2225
        # clean the channel memory, restore some bits if it was used before
2226
        if _mem.get_raw(asbytes=False)[0] == "\xff":
2227
            # this was an empty memory
2228
            _mem.set_raw("\x00" * 16)
2229
        else:
2230
            # this memory wasn't empty, save some bits that we don't know the
2231
            # meaning of, or that we don't support yet
2232
            prev_0a = _mem.get_raw()[0x0a] & SAVE_MASK_0A
2233
            prev_0b = _mem.get_raw()[0x0b] & SAVE_MASK_0B
2234
            prev_0c = _mem.get_raw()[0x0c] & SAVE_MASK_0C
2235
            prev_0d = _mem.get_raw()[0x0d] & SAVE_MASK_0D
2236
            prev_0e = _mem.get_raw()[0x0e] & SAVE_MASK_0E
2237
            prev_0f = _mem.get_raw()[0x0f] & SAVE_MASK_0F
2238
            _mem.set_raw("\x00" * 10 +
2239
                         chr(prev_0a) + chr(prev_0b) + chr(prev_0c) +
2240
                         chr(prev_0d) + chr(prev_0e) + chr(prev_0f))
2241

    
2242
        if number < 200:
2243
            _mem4.channel_attributes[number].is_scanlist1 = 0
2244
            _mem4.channel_attributes[number].is_scanlist2 = 0
2245
            _mem4.channel_attributes[number].unknown1 = 0
2246
            _mem4.channel_attributes[number].unknown2 = 0
2247
            _mem4.channel_attributes[number].is_free = 1
2248
            _mem4.channel_attributes[number].band = 0x7
2249

    
2250
        # find band
2251
        band = _find_band(self, mem.freq)
2252

    
2253
        # mode
2254
        if mem.mode == "FM":
2255
            _mem.enable_am = 0
2256
            _mem.enable_extramodes = 0
2257
        elif mem.mode == "AM":
2258
            _mem.enable_am = 1
2259
            _mem.enable_extramodes = 0
2260
        elif mem.mode == "USB":
2261
            _mem.enable_am = 0
2262
            _mem.enable_extramodes = 1
2263
        elif mem.mode == "CW":
2264
            _mem.enable_am = 1
2265
            _mem.enable_extramodes = 1
2266

    
2267

    
2268
        # frequency/offset
2269
        _mem.freq = mem.freq/10
2270
        _mem.offset = mem.offset/10
2271

    
2272
        if mem.duplex == "":
2273
            _mem.offset = 0
2274
            _mem.shift = 0
2275
        elif mem.duplex == '-':
2276
            _mem.shift = FLAGS1_OFFSET_MINUS
2277
        elif mem.duplex == '+':
2278
            _mem.shift = FLAGS1_OFFSET_PLUS
2279
        elif mem.duplex == 'off':
2280
            # we fake tx disable by setting the tx freq to 0 MHz
2281
            _mem.shift = FLAGS1_OFFSET_MINUS
2282
            _mem.offset = _mem.freq
2283

    
2284
        # set band
2285
        if number < 200:
2286
            _mem4.channel_attributes[number].is_free = 0
2287
            _mem4.channel_attributes[number].band = band
2288

    
2289
        # channels >200 are the 14 VFO chanells and don't have names
2290
        if number < 200:
2291
            _mem2 = self._memobj.channelname[number]
2292
            tag = mem.name.ljust(10) + "\x00"*6
2293
            _mem2.name = tag  # Store the alpha tag
2294

    
2295
        # tone data
2296
        self._set_tone(mem, _mem)
2297

    
2298
        # step
2299
        _mem.step = STEPS.index(mem.tuning_step)
2300

    
2301
        # tx power
2302
        if str(mem.power) == str(UVK5_POWER_LEVELS[2]):
2303
            _mem.txpower = POWER_HIGH
2304
        elif str(mem.power) == str(UVK5_POWER_LEVELS[1]):
2305
            _mem.txpower = POWER_MEDIUM
2306
        else:
2307
            _mem.txpower = POWER_LOW
2308

    
2309
        for setting in mem.extra:
2310
            sname = setting.get_name()
2311
            svalue = setting.value.get_value()
2312

    
2313
            if sname == "bandwidth":
2314
                _mem.bandwidth = BANDWIDTH_LIST.index(svalue)
2315

    
2316

    
2317
            if sname == "bclo":
2318
                _mem.bclo = svalue and 1 or 0
2319

    
2320
            if sname == "pttid":
2321
                _mem.dtmf_pttid = PTTID_LIST.index(svalue)
2322

    
2323
            if sname == "frev":
2324
                _mem.freq_reverse = svalue and 1 or 0
2325

    
2326
            if sname == "dtmfdecode":
2327
                _mem.dtmf_decode = svalue and 1 or 0
2328

    
2329
            if sname == "scrambler":
2330
                _mem.scrambler = (
2331
                    _mem.scrambler & 0xf0) | SCRAMBLER_LIST.index(svalue)
2332

    
2333
            if number < 200 and sname == "scanlists":
2334
                if svalue == "1":
2335
                    _mem4.channel_attributes[number].is_scanlist1 = 1
2336
                    _mem4.channel_attributes[number].is_scanlist2 = 0
2337
                elif svalue == "2":
2338
                    _mem4.channel_attributes[number].is_scanlist1 = 0
2339
                    _mem4.channel_attributes[number].is_scanlist2 = 1
2340
                elif svalue == "1+2":
2341
                    _mem4.channel_attributes[number].is_scanlist1 = 1
2342
                    _mem4.channel_attributes[number].is_scanlist2 = 1
2343
                else:
2344
                    _mem4.channel_attributes[number].is_scanlist1 = 0
2345
                    _mem4.channel_attributes[number].is_scanlist2 = 0
2346

    
2347
        return mem
2348

    
2349

    
2350
@directory.register
2351
class RA79Radio(UVK5Radio):
2352
    """Retevis RA79"""
2353
    VENDOR = "Retevis"
2354
    MODEL = "RA79"
(4-4/4)