Project

General

Profile

Bug #11370 » uvk5_CEC_05_HF_DO4KLA.py

Philippe Pichon, 06/04/2024 03:04 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

    
41
import struct
42
import logging
43

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

    
50
LOG = logging.getLogger(__name__)
51

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

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

    
61
DEBUG_WRITE_RADIO_HEXDUMP = True
62

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

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

    
94
} channel[207];
95

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

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

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

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

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

    
150
#seekto 0x0E98;
151
u32 POWER_ON_PASSWORD;
152

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

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

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

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

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

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

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

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

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

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

    
206
#seekto 0x0F30;
207
u8 bHasCustomAesKey;
208

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

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

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

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

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

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

    
260
# flags2
261

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

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

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

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

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

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

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

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

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

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

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

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

    
311
# steps
312
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]
313

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

    
322

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
447

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

    
462

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

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

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

    
482

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

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

    
500
    footer = serport.read(4)
501

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

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

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

    
519
    cmd2 = xorarr(cmd)
520

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

    
524
    return cmd2
525

    
526

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

    
535

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

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

    
554

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

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

    
568

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

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

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

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

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

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

    
597

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

    
602

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

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

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

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

    
631
    if DEBUG_WRITE_RADIO_HEXDUMP:
632
        hex_data = _convert_to_intel_hex(eeprom)
633
        _save_to_hex_file(hex_data, "/tmp/radio_dump.hex")
634

    
635
    return memmap.MemoryMapBytes(eeprom)
636

    
637

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

    
656

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

    
661

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

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

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

    
689
    _resetradio(serport)
690

    
691
    return True
692

    
693

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

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

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

    
710

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

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

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

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

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

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

    
774
        rf.valid_skips = [""]
775

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

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

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

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

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

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

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

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

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

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

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

    
831
        return msgs
832

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

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

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

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

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

    
870
        rx_tone = tx_tone = None
871

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

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

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

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

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

    
908
        mem = chirp_common.Memory()
909

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

    
916
        mem.number = number + 1
917

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

    
920
        tmpcomment = ""
921

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

    
927
        tmpscn = SCANLIST_LIST[0]
928

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

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

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

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

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

    
985
            return mem
986

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

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

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

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

    
1031

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

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

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

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

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

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

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

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

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

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

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

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

    
1103
        return mem
1104

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

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

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

    
1122
            # basic settings
1123

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

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

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

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

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

    
1143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1290

    
1291

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

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

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

    
1307
            # unlock settings
1308

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1447
                val = int(element.value)
1448

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1780
        # basic settings
1781

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1972

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2085

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

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

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

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

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

    
2126
            fmradio.append(rs)
2127

    
2128
        # unlock settings
2129

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

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

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

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

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

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

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

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

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

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

    
2200
        return top
2201

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

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

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

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

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

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

    
2266

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

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

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

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

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

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

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

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

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

    
2315

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

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

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

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

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

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

    
2346
        return mem
2347

    
2348

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