btech.py

Ray Strickoff, 01/07/2017 01:37 am

Download (95.4 kB)

 
1
# Copyright 2016:
2
# * Pavel Milanes CO7WT, <pavelmc@gmail.com>
3
# * Jim Unroe KC9HI, <rock.unroe@gmail.com>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17

    
18
import time
19
import struct
20
import logging
21

    
22
LOG = logging.getLogger(__name__)
23

    
24
from time import sleep
25
from chirp import chirp_common, directory, memmap
26
from chirp import bitwise, errors, util
27
from chirp.settings import RadioSettingGroup, RadioSetting, \
28
    RadioSettingValueBoolean, RadioSettingValueList, \
29
    RadioSettingValueString, RadioSettingValueInteger, \
30
    RadioSettingValueFloat, RadioSettings, InvalidValueError
31
from textwrap import dedent
32

    
33
MEM_FORMAT = """
34
#seekto 0x0000;
35
struct {
36
  lbcd rxfreq[4];
37
  lbcd txfreq[4];
38
  ul16 rxtone;
39
  ul16 txtone;
40
  u8 unknown0:4,
41
     scode:4;
42
  u8 unknown1:2,
43
     spmute:1,
44
     unknown2:3,
45
     optsig:2;
46
  u8 unknown3:3,
47
     scramble:1,
48
     unknown4:3,
49
     power:1;
50
  u8 unknown5:1,
51
     wide:1,
52
     unknown6:2,
53
     bcl:1,
54
     add:1,
55
     pttid:2;
56
} memory[200];
57

    
58
#seekto 0x0E00;
59
struct {
60
  u8 tdr;
61
  u8 unknown1;
62
  u8 sql;
63
  u8 unknown2[2];
64
  u8 tot;
65
  u8 apo;           // BTech radios use this as the Auto Power Off time
66
                    // other radios use this as pre-Time Out Alert
67
  u8 unknown3;
68
  u8 abr;
69
  u8 beep;
70
  u8 unknown4[4];
71
  u8 dtmfst;
72
  u8 unknown5[2];
73
  u8 prisc;
74
  u8 prich;
75
  u8 screv;
76
  u8 unknown6[2];
77
  u8 pttid;
78
  u8 pttlt;
79
  u8 unknown7;
80
  u8 emctp;
81
  u8 emcch;
82
  u8 ringt;
83
  u8 unknown8;
84
  u8 camdf;
85
  u8 cbmdf;
86
  u8 sync;          // BTech radios use this as the display sync setting
87
                    // other radios use this as the auto keypad lock setting
88
  u8 ponmsg;
89
  u8 wtled;
90
  u8 rxled;
91
  u8 txled;
92
  u8 unknown9[5];
93
  u8 anil;
94
  u8 reps;
95
  u8 repm;
96
  u8 tdrab;
97
  u8 ste;
98
  u8 rpste;
99
  u8 rptdl;
100
  u8 mgain;
101
  u8 dtmfg;
102
} settings;
103

    
104
#seekto 0x0E80;
105
struct {
106
  u8 unknown1;
107
  u8 vfomr;
108
  u8 keylock;
109
  u8 unknown2;
110
  u8 unknown3:4,
111
     vfomren:1,
112
     unknown4:1,
113
     reseten:1,
114
     menuen:1;
115
  u8 unknown5[11];
116
  u8 dispab;
117
  u8 mrcha;
118
  u8 mrchb;
119
  u8 menu;
120
} settings2;
121

    
122
#seekto 0x0EC0;
123
struct {
124
  char line1[6];
125
  char line2[6];
126
} poweron_msg;
127

    
128
struct settings_vfo {
129
  u8 freq[8];
130
  u8 unknown1;
131
  u8 offset[4];
132
  u8 unknown2[3];
133
  ul16 rxtone;
134
  ul16 txtone;
135
  u8 scode;
136
  u8 spmute;
137
  u8 optsig;
138
  u8 scramble;
139
  u8 wide;
140
  u8 power;
141
  u8 shiftd;
142
  u8 step;
143
  u8 unknown3[4];
144
};
145

    
146
#seekto 0x0F00;
147
struct {
148
  struct settings_vfo a;
149
  struct settings_vfo b;
150
} vfo;
151

    
152
#seekto 0x1000;
153
struct {
154
  char name[6];
155
  u8 unknown1[10];
156
} names[200];
157

    
158
#seekto 0x2400;
159
struct {
160
  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
161
  u8 group_tone;
162
  u8 repeat_tone;
163
  u8 unused[13];
164
} _5tone_std_settings[15];
165

    
166
#seekto 0x2500;
167
struct {
168
  u8 frame1[5];
169
  u8 frame2[5];
170
  u8 frame3[5];
171
  u8 standard;   // one out of LIST_5TONE_STANDARDS
172
} _5tone_codes[15];
173

    
174
#seekto 0x25F0;
175
struct {
176
  u8 _5tone_delay1; // * 10ms
177
  u8 _5tone_delay2; // * 10ms
178
  u8 _5tone_delay3; // * 10ms
179
  u8 _5tone_first_digit_ext_length;
180
  u8 unknown1;
181
  u8 unknown2;
182
  u8 unknown3;
183
  u8 unknown4;
184
  u8 decode_standard;
185
  u8 unknown5:5,
186
     _5tone_decode_call_frame3:1,
187
     _5tone_decode_call_frame2:1,
188
     _5tone_decode_call_frame1:1;
189
  u8 unknown6:5,
190
     _5tone_decode_disp_frame3:1,
191
     _5tone_decode_disp_frame2:1,
192
     _5tone_decode_disp_frame1:1;
193
  u8 decode_reset_time; // * 100 + 100ms
194
} _5tone_settings;
195

    
196
#seekto 0x2900;
197
struct {
198
  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
199
} dtmf_codes[15];
200

    
201
#seekto 0x29F0;
202
struct {
203
  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
204
  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
205
  u8 unknown0[14];
206
  u8 inspection[16];
207
  u8 monitor[16];
208
  u8 alarmcode[16];
209
  u8 stun[16];
210
  u8 kill[16];
211
  u8 revive[16];
212
  u8 unknown1[16];
213
  u8 unknown2[16];
214
  u8 unknown3[16];
215
  u8 unknown4[16];
216
  u8 unknown5[16];
217
  u8 unknown6[16];
218
  u8 unknown7[16];
219
  u8 masterid[16];
220
  u8 viceid[16];
221
  u8 unused01:7,
222
     mastervice:1;
223
  u8 unused02:3,
224
     mrevive:1,
225
     mkill:1,
226
     mstun:1,
227
     mmonitor:1,
228
     minspection:1;
229
  u8 unused03:3,
230
     vrevive:1,
231
     vkill:1,
232
     vstun:1,
233
     vmonitor:1,
234
     vinspection:1;
235
  u8 unused04:6,
236
     txdisable:1,
237
     rxdisable:1;
238
  u8 groupcode;
239
  u8 spacecode;
240
  u8 delayproctime; // * 100 + 100ms
241
  u8 resettime;     // * 100 + 100ms
242
} dtmf_settings;
243

    
244
#seekto 0x2D00;
245
struct {
246
  struct {
247
    ul16 freq1;
248
    u8 unused01[6];
249
    ul16 freq2;
250
    u8 unused02[6];
251
  } _2tone_encode[15];
252
  u8 duration_1st_tone; // *10ms
253
  u8 duration_2nd_tone; // *10ms
254
  u8 duration_gap;      // *10ms
255
  u8 unused03[13];
256
  struct {
257
    struct {
258
      u8 dec;      // one out of LIST_2TONE_DEC
259
      u8 response; // one out of LIST_2TONE_RESPONSE
260
      u8 alert;    // 1-16
261
    } decs[4];
262
    u8 unused04[4];
263
  } _2tone_decode[15];
264
  u8 unused05[16];
265

    
266
  struct {
267
    ul16 freqA;
268
    ul16 freqB;
269
    ul16 freqC;
270
    ul16 freqD;
271
    // unknown what those values mean, but they are
272
    // derived from configured frequencies
273
    ul16 derived_from_freqA; // 2304000/freqA
274
    ul16 derived_from_freqB; // 2304000/freqB
275
    ul16 derived_from_freqC; // 2304000/freqC
276
    ul16 derived_from_freqD; // 2304000/freqD
277
  }freqs[15];
278
  u8 reset_time;  // * 100 + 100ms - 100-8000ms
279
} _2tone;
280

    
281
#seekto 0x3000;
282
struct {
283
  u8 freq[8];
284
  char broadcast_station_name[6];
285
  u8 unknown[2];
286
} fm_radio_preset[16];
287

    
288
#seekto 0x3C90;
289
struct {
290
  u8 vhf_low[3];
291
  u8 vhf_high[3];
292
  u8 uhf_low[3];
293
  u8 uhf_high[3];
294
} ranges;
295

    
296
// the UV-2501+220 & KT8900R has different zones for storing ranges
297

    
298
#seekto 0x3CD0;
299
struct {
300
  u8 vhf_low[3];
301
  u8 vhf_high[3];
302
  u8 unknown1[4];
303
  u8 unknown2[6];
304
  u8 vhf2_low[3];
305
  u8 vhf2_high[3];
306
  u8 unknown3[4];
307
  u8 unknown4[6];
308
  u8 uhf_low[3];
309
  u8 uhf_high[3];
310
} ranges220;
311

    
312
#seekto 0x3F70;
313
struct {
314
  char fp[6];
315
} fingerprint;
316

    
317
"""
318

    
319
# A note about the memmory in these radios
320
#
321
# The real memory of these radios extends to 0x4000
322
# On read the factory software only uses up to 0x3200
323
# On write it just uploads the contents up to 0x3100
324
#
325
# The mem beyond 0x3200 holds the ID data
326

    
327
MEM_SIZE = 0x4000
328
BLOCK_SIZE = 0x40
329
TX_BLOCK_SIZE = 0x10
330
ACK_CMD = "\x06"
331
MODES = ["FM", "NFM"]
332
SKIP_VALUES = ["S", ""]
333
TONES = chirp_common.TONES
334
DTCS = sorted(chirp_common.DTCS_CODES + [645])
335
NAME_LENGTH = 6
336
PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
337
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
338
OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
339
SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
340

    
341
LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
342
LIST_TOA = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
343
LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
344
LIST_ABR = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
345
LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
346
LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
347
LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
348
LIST_RINGT = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
349
LIST_MDF = ["Frequency", "Channel", "Name"]
350
LIST_PONMSG = ["Full", "Message", "Battery voltage"]
351
LIST_COLOR = ["Off", "Blue", "Orange", "Purple"]
352
LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
353
LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
354
LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
355
LIST_ANIL = ["3", "4", "5"]
356
LIST_AB = ["A", "B"]
357
LIST_VFOMR = ["Frequency", "Channel"]
358
LIST_SHIFT = ["Off", "+", "-"]
359
LIST_TXP = ["High", "Low"]
360
LIST_WIDE = ["Wide", "Narrow"]
361
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
362
LIST_STEP = [str(x) for x in STEPS]
363
LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
364
                        "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
365
                        "CCITT", "NATEL", "MODAT", "none"]
366
LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
367
                                     "ZVEI2", "ZVEI3",
368
                                     "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
369
                                     "EURO", "CCITT", "NATEL", "MODAT"]
370
LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
371
                               "100", "110", "120", "130", "140", "150", "160",
372
                               "170", "180", "190", "200"]
373
LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
374
                     "B", "C", "D", "E", "F"]
375
LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
376
LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
377
LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
378
LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
379
                    "C", "D", "#", "*"]
380
LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
381
                    0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B ]
382
LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
383
LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
384
LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
385
CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
386
LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
387
                  "B-A", "B-C", "B-D",
388
                  "C-A", "C-B", "C-D",
389
                  "D-A", "D-B", "D-C"]
390
LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
391

    
392
# This is a general serial timeout for all serial read functions.
393
# Practice has show that about 0.7 sec will be enough to cover all radios.
394
STIMEOUT = 0.7
395

    
396
# this var controls the verbosity in the debug and by default it's low (False)
397
# make it True and you will to get a very verbose debug.log
398
debug = False
399

    
400
# Power Levels
401
NORMAL_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=25),
402
                       chirp_common.PowerLevel("Low", watts=10)]
403
UV5001_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=50),
404
                       chirp_common.PowerLevel("Low", watts=10)]
405

    
406
# this must be defined globaly
407
POWER_LEVELS = None
408

    
409
# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
410
VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
411
    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
412

    
413

    
414
##### ID strings #####################################################
415

    
416
# BTECH UV2501 pre-production units
417
UV2501pp_fp = "M2C294"
418
# BTECH UV2501 pre-production units 2 + and 1st Gen radios
419
UV2501pp2_fp = "M29204"
420
# B-TECH UV-2501 second generation (2G) radios
421
UV2501G2_fp = "BTG214"
422
# B-TECH UV-2501 third generation (3G) radios
423
UV2501G3_fp = "BTG324"
424

    
425
# B-TECH UV-2501+220 pre-production units
426
UV2501_220pp_fp = "M3C281"
427
# extra block read for the 2501+220 pre-production units
428
# the same for all of this radios so far
429
UV2501_220pp_id = "      280528"
430
# B-TECH UV-2501+220
431
UV2501_220_fp = "M3G201"
432
# new variant, let's call it Generation 2
433
UV2501_220G2_fp = "BTG211"
434
# B-TECH UV-2501+220 third generation (3G)
435
UV2501_220G3_fp = "BTG311"
436

    
437
# B-TECH UV-5001 pre-production units + 1st Gen radios
438
UV5001pp_fp = "V19204"
439
# B-TECH UV-5001 alpha units
440
UV5001alpha_fp = "V28204"
441
# B-TECH UV-5001 second generation (2G) radios
442
UV5001G2_fp = "BTG214"
443
# B-TECH UV-5001 second generation (2G2)
444
UV5001G22_fp = "V2G204"
445
# B-TECH UV-5001 third generation (3G)
446
UV5001G3_fp = "BTG304"
447

    
448
# special var to know when we found a BTECH Gen 3
449
BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
450

    
451

    
452
# WACCOM Mini-8900
453
MINI8900_fp = "M28854"
454

    
455

    
456
# QYT KT-UV980
457
KTUV980_fp = "H28854"
458

    
459
# QYT KT8900
460
KT8900_fp = "M29154"
461
# New generations KT8900
462
KT8900_fp1 = "M2C234"
463
KT8900_fp2 = "M2G1F4"
464
KT8900_fp3 = "M2G2F4"
465
KT8900_fp4 = "M2G304"
466
KT8900_fp5 = "M2G314"
467
# this radio has an extra ID
468
KT8900_id = "      303688"
469

    
470
# KT8900R
471
KT8900R_fp = "M3G1F4"
472
# Second Generation
473
KT8900R_fp1 = "M3G214"
474
# another model
475
KT8900R_fp2 = "M3C234"
476
# another model G4?
477
KT8900R_fp3 = "M39164"
478
# another model
479
KT8900R_fp4 = "M3G314"
480
# this radio has an extra ID
481
KT8900R_id = "280528"
482

    
483

    
484
# LUITON LT-588UV
485
LT588UV_fp = "V2G1F4"
486
# Added by rstrickoff gen 2 id
487
LT588UV_fp1 = "V2G214"
488

    
489

    
490
#### MAGICS
491
# for the Waccom Mini-8900
492
MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
493
# for the B-TECH UV-2501+220 (including pre production ones)
494
MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
495
# for the QYT KT8900 & R
496
MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
497
MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
498
# magic string for all other models
499
MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
500

    
501

    
502
def _clean_buffer(radio):
503
    """Cleaning the read serial buffer, hard timeout to survive an infinite
504
    data stream"""
505

    
506
    # touching the serial timeout to optimize the flushing
507
    # restored at the end to the default value
508
    radio.pipe.timeout = 0.1
509
    dump = "1"
510
    datacount = 0
511

    
512
    try:
513
        while len(dump) > 0:
514
            dump = radio.pipe.read(100)
515
            datacount += len(dump)
516
            # hard limit to survive a infinite serial data stream
517
            # 5 times bigger than a normal rx block (69 bytes)
518
            if datacount > 345:
519
                seriale = "Please check your serial port selection."
520
                raise errors.RadioError(seriale)
521

    
522
        # restore the default serial timeout
523
        radio.pipe.timeout = STIMEOUT
524

    
525
    except Exception:
526
        raise errors.RadioError("Unknown error cleaning the serial buffer")
527

    
528

    
529
def _rawrecv(radio, amount):
530
    """Raw read from the radio device, less intensive way"""
531

    
532
    data = ""
533

    
534
    try:
535
        data = radio.pipe.read(amount)
536

    
537
        # DEBUG
538
        if debug is True:
539
            LOG.debug("<== (%d) bytes:\n\n%s" %
540
                      (len(data), util.hexprint(data)))
541

    
542
        # fail if no data is received
543
        if len(data) == 0:
544
            raise errors.RadioError("No data received from radio")
545

    
546
        # notice on the logs if short
547
        if len(data) < amount:
548
            LOG.warn("Short reading %d bytes from the %d requested." %
549
                     (len(data), amount))
550

    
551
    except:
552
        raise errors.RadioError("Error reading data from radio")
553

    
554
    return data
555

    
556

    
557
def _send(radio, data):
558
    """Send data to the radio device"""
559

    
560
    try:
561
        for byte in data:
562
            radio.pipe.write(byte)
563
            # Some OS (mainly Linux ones) are too fast on the serial and
564
            # get the MCU inside the radio stuck in the early stages, this
565
            # hits some models more than others.
566
            #
567
            # To cope with that we introduce a delay on the writes.
568
            # Many option have been tested (delaying only after error occures, after short reads, only for linux, ...)
569
            # Finally, a static delay was chosen as simplest of all solutions (Michael Wagner, OE4AMW)
570
            # (for details, see issue 3993)
571
            sleep(0.002)
572

    
573
        # DEBUG
574
        if debug is True:
575
            LOG.debug("==> (%d) bytes:\n\n%s" %
576
                      (len(data), util.hexprint(data)))
577
    except:
578
        raise errors.RadioError("Error sending data to radio")
579

    
580

    
581
def _make_frame(cmd, addr, length, data=""):
582
    """Pack the info in the headder format"""
583
    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
584
    # add the data if set
585
    if len(data) != 0:
586
        frame += data
587

    
588
    return frame
589

    
590

    
591
def _recv(radio, addr):
592
    """Get data from the radio all at once to lower syscalls load"""
593

    
594
    # Get the full 69 bytes at a time to reduce load
595
    # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
596

    
597
    # get the whole block
598
    block = _rawrecv(radio, BLOCK_SIZE + 5)
599

    
600
    # basic check
601
    if len(block) < (BLOCK_SIZE + 5):
602
        raise errors.RadioError("Short read of the block 0x%04x" % addr)
603

    
604
    # checking for the ack
605
    if block[0] != ACK_CMD:
606
        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
607

    
608
    # header validation
609
    c, a, l = struct.unpack(">BHB", block[1:5])
610
    if a != addr or l != BLOCK_SIZE or c != ord("X"):
611
        LOG.debug("Invalid header for block 0x%04x" % addr)
612
        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
613
        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
614

    
615
    # return the data
616
    return block[5:]
617

    
618

    
619
def _start_clone_mode(radio, status):
620
    """Put the radio in clone mode and get the ident string, 3 tries"""
621

    
622
    # cleaning the serial buffer
623
    _clean_buffer(radio)
624

    
625
    # prep the data to show in the UI
626
    status.cur = 0
627
    status.msg = "Identifying the radio..."
628
    status.max = 3
629
    radio.status_fn(status)
630

    
631
    try:
632
        for a in range(0, status.max):
633
            # Update the UI
634
            status.cur = a + 1
635
            radio.status_fn(status)
636

    
637
            # send the magic word
638
            _send(radio, radio._magic)
639

    
640
            # Now you get a x06 of ACK if all goes well
641
            ack = radio.pipe.read(1)
642

    
643
            if ack == "\x06":
644
                # DEBUG
645
                LOG.info("Magic ACK received")
646
                status.cur = status.max
647
                radio.status_fn(status)
648

    
649
                return True
650

    
651
        return False
652

    
653
    except errors.RadioError:
654
        raise
655
    except Exception, e:
656
        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
657

    
658

    
659
def _do_ident(radio, status, upload=False):
660
    """Put the radio in PROGRAM mode & identify it"""
661
    #  set the serial discipline
662
    radio.pipe.baudrate = 9600
663
    radio.pipe.parity = "N"
664

    
665
    # open the radio into program mode
666
    if _start_clone_mode(radio, status) is False:
667
        msg = "Radio did not enter clone mode"
668
        # warning about old versions of QYT KT8900
669
        if radio.MODEL == "KT8900":
670
            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
671
            msg += " known variant of this radios that is a clone of it."
672
        raise errors.RadioError(msg)
673

    
674
    # Ok, get the ident string
675
    ident = _rawrecv(radio, 49)
676

    
677
    # basic check for the ident
678
    if len(ident) != 49:
679
        raise errors.RadioError("Radio send a short ident block.")
680

    
681
    # check if ident is OK
682
    itis = False
683
    for fp in radio._fileid:
684
        if fp in ident:
685
            # got it!
686
            itis = True
687
            # checking if we are dealing with a Gen 3 BTECH
688
            if radio.VENDOR == "BTECH" and fp in BTECH3:
689
                radio.btech3 = True
690

    
691
            break
692

    
693
    if itis is False:
694
        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
695
        raise errors.RadioError("Radio identification failed.")
696

    
697
    # some radios needs a extra read and check for a code on it, this ones
698
    # has the check value in the _id2 var, others simply False
699
    if radio._id2 is not False:
700
        # lower the timeout here as this radios are reseting due to timeout
701
        radio.pipe.timeout = 0.05
702

    
703
        # query & receive the extra ID
704
        _send(radio, _make_frame("S", 0x3DF0, 16))
705
        id2 = _rawrecv(radio, 21)
706

    
707
        # WARNING !!!!!!
708
        # different radios send a response with a different amount of data
709
        # it seems that it's padded with \xff, \x20 and some times with \x00
710
        # we just care about the first 16, our magic string is in there
711
        if len(id2) < 16:
712
            raise errors.RadioError("The extra ID is short, aborting.")
713

    
714
        # ok, the correct string must be in the received data
715
        if radio._id2 not in id2:
716
            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
717
                      (radio.MODEL, util.hexprint(id2)))
718
            raise errors.RadioError("The extra ID is wrong, aborting.")
719

    
720
        # this radios need a extra request/answer here on the upload
721
        # the amount of data received depends of the radio type
722
        #
723
        # also the first block of TX must no have the ACK at the beginning
724
        # see _upload for this.
725
        if upload is True:
726
            # send an ACK
727
            _send(radio, ACK_CMD)
728

    
729
            # the amount of data depend on the radio, so far we have two radios
730
            # reading two bytes with an ACK at the end and just ONE with just
731
            # one byte (QYT KT8900)
732
            # the JT-6188 appears a clone of the last, but reads TWO bytes.
733
            #
734
            # we will read two bytes with a custom timeout to not penalize the
735
            # users for this.
736
            #
737
            # we just check for a response and last byte being a ACK, that is
738
            # the common stone for all radios (3 so far)
739
            ack = _rawrecv(radio, 2)
740

    
741
            # checking
742
            if len(ack) == 0 or ack[-1:] != ACK_CMD:
743
                raise errors.RadioError("Radio didn't ACK the upload")
744

    
745
            # restore the default serial timeout
746
            radio.pipe.timeout = STIMEOUT
747

    
748
    # DEBUG
749
    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
750

    
751
    return True
752

    
753

    
754
def _download(radio):
755
    """Get the memory map"""
756

    
757
    # UI progress
758
    status = chirp_common.Status()
759

    
760
    # put radio in program mode and identify it
761
    _do_ident(radio, status)
762

    
763
    # the models that doesn't have the extra ID have to make a dummy read here
764
    if radio._id2 is False:
765
        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
766
        discard = _rawrecv(radio, BLOCK_SIZE + 5)
767

    
768
        if debug is True:
769
            LOG.info("Dummy first block read done, got this:\n\n %s",
770
                     util.hexprint(discard))
771

    
772
    # reset the progress bar in the UI
773
    status.max = MEM_SIZE / BLOCK_SIZE
774
    status.msg = "Cloning from radio..."
775
    status.cur = 0
776
    radio.status_fn(status)
777

    
778
    # cleaning the serial buffer
779
    _clean_buffer(radio)
780

    
781
    data = ""
782
    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
783
        # sending the read request
784
        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
785

    
786
        # read
787
        d = _recv(radio, addr)
788

    
789
        # aggregate the data
790
        data += d
791

    
792
        # UI Update
793
        status.cur = addr / BLOCK_SIZE
794
        status.msg = "Cloning from radio..."
795
        radio.status_fn(status)
796

    
797
    return data
798

    
799

    
800
def _upload(radio):
801
    """Upload procedure"""
802

    
803
    # The UPLOAD mem is restricted to lower than 0x3100,
804
    # so we will overide that here localy
805
    MEM_SIZE = 0x3100
806

    
807
    # UI progress
808
    status = chirp_common.Status()
809

    
810
    # put radio in program mode and identify it
811
    _do_ident(radio, status, True)
812

    
813
    # get the data to upload to radio
814
    data = radio.get_mmap()
815

    
816
    # Reset the UI progress
817
    status.max = MEM_SIZE / TX_BLOCK_SIZE
818
    status.cur = 0
819
    status.msg = "Cloning to radio..."
820
    radio.status_fn(status)
821

    
822
    # the radios that doesn't have the extra ID 'may' do a dummy write, I found
823
    # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
824
    # dummy write is accepted (it actually writes to the mem!) by the radio.
825

    
826
    # cleaning the serial buffer
827
    _clean_buffer(radio)
828

    
829
    # the fun start here
830
    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
831
        # getting the block of data to send
832
        d = data[addr:addr + TX_BLOCK_SIZE]
833

    
834
        # build the frame to send
835
        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
836

    
837
        # first block must not send the ACK at the beginning for the
838
        # ones that has the extra id, since this have to do a extra step
839
        if addr == 0 and radio._id2 is not False:
840
            frame = frame[1:]
841

    
842
        # send the frame
843
        _send(radio, frame)
844

    
845
        # receiving the response
846
        ack = _rawrecv(radio, 1)
847

    
848
        # basic check
849
        if len(ack) != 1:
850
            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
851

    
852
        if not ack in "\x06\x05":
853
            raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
854

    
855
         # UI Update
856
        status.cur = addr / TX_BLOCK_SIZE
857
        status.msg = "Cloning to radio..."
858
        radio.status_fn(status)
859

    
860

    
861
def model_match(cls, data):
862
    """Match the opened/downloaded image to the correct version"""
863
    rid = data[0x3f70:0x3f76]
864

    
865
    if rid in cls._fileid:
866
        return True
867

    
868
    return False
869

    
870

    
871
def _decode_ranges(low, high):
872
    """Unpack the data in the ranges zones in the memmap and return
873
    a tuple with the integer corresponding to the Mhz it means"""
874
    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
875
    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
876
    ilow *= 1000000
877
    ihigh *= 1000000
878

    
879
    return (ilow, ihigh)
880

    
881

    
882
def _split(rf, f1, f2):
883
    """Returns False if the two freqs are in the same band (no split)
884
    or True otherwise"""
885

    
886
    # determine if the two freqs are in the same band
887
    for low, high in rf.valid_bands:
888
        if f1 >= low and f1 <= high and \
889
                f2 >= low and f2 <= high:
890
            # if the two freqs are on the same Band this is not a split
891
            return False
892

    
893
    # if you get here is because the freq pairs are split
894
    return False
895

    
896

    
897
class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
898
    """BTECH's UV-5001 and alike radios"""
899
    VENDOR = "BTECH"
900
    MODEL = ""
901
    IDENT = ""
902
    _vhf_range = (130000000, 180000000)
903
    _220_range = (210000000, 231000000)
904
    _uhf_range = (400000000, 521000000)
905
    _upper = 199
906
    _magic = MSTRING
907
    _fileid = None
908
    _id2 = False
909
    btech3 = False
910

    
911
    @classmethod
912
    def get_prompts(cls):
913
        rp = chirp_common.RadioPrompts()
914
        rp.experimental = \
915
            ('This driver is experimental.\n'
916
             '\n'
917
             'Please keep a copy of your memories with the original software '
918
             'if you treasure them, this driver is new and may contain'
919
             ' bugs.\n'
920
             '\n'
921
             )
922
        rp.pre_download = _(dedent("""\
923
            Follow these instructions to download your info:
924

    
925
            1 - Turn off your radio
926
            2 - Connect your interface cable
927
            3 - Turn on your radio
928
            4 - Do the download of your radio data
929

    
930
            """))
931
        rp.pre_upload = _(dedent("""\
932
            Follow these instructions to upload your info:
933

    
934
            1 - Turn off your radio
935
            2 - Connect your interface cable
936
            3 - Turn on your radio
937
            4 - Do the upload of your radio data
938

    
939
            """))
940
        return rp
941

    
942
    def get_features(self):
943
        """Get the radio's features"""
944

    
945
        # we will use the following var as global
946
        global POWER_LEVELS
947

    
948
        rf = chirp_common.RadioFeatures()
949
        rf.has_settings = True
950
        rf.has_bank = False
951
        rf.has_tuning_step = False
952
        rf.can_odd_split = True
953
        rf.has_name = True
954
        rf.has_offset = True
955
        rf.has_mode = True
956
        rf.has_dtcs = True
957
        rf.has_rx_dtcs = True
958
        rf.has_dtcs_polarity = True
959
        rf.has_ctone = True
960
        rf.has_cross = True
961
        rf.valid_modes = MODES
962
        rf.valid_characters = VALID_CHARS
963
        rf.valid_name_length = NAME_LENGTH
964
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
965
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
966
        rf.valid_cross_modes = [
967
            "Tone->Tone",
968
            "DTCS->",
969
            "->DTCS",
970
            "Tone->DTCS",
971
            "DTCS->Tone",
972
            "->Tone",
973
            "DTCS->DTCS"]
974
        rf.valid_skips = SKIP_VALUES
975
        rf.valid_dtcs_codes = DTCS
976
        rf.memory_bounds = (0, self._upper)
977

    
978
        # power levels
979
        if self.MODEL == "UV-5001":
980
            POWER_LEVELS = UV5001_POWER_LEVELS  # Higher power (50W)
981
        else:
982
            POWER_LEVELS = NORMAL_POWER_LEVELS  # Lower power (25W)
983

    
984
        rf.valid_power_levels = POWER_LEVELS
985

    
986
        # bands
987
        rf.valid_bands = [self._vhf_range, self._uhf_range]
988

    
989
        # 2501+220 & KT8900R
990
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
991
            rf.valid_bands.append(self._220_range)
992

    
993
        return rf
994

    
995
    def sync_in(self):
996
        """Download from radio"""
997
        data = _download(self)
998
        self._mmap = memmap.MemoryMap(data)
999
        self.process_mmap()
1000

    
1001
    def sync_out(self):
1002
        """Upload to radio"""
1003
        try:
1004
            _upload(self)
1005
        except errors.RadioError:
1006
            raise
1007
        except Exception, e:
1008
            raise errors.RadioError("Error: %s" % e)
1009

    
1010
    def set_options(self):
1011
        """This is to read the options from the image and set it in the
1012
        environment, for now just the limits of the freqs in the VHF/UHF
1013
        ranges"""
1014

    
1015
        # setting the correct ranges for each radio type
1016
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1017
            # the model 2501+220 has a segment in 220
1018
            # and a different position in the memmap
1019
            # also the QYT KT8900R
1020
            ranges = self._memobj.ranges220
1021
        else:
1022
            ranges = self._memobj.ranges
1023

    
1024
        # the normal dual bands
1025
        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
1026
        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
1027

    
1028
        # DEBUG
1029
        LOG.info("Radio ranges: VHF %d to %d" % vhf)
1030
        LOG.info("Radio ranges: UHF %d to %d" % uhf)
1031

    
1032
        # 220Mhz radios case
1033
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1034
            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
1035
            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
1036
            self._220_range = vhf2
1037

    
1038
        # set the class with the real data
1039
        self._vhf_range = vhf
1040
        self._uhf_range = uhf
1041

    
1042
    def process_mmap(self):
1043
        """Process the mem map into the mem object"""
1044

    
1045
        # Get it
1046
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
1047

    
1048
        # load specific parameters from the radio image
1049
        self.set_options()
1050

    
1051
    def get_raw_memory(self, number):
1052
        return repr(self._memobj.memory[number])
1053

    
1054
    def _decode_tone(self, val):
1055
        """Parse the tone data to decode from mem, it returns:
1056
        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
1057
        pol = None
1058

    
1059
        if val in [0, 65535]:
1060
            return '', None, None
1061
        elif val > 0x0258:
1062
            a = val / 10.0
1063
            return 'Tone', a, pol
1064
        else:
1065
            if val > 0x69:
1066
                index = val - 0x6A
1067
                pol = "R"
1068
            else:
1069
                index = val - 1
1070
                pol = "N"
1071

    
1072
            tone = DTCS[index]
1073
            return 'DTCS', tone, pol
1074

    
1075
    def _encode_tone(self, memval, mode, val, pol):
1076
        """Parse the tone data to encode from UI to mem"""
1077
        if mode == '' or mode is None:
1078
            memval.set_raw("\x00\x00")
1079
        elif mode == 'Tone':
1080
            memval.set_value(val * 10)
1081
        elif mode == 'DTCS':
1082
            # detect the index in the DTCS list
1083
            try:
1084
                index = DTCS.index(val)
1085
                if pol == "N":
1086
                    index += 1
1087
                else:
1088
                    index += 0x6A
1089
                memval.set_value(index)
1090
            except:
1091
                msg = "Digital Tone '%d' is not supported" % value
1092
                LOG.error(msg)
1093
                raise errors.RadioError(msg)
1094
        else:
1095
            msg = "Internal error: invalid mode '%s'" % mode
1096
            LOG.error(msg)
1097
            raise errors.InvalidDataError(msg)
1098

    
1099
    def get_memory(self, number):
1100
        """Get the mem representation from the radio image"""
1101
        _mem = self._memobj.memory[number]
1102
        _names = self._memobj.names[number]
1103

    
1104
        # Create a high-level memory object to return to the UI
1105
        mem = chirp_common.Memory()
1106

    
1107
        # Memory number
1108
        mem.number = number
1109

    
1110
        if _mem.get_raw()[0] == "\xFF":
1111
            mem.empty = True
1112
            return mem
1113

    
1114
        # Freq and offset
1115
        mem.freq = int(_mem.rxfreq) * 10
1116
        # tx freq can be blank
1117
        if _mem.get_raw()[4] == "\xFF":
1118
            # TX freq not set
1119
            mem.offset = 0
1120
            mem.duplex = "off"
1121
        else:
1122
            # TX freq set
1123
            offset = (int(_mem.txfreq) * 10) - mem.freq
1124
            if offset != 0:
1125
                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
1126
                    mem.duplex = "split"
1127
                    mem.offset = int(_mem.txfreq) * 10
1128
                elif offset < 0:
1129
                    mem.offset = abs(offset)
1130
                    mem.duplex = "-"
1131
                elif offset > 0:
1132
                    mem.offset = offset
1133
                    mem.duplex = "+"
1134
            else:
1135
                mem.offset = 0
1136

    
1137
        # name TAG of the channel
1138
        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
1139

    
1140
        # power
1141
        mem.power = POWER_LEVELS[int(_mem.power)]
1142

    
1143
        # wide/narrow
1144
        mem.mode = MODES[int(_mem.wide)]
1145

    
1146
        # skip
1147
        mem.skip = SKIP_VALUES[_mem.add]
1148

    
1149
        # tone data
1150
        rxtone = txtone = None
1151
        txtone = self._decode_tone(_mem.txtone)
1152
        rxtone = self._decode_tone(_mem.rxtone)
1153
        chirp_common.split_tone_decode(mem, txtone, rxtone)
1154

    
1155
        # Extra
1156
        mem.extra = RadioSettingGroup("extra", "Extra")
1157

    
1158
        scramble = RadioSetting("scramble", "Scramble",
1159
                                RadioSettingValueBoolean(bool(_mem.scramble)))
1160
        mem.extra.append(scramble)
1161

    
1162
        bcl = RadioSetting("bcl", "Busy channel lockout",
1163
                           RadioSettingValueBoolean(bool(_mem.bcl)))
1164
        mem.extra.append(bcl)
1165

    
1166
        pttid = RadioSetting("pttid", "PTT ID",
1167
                             RadioSettingValueList(PTTID_LIST,
1168
                                                   PTTID_LIST[_mem.pttid]))
1169
        mem.extra.append(pttid)
1170

    
1171
        # validating scode
1172
        scode = _mem.scode if _mem.scode != 15 else 0
1173
        pttidcode = RadioSetting("scode", "PTT ID signal code",
1174
                                 RadioSettingValueList(
1175
                                     PTTIDCODE_LIST,
1176
                                     PTTIDCODE_LIST[scode]))
1177
        mem.extra.append(pttidcode)
1178

    
1179
        optsig = RadioSetting("optsig", "Optional signaling",
1180
                              RadioSettingValueList(
1181
                                  OPTSIG_LIST,
1182
                                  OPTSIG_LIST[_mem.optsig]))
1183
        mem.extra.append(optsig)
1184

    
1185
        spmute = RadioSetting("spmute", "Speaker mute",
1186
                              RadioSettingValueList(
1187
                                  SPMUTE_LIST,
1188
                                  SPMUTE_LIST[_mem.spmute]))
1189
        mem.extra.append(spmute)
1190

    
1191
        return mem
1192

    
1193
    def set_memory(self, mem):
1194
        """Set the memory data in the eeprom img from the UI"""
1195
        # get the eprom representation of this channel
1196
        _mem = self._memobj.memory[mem.number]
1197
        _names = self._memobj.names[mem.number]
1198

    
1199
        mem_was_empty = False
1200
        # same method as used in get_memory for determining if mem is empty
1201
        # doing this BEFORE overwriting it with new values ...
1202
        if _mem.get_raw()[0] == "\xFF":
1203
            LOG.debug("This mem was empty before")
1204
            mem_was_empty = True
1205
        
1206
        # if empty memmory
1207
        if mem.empty:
1208
            # the channel itself
1209
            _mem.set_raw("\xFF" * 16)
1210
            # the name tag
1211
            _names.set_raw("\xFF" * 16)
1212
            return
1213

    
1214
        # frequency
1215
        _mem.rxfreq = mem.freq / 10
1216

    
1217
        # duplex
1218
        if mem.duplex == "+":
1219
            _mem.txfreq = (mem.freq + mem.offset) / 10
1220
        elif mem.duplex == "-":
1221
            _mem.txfreq = (mem.freq - mem.offset) / 10
1222
        elif mem.duplex == "off":
1223
            for i in _mem.txfreq:
1224
                i.set_raw("\xFF")
1225
        elif mem.duplex == "split":
1226
            _mem.txfreq = mem.offset / 10
1227
        else:
1228
            _mem.txfreq = mem.freq / 10
1229

    
1230
        # tone data
1231
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
1232
            chirp_common.split_tone_encode(mem)
1233
        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
1234
        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
1235

    
1236
        # name TAG of the channel
1237
        if len(mem.name) < NAME_LENGTH:
1238
            # we must pad to NAME_LENGTH chars, " " = "\xFF"
1239
            mem.name = str(mem.name).ljust(NAME_LENGTH, " ")
1240
        _names.name = str(mem.name).replace(" ", "\xFF")
1241

    
1242
        # power, # default power level is high
1243
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
1244

    
1245
        # wide/narrow
1246
        _mem.wide = MODES.index(mem.mode)
1247

    
1248
        # scan add property
1249
        _mem.add = SKIP_VALUES.index(mem.skip)
1250

    
1251
        # reseting unknowns, this have to be set by hand
1252
        _mem.unknown0 = 0
1253
        _mem.unknown1 = 0
1254
        _mem.unknown2 = 0
1255
        _mem.unknown3 = 0
1256
        _mem.unknown4 = 0
1257
        _mem.unknown5 = 0
1258
        _mem.unknown6 = 0
1259

    
1260
        # extra settings
1261
        if len(mem.extra) > 0:
1262
            # there are setting, parse
1263
            LOG.debug("Extra-Setting supplied. Setting them.")
1264
            for setting in mem.extra:
1265
                setattr(_mem, setting.get_name(), setting.value)
1266
        else:
1267
            if mem.empty:
1268
                LOG.debug("New mem is empty.")
1269
            else:
1270
                LOG.debug("New mem is NOT empty")
1271
                # set extra-settings to default ONLY when apreviously empty or
1272
                # deleted memory was edited to prevent errors such as #4121
1273
                if mem_was_empty :
1274
                    LOG.debug("old mem was empty. Setting default for extras.")
1275
                    _mem.spmute = 0
1276
                    _mem.optsig = 0
1277
                    _mem.scramble = 0
1278
                    _mem.bcl = 0
1279
                    _mem.pttid = 0
1280
                    _mem.scode = 0
1281

    
1282
        return mem
1283

    
1284
    def get_settings(self):
1285
        """Translate the bit in the mem_struct into settings in the UI"""
1286
        _mem = self._memobj
1287
        basic = RadioSettingGroup("basic", "Basic Settings")
1288
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
1289
        other = RadioSettingGroup("other", "Other Settings")
1290
        work = RadioSettingGroup("work", "Work Mode Settings")
1291
        fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
1292
        top = RadioSettings(basic, advanced, other, work, fm_presets)
1293

    
1294
        # Basic
1295
        tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
1296
                           RadioSettingValueBoolean(_mem.settings.tdr))
1297
        basic.append(tdr)
1298

    
1299
        sql = RadioSetting("settings.sql", "Squelch level",
1300
                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
1301
        basic.append(sql)
1302

    
1303
        tot = RadioSetting("settings.tot", "Time out timer",
1304
                           RadioSettingValueList(LIST_TOT, LIST_TOT[
1305
                               _mem.settings.tot]))
1306
        basic.append(tot)
1307

    
1308
        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
1309
            apo = RadioSetting("settings.apo", "Auto power off timer",
1310
                               RadioSettingValueList(LIST_APO, LIST_APO[
1311
                                   _mem.settings.apo]))
1312
            basic.append(apo)
1313
        else:
1314
            toa = RadioSetting("settings.apo", "Time out alert timer",
1315
                               RadioSettingValueList(LIST_TOA, LIST_TOA[
1316
                                   _mem.settings.apo]))
1317
            basic.append(toa)
1318

    
1319
        abr = RadioSetting("settings.abr", "Backlight timer",
1320
                           RadioSettingValueList(LIST_ABR, LIST_ABR[
1321
                               _mem.settings.abr]))
1322
        basic.append(abr)
1323

    
1324
        beep = RadioSetting("settings.beep", "Key beep",
1325
                            RadioSettingValueBoolean(_mem.settings.beep))
1326
        basic.append(beep)
1327

    
1328
        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
1329
                              RadioSettingValueList(LIST_DTMFST, LIST_DTMFST[
1330
                                  _mem.settings.dtmfst]))
1331
        basic.append(dtmfst)
1332

    
1333
        prisc = RadioSetting("settings.prisc", "Priority scan",
1334
                             RadioSettingValueBoolean(_mem.settings.prisc))
1335
        basic.append(prisc)
1336

    
1337
        prich = RadioSetting("settings.prich", "Priority channel",
1338
                             RadioSettingValueInteger(0, 199,
1339
                                 _mem.settings.prich))
1340
        basic.append(prich)
1341

    
1342
        screv = RadioSetting("settings.screv", "Scan resume method",
1343
                             RadioSettingValueList(LIST_SCREV, LIST_SCREV[
1344
                                 _mem.settings.screv]))
1345
        basic.append(screv)
1346

    
1347
        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
1348
                             RadioSettingValueInteger(0, 30,
1349
                                 _mem.settings.pttlt))
1350
        basic.append(pttlt)
1351

    
1352
        emctp = RadioSetting("settings.emctp", "Alarm mode",
1353
                             RadioSettingValueList(LIST_EMCTP, LIST_EMCTP[
1354
                                 _mem.settings.emctp]))
1355
        basic.append(emctp)
1356

    
1357
        emcch = RadioSetting("settings.emcch", "Alarm channel",
1358
                             RadioSettingValueInteger(0, 199,
1359
                                 _mem.settings.emcch))
1360
        basic.append(emcch)
1361

    
1362
        ringt = RadioSetting("settings.ringt", "Ring time",
1363
                             RadioSettingValueList(LIST_RINGT, LIST_RINGT[
1364
                                 _mem.settings.ringt]))
1365
        basic.append(ringt)
1366

    
1367
        camdf = RadioSetting("settings.camdf", "Display mode A",
1368
                             RadioSettingValueList(LIST_MDF, LIST_MDF[
1369
                                 _mem.settings.camdf]))
1370
        basic.append(camdf)
1371

    
1372
        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
1373
                             RadioSettingValueList(LIST_MDF, LIST_MDF[
1374
                                 _mem.settings.cbmdf]))
1375
        basic.append(cbmdf)
1376

    
1377
        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
1378
           sync = RadioSetting("settings.sync", "A/B channel sync",
1379
                               RadioSettingValueBoolean(_mem.settings.sync))
1380
           basic.append(sync)
1381
        else:
1382
           autolk = RadioSetting("settings.sync", "Auto keylock",
1383
                                 RadioSettingValueBoolean(_mem.settings.sync))
1384
           basic.append(autolk)
1385

    
1386
        ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
1387
                              RadioSettingValueList(LIST_PONMSG, LIST_PONMSG[
1388
                                  _mem.settings.ponmsg]))
1389
        basic.append(ponmsg)
1390

    
1391
        wtled = RadioSetting("settings.wtled", "Standby backlight Color",
1392
                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
1393
                                 _mem.settings.wtled]))
1394
        basic.append(wtled)
1395

    
1396
        rxled = RadioSetting("settings.rxled", "RX backlight Color",
1397
                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
1398
                                 _mem.settings.rxled]))
1399
        basic.append(rxled)
1400

    
1401
        txled = RadioSetting("settings.txled", "TX backlight Color",
1402
                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
1403
                                 _mem.settings.txled]))
1404
        basic.append(txled)
1405

    
1406
        anil = RadioSetting("settings.anil", "ANI length",
1407
                            RadioSettingValueList(LIST_ANIL, LIST_ANIL[
1408
                                _mem.settings.anil]))
1409
        basic.append(anil)
1410

    
1411
        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
1412
                            RadioSettingValueList(LIST_REPS, LIST_REPS[
1413
                                _mem.settings.reps]))
1414
        basic.append(reps)
1415

    
1416
        repm = RadioSetting("settings.repm", "Relay condition",
1417
                            RadioSettingValueList(LIST_REPM, LIST_REPM[
1418
                                _mem.settings.repm]))
1419
        basic.append(repm)
1420

    
1421
        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
1422
            tdrab = RadioSetting("settings.tdrab", "TDR return time",
1423
                                 RadioSettingValueList(LIST_ABR, LIST_ABR[
1424
                                     _mem.settings.tdrab]))
1425
            basic.append(tdrab)
1426

    
1427
            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
1428
                               RadioSettingValueBoolean(_mem.settings.ste))
1429
            basic.append(ste)
1430

    
1431
            rpste = RadioSetting("settings.rpste", "Repeater STE",
1432
                                 RadioSettingValueList(LIST_RINGT, LIST_RINGT[
1433
                                     _mem.settings.rpste]))
1434
            basic.append(rpste)
1435

    
1436
            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
1437
                                 RadioSettingValueList(LIST_RPTDL, LIST_RPTDL[
1438
                                     _mem.settings.rptdl]))
1439
            basic.append(rptdl)
1440

    
1441
        if str(_mem.fingerprint.fp) in BTECH3:
1442

    
1443
            mgain = RadioSetting("settings.mgain", "Mic gain",
1444
                                 RadioSettingValueInteger(0, 120,
1445
                                     _mem.settings.mgain))
1446
            basic.append(mgain)
1447

    
1448
            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
1449
                                 RadioSettingValueInteger(0, 60,
1450
                                     _mem.settings.dtmfg))
1451
            basic.append(dtmfg)
1452

    
1453
        # Advanced
1454
        def _filter(name):
1455
            filtered = ""
1456
            for char in str(name):
1457
                if char in VALID_CHARS:
1458
                    filtered += char
1459
                else:
1460
                    filtered += " "
1461
            return filtered
1462

    
1463
        _msg = self._memobj.poweron_msg
1464
        line1 = RadioSetting("poweron_msg.line1", "Power-on message line 1",
1465
                             RadioSettingValueString(0, 6, _filter(
1466
                                 _msg.line1)))
1467
        advanced.append(line1)
1468
        line2 = RadioSetting("poweron_msg.line2", "Power-on message line 2",
1469
                             RadioSettingValueString(0, 6, _filter(
1470
                                 _msg.line2)))
1471
        advanced.append(line2)
1472

    
1473
        if self.MODEL in ("UV-2501", "UV-5001"):
1474
            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
1475
                                   RadioSettingValueBoolean(
1476
                                       _mem.settings2.vfomren))
1477
            advanced.append(vfomren)
1478

    
1479
            reseten = RadioSetting("settings2.reseten", "RESET",
1480
                                   RadioSettingValueBoolean(
1481
                                       _mem.settings2.reseten))
1482
            advanced.append(reseten)
1483

    
1484
            menuen = RadioSetting("settings2.menuen", "Menu",
1485
                                  RadioSettingValueBoolean(
1486
                                      _mem.settings2.menuen))
1487
            advanced.append(menuen)
1488

    
1489
        # Other
1490
        def convert_bytes_to_limit(bytes):
1491
            limit = ""
1492
            for byte in bytes:
1493
                if byte < 10:
1494
                    limit += chr(byte + 0x30)
1495
                else:
1496
                    break
1497
            return limit
1498

    
1499
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1500
            _ranges = self._memobj.ranges220
1501
            ranges = "ranges220"
1502
        else:
1503
            _ranges = self._memobj.ranges
1504
            ranges = "ranges"
1505

    
1506
        _limit = convert_bytes_to_limit(_ranges.vhf_low)
1507
        val = RadioSettingValueString(0, 3, _limit)
1508
        val.set_mutable(False)
1509
        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
1510
        other.append(vhf_low)
1511

    
1512
        _limit = convert_bytes_to_limit(_ranges.vhf_high)
1513
        val = RadioSettingValueString(0, 3, _limit)
1514
        val.set_mutable(False)
1515
        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
1516
        other.append(vhf_high)
1517

    
1518
        if self.MODEL in ["UV-2501+220", "KT8900R"]:
1519
            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
1520
            val = RadioSettingValueString(0, 3, _limit)
1521
            val.set_mutable(False)
1522
            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
1523
            other.append(vhf2_low)
1524

    
1525
            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
1526
            val = RadioSettingValueString(0, 3, _limit)
1527
            val.set_mutable(False)
1528
            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
1529
            other.append(vhf2_high)
1530

    
1531
        _limit = convert_bytes_to_limit(_ranges.uhf_low)
1532
        val = RadioSettingValueString(0, 3, _limit)
1533
        val.set_mutable(False)
1534
        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
1535
        other.append(uhf_low)
1536

    
1537
        _limit = convert_bytes_to_limit(_ranges.uhf_high)
1538
        val = RadioSettingValueString(0, 3, _limit)
1539
        val.set_mutable(False)
1540
        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
1541
        other.append(uhf_high)
1542

    
1543
        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
1544
        val.set_mutable(False)
1545
        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
1546
        other.append(fp)
1547

    
1548
        # Work
1549
        dispab = RadioSetting("settings2.dispab", "Display",
1550
                              RadioSettingValueList(LIST_AB,LIST_AB[
1551
                                  _mem.settings2.dispab]))
1552
        work.append(dispab)
1553

    
1554
        vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
1555
                             RadioSettingValueList(LIST_VFOMR,LIST_VFOMR[
1556
                                 _mem.settings2.vfomr]))
1557
        work.append(vfomr)
1558

    
1559
        keylock = RadioSetting("settings2.keylock", "Keypad lock",
1560
                           RadioSettingValueBoolean(_mem.settings2.keylock))
1561
        work.append(keylock)
1562

    
1563
        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
1564
                             RadioSettingValueInteger(0, 199,
1565
                                 _mem.settings2.mrcha))
1566
        work.append(mrcha)
1567

    
1568
        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
1569
                             RadioSettingValueInteger(0, 199,
1570
                                 _mem.settings2.mrchb))
1571
        work.append(mrchb)
1572

    
1573
        def convert_bytes_to_freq(bytes):
1574
            real_freq = 0
1575
            for byte in bytes:
1576
                real_freq = (real_freq * 10) + byte
1577
            return chirp_common.format_freq(real_freq * 10)
1578

    
1579
        def my_validate(value):
1580
            value = chirp_common.parse_freq(value)
1581
            if "+220" in self.MODEL:
1582
                if 180000000 <= value and value < 210000000:
1583
                    msg = ("Can't be between 180.00000-210.00000")
1584
                    raise InvalidValueError(msg)
1585
                elif 231000000 <= value and value < 400000000:
1586
                    msg = ("Can't be between 231.00000-400.00000")
1587
                    raise InvalidValueError(msg)
1588
            elif "8900R" in self.MODEL:
1589
                if 180000000 <= value and value < 240000000:
1590
                    msg = ("Can't be between 180.00000-240.00000")
1591
                    raise InvalidValueError(msg)
1592
                elif 271000000 <= value and value < 400000000:
1593
                    msg = ("Can't be between 271.00000-400.00000")
1594
                    raise InvalidValueError(msg)
1595
            elif 180000000 <= value and value < 400000000:
1596
                msg = ("Can't be between 180.00000-400.00000")
1597
                raise InvalidValueError(msg)
1598
            return chirp_common.format_freq(value)
1599

    
1600
        def apply_freq(setting, obj):
1601
            value = chirp_common.parse_freq(str(setting.value)) / 10
1602
            for i in range(7, -1, -1):
1603
                obj.freq[i] = value % 10
1604
                value /= 10
1605

    
1606
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1607
                                        _mem.vfo.a.freq))
1608
        val1a.set_validate_callback(my_validate)
1609
        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
1610
        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
1611
        work.append(vfoafreq)
1612

    
1613
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
1614
                                        _mem.vfo.b.freq))
1615
        val1b.set_validate_callback(my_validate)
1616
        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
1617
        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
1618
        work.append(vfobfreq)
1619

    
1620
        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
1621
                                  RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
1622
                                      _mem.vfo.a.shiftd]))
1623
        work.append(vfoashiftd)
1624

    
1625
        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
1626
                                  RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
1627
                                      _mem.vfo.b.shiftd]))
1628
        work.append(vfobshiftd)
1629

    
1630
        def convert_bytes_to_offset(bytes):
1631
            real_offset = 0
1632
            for byte in bytes:
1633
                real_offset = (real_offset * 10) + byte
1634
            return chirp_common.format_freq(real_offset * 10000)
1635

    
1636
        def apply_offset(setting, obj):
1637
            value = chirp_common.parse_freq(str(setting.value)) / 10000
1638
            for i in range(3, -1, -1):
1639
                obj.offset[i] = value % 10
1640
                value /= 10
1641

    
1642
        val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1643
                                        _mem.vfo.a.offset))
1644
        vfoaoffset = RadioSetting("vfo.a.offset",
1645
                                  "VFO A offset (0.00-99.95)", val1a)
1646
        vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
1647
        work.append(vfoaoffset)
1648

    
1649
        val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
1650
                                        _mem.vfo.b.offset))
1651
        vfoboffset = RadioSetting("vfo.b.offset",
1652
                                  "VFO B offset (0.00-99.95)", val1b)
1653
        vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
1654
        work.append(vfoboffset)
1655

    
1656
        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
1657
                                RadioSettingValueList(LIST_TXP,LIST_TXP[
1658
                                    _mem.vfo.a.power]))
1659
        work.append(vfoatxp)
1660

    
1661
        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
1662
                                RadioSettingValueList(LIST_TXP,LIST_TXP[
1663
                                    _mem.vfo.b.power]))
1664
        work.append(vfobtxp)
1665

    
1666
        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
1667
                                RadioSettingValueList(LIST_WIDE,LIST_WIDE[
1668
                                    _mem.vfo.a.wide]))
1669
        work.append(vfoawide)
1670

    
1671
        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
1672
                                RadioSettingValueList(LIST_WIDE,LIST_WIDE[
1673
                                    _mem.vfo.b.wide]))
1674
        work.append(vfobwide)
1675

    
1676
        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
1677
                                RadioSettingValueList(LIST_STEP,LIST_STEP[
1678
                                    _mem.vfo.a.step]))
1679
        work.append(vfoastep)
1680

    
1681
        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
1682
                                RadioSettingValueList(LIST_STEP,LIST_STEP[
1683
                                    _mem.vfo.b.step]))
1684
        work.append(vfobstep)
1685

    
1686
        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
1687
                                  RadioSettingValueList(OPTSIG_LIST,
1688
                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
1689
        work.append(vfoaoptsig)
1690

    
1691
        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
1692
                                  RadioSettingValueList(OPTSIG_LIST,
1693
                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
1694
        work.append(vfoboptsig)
1695

    
1696
        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
1697
                                  RadioSettingValueList(SPMUTE_LIST,
1698
                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
1699
        work.append(vfoaspmute)
1700

    
1701
        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
1702
                                  RadioSettingValueList(SPMUTE_LIST,
1703
                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
1704
        work.append(vfobspmute)
1705

    
1706
        vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
1707
                               RadioSettingValueBoolean(_mem.vfo.a.scramble))
1708
        work.append(vfoascr)
1709

    
1710
        vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
1711
                               RadioSettingValueBoolean(_mem.vfo.b.scramble))
1712
        work.append(vfobscr)
1713

    
1714
        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
1715
                                 RadioSettingValueList(PTTIDCODE_LIST,
1716
                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
1717
        work.append(vfoascode)
1718

    
1719
        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
1720
                                 RadioSettingValueList(PTTIDCODE_LIST,
1721
                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
1722
        work.append(vfobscode)
1723

    
1724
        pttid = RadioSetting("settings.pttid", "PTT ID",
1725
                             RadioSettingValueList(PTTID_LIST,
1726
                                 PTTID_LIST[_mem.settings.pttid]))
1727
        work.append(pttid)
1728

    
1729
        #FM presets
1730
        def fm_validate(value):
1731
            if value == 0:
1732
                return chirp_common.format_freq(value)
1733
            if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
1734
                msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
1735
                raise InvalidValueError(msg)
1736
            return value
1737

    
1738
        def apply_fm_preset_name(setting, obj):
1739
            valstring = str (setting.value)
1740
            for i in range(0,6):
1741
                if valstring[i] in VALID_CHARS:
1742
                    obj[i] = valstring[i]
1743
                else:
1744
                    obj[i] = '0xff'
1745

    
1746
        def apply_fm_freq(setting, obj):
1747
            value = chirp_common.parse_freq(str(setting.value)) / 10
1748
            for i in range(7, -1, -1):
1749
                obj.freq[i] = value % 10
1750
                value /= 10
1751
        
1752
        _presets = self._memobj.fm_radio_preset
1753
        i = 1
1754
        for preset in _presets:
1755
            line = RadioSetting("fm_presets_"+ str(i), "Station name " + str(i),
1756
                                RadioSettingValueString(0, 6, _filter(
1757
                                    preset.broadcast_station_name)))
1758
            line.set_apply_callback(apply_fm_preset_name, 
1759
                                    preset.broadcast_station_name)
1760
            
1761
            val = RadioSettingValueFloat(0, 108, convert_bytes_to_freq(preset.freq))
1762
            fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq", "Frequency "+ str(i), val)
1763
            val.set_validate_callback(fm_validate)
1764
            fmfreq.set_apply_callback(apply_fm_freq, preset)
1765
            fm_presets.append(line)
1766
            fm_presets.append(fmfreq)
1767
            
1768
            i = i + 1
1769

    
1770
        # DTMF-Setting
1771
        dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
1772
                                               "DTMF Encoding Settings")
1773
        dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
1774
                                               "DTMF Decoding Settings")
1775
        top.append(dtmf_enc_settings)
1776
        top.append(dtmf_dec_settings)
1777
        txdisable = RadioSetting("dtmf_settings.txdisable", 
1778
                                 "TX-Disable",
1779
                                 RadioSettingValueBoolean(
1780
                                     _mem.dtmf_settings.txdisable))
1781
        dtmf_enc_settings.append(txdisable)
1782

    
1783
        rxdisable = RadioSetting("dtmf_settings.rxdisable", 
1784
                                 "RX-Disable",
1785
                                 RadioSettingValueBoolean(
1786
                                     _mem.dtmf_settings.rxdisable))
1787
        dtmf_enc_settings.append(rxdisable)
1788

    
1789
        dtmfspeed_on = RadioSetting(
1790
            "dtmf_settings.dtmfspeed_on",
1791
            "DTMF Speed (On Time)",
1792
            RadioSettingValueList(LIST_DTMF_SPEED,
1793
                                  LIST_DTMF_SPEED[
1794
                                      _mem.dtmf_settings.dtmfspeed_on]))
1795
        dtmf_enc_settings.append(dtmfspeed_on)
1796

    
1797
        dtmfspeed_off = RadioSetting(
1798
            "dtmf_settings.dtmfspeed_off",
1799
            "DTMF Speed (Off Time)",
1800
            RadioSettingValueList(LIST_DTMF_SPEED,
1801
                                  LIST_DTMF_SPEED[
1802
                                      _mem.dtmf_settings.dtmfspeed_off]))
1803
        dtmf_enc_settings.append(dtmfspeed_off)
1804

    
1805
        def memory2string(dmtf_mem):
1806
            dtmf_string = ""
1807
            for digit in dmtf_mem:
1808
                if digit != 255:
1809
                    index = LIST_DTMF_VALUES.index(digit)
1810
                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
1811
            return dtmf_string
1812

    
1813
        def apply_dmtf_frame(setting, obj):
1814
            LOG.debug("Setting DTMF-Code: " + str(setting.value) )
1815
            val_string = str(setting.value)
1816
            for i in range(0,16):
1817
                obj[i] = 255
1818
            i = 0
1819
            for current_char in val_string:
1820
                current_char = current_char.upper()
1821
                index = LIST_DTMF_DIGITS.index(current_char)
1822
                obj[i] = LIST_DTMF_VALUES[ index ]
1823
                i = i + 1
1824

    
1825
        codes = self._memobj.dtmf_codes
1826
        i = 1
1827
        for dtmfcode in codes:
1828
            val = RadioSettingValueString(0, 16, 
1829
                                          memory2string(dtmfcode.code),
1830
                                          False, CHARSET_DTMF_DIGITS)
1831
            line = RadioSetting("dtmf_code_" + str(i) + "_code",
1832
                                "DMTF Code " + str(i), val)
1833
            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
1834
            dtmf_enc_settings.append(line)
1835
            i = i + 1
1836

    
1837
        line = RadioSetting("dtmf_settings.mastervice", 
1838
                            "Master and Vice ID",
1839
                            RadioSettingValueBoolean(
1840
                                _mem.dtmf_settings.mastervice))
1841
        dtmf_dec_settings.append(line)
1842

    
1843
        val = RadioSettingValueString(0, 16, 
1844
                                      memory2string(
1845
                                          _mem.dtmf_settings.masterid),
1846
                                          False, CHARSET_DTMF_DIGITS)
1847
        line = RadioSetting("dtmf_settings.masterid",
1848
                            "Master Control ID ", val)
1849
        line.set_apply_callback(apply_dmtf_frame,
1850
                                _mem.dtmf_settings.masterid)
1851
        dtmf_dec_settings.append(line)
1852

    
1853
        line = RadioSetting("dtmf_settings.minspection", 
1854
                            "Master Inspection",
1855
                            RadioSettingValueBoolean(
1856
                                _mem.dtmf_settings.minspection))
1857
        dtmf_dec_settings.append(line)
1858

    
1859
        line = RadioSetting("dtmf_settings.mmonitor", 
1860
                            "Master Monitor",
1861
                            RadioSettingValueBoolean(
1862
                                _mem.dtmf_settings.mmonitor))
1863
        dtmf_dec_settings.append(line)
1864

    
1865
        line = RadioSetting("dtmf_settings.mstun", 
1866
                            "Master Stun",
1867
                            RadioSettingValueBoolean(
1868
                                _mem.dtmf_settings.mstun))
1869
        dtmf_dec_settings.append(line)
1870

    
1871
        line = RadioSetting("dtmf_settings.mkill", 
1872
                            "Master Kill",
1873
                            RadioSettingValueBoolean(
1874
                                _mem.dtmf_settings.mkill))
1875
        dtmf_dec_settings.append(line)
1876

    
1877
        line = RadioSetting("dtmf_settings.mrevive", 
1878
                            "Master Revive",
1879
                            RadioSettingValueBoolean(
1880
                                _mem.dtmf_settings.mrevive))
1881
        dtmf_dec_settings.append(line)
1882

    
1883
        val = RadioSettingValueString(0, 16, 
1884
                                      memory2string(
1885
                                          _mem.dtmf_settings.viceid),
1886
                                          False, CHARSET_DTMF_DIGITS)
1887
        line = RadioSetting("dtmf_settings.viceid",
1888
                            "Vice Control ID ", val)
1889
        line.set_apply_callback(apply_dmtf_frame,
1890
                                _mem.dtmf_settings.viceid)
1891
        dtmf_dec_settings.append(line)
1892

    
1893
        line = RadioSetting("dtmf_settings.vinspection", 
1894
                            "Vice Inspection",
1895
                            RadioSettingValueBoolean(
1896
                                _mem.dtmf_settings.vinspection))
1897
        dtmf_dec_settings.append(line)
1898

    
1899
        line = RadioSetting("dtmf_settings.vmonitor", 
1900
                            "Vice Monitor",
1901
                            RadioSettingValueBoolean(
1902
                                _mem.dtmf_settings.vmonitor))
1903
        dtmf_dec_settings.append(line)
1904

    
1905
        line = RadioSetting("dtmf_settings.vstun", 
1906
                            "Vice Stun",
1907
                            RadioSettingValueBoolean(
1908
                                _mem.dtmf_settings.vstun))
1909
        dtmf_dec_settings.append(line)
1910

    
1911
        line = RadioSetting("dtmf_settings.vkill", 
1912
                            "Vice Kill",
1913
                            RadioSettingValueBoolean(
1914
                                _mem.dtmf_settings.vkill))
1915
        dtmf_dec_settings.append(line)
1916

    
1917
        line = RadioSetting("dtmf_settings.vrevive", 
1918
                            "Vice Revive",
1919
                            RadioSettingValueBoolean(
1920
                                _mem.dtmf_settings.vrevive))
1921
        dtmf_dec_settings.append(line)
1922

    
1923
        val = RadioSettingValueString(0, 16, 
1924
                                      memory2string(
1925
                                          _mem.dtmf_settings.inspection),
1926
                                          False, CHARSET_DTMF_DIGITS)
1927
        line = RadioSetting("dtmf_settings.inspection",
1928
                            "Inspection", val)
1929
        line.set_apply_callback(apply_dmtf_frame,
1930
                                _mem.dtmf_settings.inspection)
1931
        dtmf_dec_settings.append(line)
1932

    
1933
        val = RadioSettingValueString(0, 16, 
1934
                                      memory2string(
1935
                                          _mem.dtmf_settings.alarmcode),
1936
                                          False, CHARSET_DTMF_DIGITS)
1937
        line = RadioSetting("dtmf_settings.alarmcode",
1938
                            "Alarm", val)
1939
        line.set_apply_callback(apply_dmtf_frame,
1940
                                _mem.dtmf_settings.alarmcode)
1941
        dtmf_dec_settings.append(line)
1942

    
1943
        val = RadioSettingValueString(0, 16, 
1944
                                      memory2string(
1945
                                          _mem.dtmf_settings.kill),
1946
                                          False, CHARSET_DTMF_DIGITS)
1947
        line = RadioSetting("dtmf_settings.kill",
1948
                            "Kill", val)
1949
        line.set_apply_callback(apply_dmtf_frame,
1950
                                _mem.dtmf_settings.kill)
1951
        dtmf_dec_settings.append(line)
1952

    
1953
        val = RadioSettingValueString(0, 16, 
1954
                                      memory2string(
1955
                                          _mem.dtmf_settings.monitor),
1956
                                          False, CHARSET_DTMF_DIGITS)
1957
        line = RadioSetting("dtmf_settings.monitor",
1958
                            "Monitor", val)
1959
        line.set_apply_callback(apply_dmtf_frame,
1960
                                _mem.dtmf_settings.monitor)
1961
        dtmf_dec_settings.append(line)
1962

    
1963
        val = RadioSettingValueString(0, 16, 
1964
                                      memory2string(
1965
                                          _mem.dtmf_settings.stun),
1966
                                          False, CHARSET_DTMF_DIGITS)
1967
        line = RadioSetting("dtmf_settings.stun",
1968
                            "Stun", val)
1969
        line.set_apply_callback(apply_dmtf_frame,
1970
                                _mem.dtmf_settings.stun)
1971
        dtmf_dec_settings.append(line)
1972

    
1973
        val = RadioSettingValueString(0, 16, 
1974
                                      memory2string(
1975
                                          _mem.dtmf_settings.revive),
1976
                                          False, CHARSET_DTMF_DIGITS)
1977
        line = RadioSetting("dtmf_settings.revive",
1978
                            "Revive", val)
1979
        line.set_apply_callback(apply_dmtf_frame,
1980
                                _mem.dtmf_settings.revive)
1981
        dtmf_dec_settings.append(line)
1982

    
1983
        def apply_dmtf_listvalue(setting, obj):
1984
            LOG.debug("Setting value: "+ str(setting.value) + " from list")
1985
            val = str(setting.value)
1986
            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
1987
            val = LIST_DTMF_SPECIAL_VALUES[index]
1988
            obj.set_value(val)
1989

    
1990
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
1991
        line = RadioSetting(
1992
            "dtmf_settings.groupcode",
1993
            "Group Code",
1994
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
1995
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
1996
        line.set_apply_callback(apply_dmtf_listvalue,
1997
                                _mem.dtmf_settings.groupcode)
1998
        dtmf_dec_settings.append(line)
1999

    
2000
        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
2001
        line = RadioSetting(
2002
            "dtmf_settings.spacecode",
2003
            "Space Code",
2004
            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
2005
                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
2006
        line.set_apply_callback(apply_dmtf_listvalue,
2007
                                _mem.dtmf_settings.spacecode)
2008
        dtmf_dec_settings.append(line)
2009

    
2010
        line = RadioSetting(
2011
            "dtmf_settings.resettime",
2012
            "Reset time",
2013
            RadioSettingValueList(LIST_5TONE_RESET,
2014
                                  LIST_5TONE_RESET[
2015
                                      _mem.dtmf_settings.resettime]))
2016
        dtmf_dec_settings.append(line)
2017

    
2018
        line = RadioSetting(
2019
            "dtmf_settings.delayproctime",
2020
            "Delay processing time",
2021
            RadioSettingValueList(LIST_DTMF_DELAY,
2022
                                  LIST_DTMF_DELAY[
2023
                                      _mem.dtmf_settings.delayproctime]))
2024
        dtmf_dec_settings.append(line)
2025

    
2026

    
2027
        # 5 Tone Settings
2028
        stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
2029
        codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
2030

    
2031
        group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
2032
        group_5tone.append(stds_5tone)
2033
        group_5tone.append(codes_5tone)
2034

    
2035
        top.append(group_5tone)
2036

    
2037
        def apply_list_value(setting, obj):
2038
            options = setting.value.get_options()
2039
            obj.set_value ( options.index(str(setting.value)) )
2040

    
2041
        _5tone_standards = self._memobj._5tone_std_settings
2042
        i = 0
2043
        for standard in _5tone_standards:
2044
            std_5tone = RadioSettingGroup ("std_5tone_" + str(i), 
2045
                                           LIST_5TONE_STANDARDS[i])
2046
            stds_5tone.append(std_5tone)
2047
 
2048
            period = standard.period
2049
            if period == 255:
2050
                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] + 
2051
                          " is not yet configured. Setting to 70ms.")
2052
                period = 5
2053

    
2054
            if period <= len( LIST_5TONE_STANDARD_PERIODS ):
2055
                line = RadioSetting(
2056
                    "_5tone_std_settings_" + str(i) + "_period",
2057
                    "Period (ms)", RadioSettingValueList
2058
                    (LIST_5TONE_STANDARD_PERIODS,
2059
                     LIST_5TONE_STANDARD_PERIODS[period]))
2060
                line.set_apply_callback(apply_list_value, standard.period)
2061
                std_5tone.append(line)
2062
            else:
2063
                LOG.debug("Invalid value for 5tone period! Disabling.")
2064

    
2065
            group_tone = standard.group_tone
2066
            if group_tone == 255:
2067
                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
2068
                          " is not yet configured. Setting to A.")
2069
                group_tone = 10
2070

    
2071
            if group_tone <= len( LIST_5TONE_DIGITS ):
2072
                line = RadioSetting(
2073
                    "_5tone_std_settings_" + str(i) + "_grouptone",
2074
                    "Group Tone",
2075
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2076
                                          LIST_5TONE_DIGITS[
2077
                                              group_tone]))
2078
                line.set_apply_callback(apply_list_value,
2079
                                        standard.group_tone)
2080
                std_5tone.append(line)
2081
            else:
2082
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2083

    
2084
            repeat_tone = standard.repeat_tone
2085
            if repeat_tone == 255:
2086
                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] + 
2087
                          " is not yet configured. Setting to E.")
2088
                repeat_tone = 14
2089

    
2090
            if repeat_tone <= len( LIST_5TONE_DIGITS ):
2091
                line = RadioSetting(
2092
                    "_5tone_std_settings_" + str(i) + "_repttone",
2093
                    "Repeat Tone",
2094
                    RadioSettingValueList(LIST_5TONE_DIGITS,
2095
                                          LIST_5TONE_DIGITS[
2096
                                              repeat_tone]))
2097
                line.set_apply_callback(apply_list_value,
2098
                                        standard.repeat_tone)
2099
                std_5tone.append(line)
2100
            else:
2101
                LOG.debug("Invalid value for 5tone digit! Disabling.")
2102
            i = i + 1
2103

    
2104
        def my_apply_5tonestdlist_value(setting, obj):
2105
            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
2106
                obj.set_value(0xFF)
2107
            else:
2108
                obj.set_value( LIST_5TONE_STANDARDS.
2109
                              index(str(setting.value)) )
2110

    
2111
        def apply_5tone_frame(setting, obj):
2112
            LOG.debug("Setting 5 Tone: " + str(setting.value) )
2113
            valstring = str(setting.value)
2114
            if len(valstring) == 0:
2115
                for i in range(0,5):
2116
                    obj[i] = 255
2117
            else:
2118
                validFrame = True
2119
                for i in range(0,5):
2120
                    currentChar = valstring[i].upper()
2121
                    if currentChar in LIST_5TONE_DIGITS:
2122
                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
2123
                    else:
2124
                        validFrame = False
2125
                        LOG.debug("invalid char: " + str(currentChar))
2126
                if not validFrame:
2127
                    LOG.debug("setting whole frame to FF" )
2128
                    for i in range(0,5):
2129
                        obj[i] = 255
2130

    
2131
        def validate_5tone_frame(value):
2132
            if (len(str(value)) != 5) and (len(str(value)) != 0) :
2133
                msg = ("5 Tone must have 5 digits or 0 digits")
2134
                raise InvalidValueError(msg)
2135
            for digit in str(value):
2136
                if digit.upper() not in LIST_5TONE_DIGITS:
2137
                    msg = (str(digit) + " is not a valid digit for 5tones")
2138
                    raise InvalidValueError(msg)
2139
            return value
2140

    
2141
        def frame2string(frame):
2142
            frameString = ""
2143
            for digit in frame:
2144
                if digit != 255:
2145
                    frameString = frameString + LIST_5TONE_DIGITS[digit]
2146
            return frameString
2147

    
2148
        _5tone_codes = self._memobj._5tone_codes
2149
        i = 1
2150
        for code in _5tone_codes:
2151
            code_5tone = RadioSettingGroup ("code_5tone_" + str(i),
2152
                                            "5 Tone code " + str(i))
2153
            codes_5tone.append(code_5tone)
2154
            if (code.standard == 255 ):
2155
                currentVal = 15
2156
            else:
2157
                currentVal = code.standard
2158
            line = RadioSetting("_5tone_code_" + str(i) + "_std", 
2159
                                " Standard",
2160
                                RadioSettingValueList(LIST_5TONE_STANDARDS,
2161
                                                      LIST_5TONE_STANDARDS[
2162
                                                          currentVal]) )
2163
            line.set_apply_callback(my_apply_5tonestdlist_value,
2164
                                    code.standard)
2165
            code_5tone.append(line)
2166

    
2167
            val = RadioSettingValueString(0, 6,
2168
                                          frame2string(code.frame1), False)
2169
            line = RadioSetting("_5tone_code_" + str(i) + "_frame1", 
2170
                                " Frame 1", val)
2171
            val.set_validate_callback(validate_5tone_frame)
2172
            line.set_apply_callback(apply_5tone_frame, code.frame1)
2173
            code_5tone.append(line)
2174

    
2175
            val = RadioSettingValueString(0, 6,
2176
                                          frame2string(code.frame2), False)
2177
            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
2178
                                " Frame 2", val)
2179
            val.set_validate_callback(validate_5tone_frame)
2180
            line.set_apply_callback(apply_5tone_frame, code.frame2)
2181
            code_5tone.append(line)
2182

    
2183
            val = RadioSettingValueString(0, 6,
2184
                                          frame2string(code.frame3), False)
2185
            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
2186
                                " Frame 3", val)
2187
            val.set_validate_callback(validate_5tone_frame)
2188
            line.set_apply_callback(apply_5tone_frame, code.frame3)
2189
            code_5tone.append(line)
2190
            i = i + 1
2191

    
2192
        _5_tone_decode1 = RadioSetting(
2193
            "_5tone_settings._5tone_decode_call_frame1",
2194
            "5 Tone decode call Frame 1",
2195
            RadioSettingValueBoolean(
2196
                _mem._5tone_settings._5tone_decode_call_frame1))
2197
        group_5tone.append(_5_tone_decode1)
2198

    
2199
        _5_tone_decode2 = RadioSetting(
2200
            "_5tone_settings._5tone_decode_call_frame2",
2201
            "5 Tone decode call Frame 2",
2202
            RadioSettingValueBoolean(
2203
                _mem._5tone_settings._5tone_decode_call_frame2))
2204
        group_5tone.append(_5_tone_decode2)
2205

    
2206
        _5_tone_decode3 = RadioSetting(
2207
            "_5tone_settings._5tone_decode_call_frame3",
2208
            "5 Tone decode call Frame 3",
2209
            RadioSettingValueBoolean(
2210
                _mem._5tone_settings._5tone_decode_call_frame3))
2211
        group_5tone.append(_5_tone_decode3)
2212

    
2213
        _5_tone_decode_disp1 = RadioSetting(
2214
            "_5tone_settings._5tone_decode_disp_frame1",
2215
            "5 Tone decode disp Frame 1",
2216
            RadioSettingValueBoolean(
2217
                _mem._5tone_settings._5tone_decode_disp_frame1))
2218
        group_5tone.append(_5_tone_decode_disp1)
2219

    
2220
        _5_tone_decode_disp2 = RadioSetting(
2221
            "_5tone_settings._5tone_decode_disp_frame2",
2222
            "5 Tone decode disp Frame 2",
2223
            RadioSettingValueBoolean(
2224
                _mem._5tone_settings._5tone_decode_disp_frame2))
2225
        group_5tone.append(_5_tone_decode_disp2)
2226

    
2227
        _5_tone_decode_disp3 = RadioSetting(
2228
            "_5tone_settings._5tone_decode_disp_frame3",
2229
            "5 Tone decode disp Frame 3",
2230
            RadioSettingValueBoolean(
2231
                _mem._5tone_settings._5tone_decode_disp_frame3))
2232
        group_5tone.append(_5_tone_decode_disp3)
2233

    
2234
        decode_standard = _mem._5tone_settings.decode_standard
2235
        if decode_standard == 255:
2236
            decode_standard = 0
2237
        if decode_standard <= len (LIST_5TONE_STANDARDS_without_none) :
2238
            line = RadioSetting("_5tone_settings.decode_standard", 
2239
                                "5 Tone-decode Standard",
2240
                                RadioSettingValueList(
2241
                                    LIST_5TONE_STANDARDS_without_none,
2242
                                    LIST_5TONE_STANDARDS_without_none[
2243
                                        decode_standard]))
2244
            group_5tone.append(line)
2245
        else:
2246
            LOG.debug("Invalid decode std...")
2247
            
2248
        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
2249
        if _5tone_delay1 == 255:
2250
            _5tone_delay1 = 20
2251

    
2252
        if _5tone_delay1 <= len( LIST_5TONE_DELAY ):
2253
            list = RadioSettingValueList(LIST_5TONE_DELAY, 
2254
                                         LIST_5TONE_DELAY[
2255
                                             _5tone_delay1])
2256
            line = RadioSetting("_5tone_settings._5tone_delay1",
2257
                                "5 Tone Delay Frame 1", list)
2258
            group_5tone.append(line)
2259
        else:
2260
            LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
2261

    
2262
        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
2263
        if _5tone_delay2 == 255:
2264
            _5tone_delay2 = 20
2265
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2266

    
2267
        if _5tone_delay2 <= len( LIST_5TONE_DELAY ):
2268
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2269
                                         LIST_5TONE_DELAY[
2270
                                             _5tone_delay2])
2271
            line = RadioSetting("_5tone_settings._5tone_delay2",
2272
                                "5 Tone Delay Frame 2", list)
2273
            group_5tone.append(line)
2274
        else:
2275
            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
2276

    
2277
        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
2278
        if _5tone_delay3 == 255:
2279
            _5tone_delay3 = 20
2280
            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
2281

    
2282
        if _5tone_delay3 <= len( LIST_5TONE_DELAY ):
2283
            list = RadioSettingValueList(LIST_5TONE_DELAY,
2284
                                         LIST_5TONE_DELAY[
2285
                                             _5tone_delay3])
2286
            line = RadioSetting("_5tone_settings._5tone_delay3",
2287
                                "5 Tone Delay Frame 3", list )
2288
            group_5tone.append(line)
2289
        else:
2290
            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
2291

    
2292
        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
2293
        if ext_length == 255:
2294
            ext_length = 0
2295
            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
2296

    
2297
        if ext_length <= len(
2298
            LIST_5TONE_DELAY ):
2299
            list = RadioSettingValueList(
2300
                LIST_5TONE_DELAY, 
2301
                LIST_5TONE_DELAY[
2302
                    ext_length])
2303
            line = RadioSetting(
2304
                "_5tone_settings._5tone_first_digit_ext_length",
2305
                "First digit extend length", list)
2306
            group_5tone.append(line)
2307
        else:
2308
            LOG.debug("Invalid value for 5tone ext length! Disabling.")
2309

    
2310
        decode_reset_time = _mem._5tone_settings.decode_reset_time
2311
        if decode_reset_time == 255:
2312
            decode_reset_time = 59
2313
            LOG.debug("Decode reset time unconfigured. resetting.")
2314
        if decode_reset_time <= len(LIST_5TONE_RESET):
2315
            list = RadioSettingValueList(
2316
                LIST_5TONE_RESET,
2317
                LIST_5TONE_RESET[
2318
                    decode_reset_time])
2319
            line = RadioSetting("_5tone_settings.decode_reset_time",
2320
                                "Decode reset time", list)
2321
            group_5tone.append(line)
2322
        else:
2323
            LOG.debug("Invalid value decode reset time! Disabling.")
2324

    
2325
        # 2 Tone
2326
        encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
2327
        decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
2328

    
2329
        top.append(encode_2tone)
2330
        top.append(decode_2tone)
2331

    
2332
        duration_1st_tone = self._memobj._2tone.duration_1st_tone
2333
        if duration_1st_tone == 255:
2334
            LOG.debug("Duration of first 2 Tone digit is not yet " +
2335
                      "configured. Setting to 600ms")
2336
            duration_1st_tone = 60
2337

    
2338
        if duration_1st_tone <= len( LIST_5TONE_DELAY ):
2339
            line = RadioSetting("_2tone.duration_1st_tone", 
2340
                                "Duration 1st Tone",
2341
                                RadioSettingValueList(LIST_5TONE_DELAY,
2342
                                                      LIST_5TONE_DELAY[
2343
                                                          duration_1st_tone]))
2344
            encode_2tone.append(line)
2345

    
2346
        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
2347
        if duration_2nd_tone == 255:
2348
            LOG.debug("Duration of second 2 Tone digit is not yet " +
2349
                      "configured. Setting to 600ms")
2350
            duration_2nd_tone = 60
2351

    
2352
        if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
2353
            line = RadioSetting("_2tone.duration_2nd_tone", 
2354
                                "Duration 2nd Tone",
2355
                                RadioSettingValueList(LIST_5TONE_DELAY,
2356
                                                      LIST_5TONE_DELAY[
2357
                                                          duration_2nd_tone]))
2358
            encode_2tone.append(line)
2359

    
2360
        duration_gap = self._memobj._2tone.duration_gap
2361
        if duration_gap == 255:
2362
            LOG.debug("Duration of gap is not yet " +
2363
                      "configured. Setting to 300ms")
2364
            duration_gap = 30
2365

    
2366
        if duration_gap <= len( LIST_5TONE_DELAY ):
2367
            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
2368
                                RadioSettingValueList(LIST_5TONE_DELAY,
2369
                                                      LIST_5TONE_DELAY[
2370
                                                          duration_gap]))
2371
            encode_2tone.append(line)
2372

    
2373
        def _2tone_validate(value):
2374
            if value == 0:
2375
                return 65535
2376
            if value == 65535:
2377
                return value
2378
            if not (300 <= value and value <= 3000):
2379
                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
2380
                raise InvalidValueError(msg)
2381
            return value
2382

    
2383
        def apply_2tone_freq(setting, obj):
2384
            val = int(setting.value)
2385
            if (val == 0) or (val == 65535):
2386
                obj.set_value(65535)
2387
            else:
2388
                obj.set_value(val)
2389

    
2390
        i = 1
2391
        for code in  self._memobj._2tone._2tone_encode:
2392
            code_2tone = RadioSettingGroup ("code_2tone_" + str(i), 
2393
                                           "Encode Code " + str(i))
2394
            encode_2tone.append(code_2tone)
2395

    
2396
            tmp = code.freq1
2397
            if tmp == 65535:
2398
                tmp = 0
2399
            val1 = RadioSettingValueInteger(0, 65535, tmp)
2400
            freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1", 
2401
                                 "Frequency 1", val1)
2402
            val1.set_validate_callback(_2tone_validate)
2403
            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
2404
            code_2tone.append(freq1)
2405

    
2406
            tmp = code.freq2
2407
            if tmp == 65535:
2408
                tmp = 0
2409
            val2 = RadioSettingValueInteger(0, 65535, tmp)
2410
            freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2", 
2411
                                 "Frequency 2", val2)
2412
            val2.set_validate_callback(_2tone_validate)
2413
            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
2414
            code_2tone.append(freq2)
2415

    
2416
            i = i + 1
2417

    
2418
        decode_reset_time = _mem._2tone.reset_time
2419
        if decode_reset_time == 255:
2420
            decode_reset_time = 59
2421
            LOG.debug("Decode reset time unconfigured. resetting.")
2422
        if decode_reset_time <= len(LIST_5TONE_RESET):
2423
            list = RadioSettingValueList(
2424
                LIST_5TONE_RESET,
2425
                LIST_5TONE_RESET[
2426
                    decode_reset_time])
2427
            line = RadioSetting("_2tone.reset_time",
2428
                                "Decode reset time", list)
2429
            decode_2tone.append(line)
2430
        else:
2431
            LOG.debug("Invalid value decode reset time! Disabling.")
2432

    
2433
        def apply_2tone_freq_pair(setting, obj):
2434
            val = int(setting.value)
2435
            derived_val = 65535
2436
            frqname = str(setting._name[-5:])
2437
            derivedname = "derived_from_" + frqname
2438

    
2439
            if (val == 0):
2440
                val = 65535
2441
                derived_val = 65535
2442
            else:
2443
                derived_val = int(round(2304000.0/val))
2444

    
2445
            obj[frqname].set_value( val )
2446
            obj[derivedname].set_value( derived_val )
2447

    
2448
            LOG.debug("Apply " + frqname + ": " + str(val) + " | " 
2449
                      + derivedname + ": " + str(derived_val))
2450

    
2451
        i = 1
2452
        for decode_code in  self._memobj._2tone._2tone_decode:
2453
            _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
2454
                                           "Decode Code " + str(i))
2455
            decode_2tone.append(_2tone_dec_code)
2456

    
2457
            j = 1
2458
            for dec in decode_code.decs:
2459
                val = dec.dec
2460
                if val == 255:
2461
                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +  
2462
                              " is not yet configured. Setting to 0.")
2463
                    val = 0
2464

    
2465
                if val <= len( LIST_2TONE_DEC ):
2466
                    line = RadioSetting(
2467
                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
2468
                        "Dec " + str(j), RadioSettingValueList
2469
                        (LIST_2TONE_DEC,
2470
                         LIST_2TONE_DEC[val]))
2471
                    line.set_apply_callback(apply_list_value, dec.dec)
2472
                    _2tone_dec_code.append(line)
2473
                else:
2474
                    LOG.debug("Invalid value for 2tone dec! Disabling.")
2475

    
2476
                val = dec.response
2477
                if val == 255:
2478
                    LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
2479
                              " is not yet configured. Setting to 0.")
2480
                    val = 0
2481

    
2482
                if val <= len( LIST_2TONE_RESPONSE ):
2483
                    line = RadioSetting(
2484
                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
2485
                        "Response " + str(j), RadioSettingValueList
2486
                        (LIST_2TONE_RESPONSE,
2487
                         LIST_2TONE_RESPONSE[val]))
2488
                    line.set_apply_callback(apply_list_value, dec.response)
2489
                    _2tone_dec_code.append(line)
2490
                else:
2491
                    LOG.debug("Invalid value for 2tone response! Disabling.")
2492

    
2493
                val = dec.alert
2494
                if val == 255:
2495
                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +  
2496
                              " is not yet configured. Setting to 0.")
2497
                    val = 0
2498

    
2499
                if val <= len( PTTIDCODE_LIST ):
2500
                    line = RadioSetting(
2501
                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
2502
                        "Alert " + str(j), RadioSettingValueList
2503
                        (PTTIDCODE_LIST,
2504
                         PTTIDCODE_LIST[val]))
2505
                    line.set_apply_callback(apply_list_value, dec.alert)
2506
                    _2tone_dec_code.append(line)
2507
                else:
2508
                    LOG.debug("Invalid value for 2tone alert! Disabling.")
2509
                j = j + 1
2510

    
2511
            freq = self._memobj._2tone.freqs[i-1]
2512
            for char in ['A', 'B', 'C', 'D']:
2513
                setting_name = "freq" + str(char)
2514

    
2515
                tmp = freq[setting_name]
2516
                if tmp == 65535:
2517
                    tmp = 0
2518
                if tmp != 0:
2519
                    expected = int(round(2304000.0/tmp))
2520
                    from_mem = freq["derived_from_" + setting_name]
2521
                    if expected != from_mem:
2522
                        LOG.error("Expected " + str(expected) + 
2523
                                  " but read " + str(from_mem ) + 
2524
                                  ". Disabling 2Tone Decode Freqs!")
2525
                        break
2526
                val = RadioSettingValueInteger(0, 65535, tmp)
2527
                frq = RadioSetting("2tone_dec_"+ str(i) + "_freq" + str(char),
2528
                                         ("Decode Frequency " +str(char)), val)
2529
                val.set_validate_callback(_2tone_validate)
2530
                frq.set_apply_callback(apply_2tone_freq_pair, freq)
2531
                _2tone_dec_code.append(frq)
2532

    
2533
            i = i + 1
2534

    
2535
        return top
2536

    
2537
    def set_settings(self, settings):
2538
        _settings = self._memobj.settings
2539
        for element in settings:
2540
            if not isinstance(element, RadioSetting):
2541
                if element.get_name() == "fm_preset":
2542
                    self._set_fm_preset(element)
2543
                else:
2544
                    self.set_settings(element)
2545
                    continue
2546
            else:
2547
                try:
2548
                    name = element.get_name()
2549
                    if "." in name:
2550
                        bits = name.split(".")
2551
                        obj = self._memobj
2552
                        for bit in bits[:-1]:
2553
                            if "/" in bit:
2554
                                bit, index = bit.split("/", 1)
2555
                                index = int(index)
2556
                                obj = getattr(obj, bit)[index]
2557
                            else:
2558
                                obj = getattr(obj, bit)
2559
                        setting = bits[-1]
2560
                    else:
2561
                        obj = _settings
2562
                        setting = element.get_name()
2563

    
2564
                    if element.has_apply_callback():
2565
                        LOG.debug("Using apply callback")
2566
                        element.run_apply_callback()
2567
                    elif element.value.get_mutable():
2568
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2569
                        setattr(obj, setting, element.value)
2570
                except Exception, e:
2571
                    LOG.debug(element.get_name())
2572
                    raise
2573

    
2574
    @classmethod
2575
    def match_model(cls, filedata, filename):
2576
        match_size = False
2577
        match_model = False
2578

    
2579
        # testing the file data size
2580
        if len(filedata) == MEM_SIZE:
2581
            match_size = True
2582

    
2583
        # testing the firmware model fingerprint
2584
        match_model = model_match(cls, filedata)
2585

    
2586
        if match_size and match_model:
2587
            return True
2588
        else:
2589
            return False
2590

    
2591

    
2592
# Declaring Aliases (Clones of the real radios)
2593
class JT2705M(chirp_common.Alias):
2594
    VENDOR = "Jetstream"
2595
    MODEL = "JT2705M"
2596

    
2597

    
2598
class JT6188Mini(chirp_common.Alias):
2599
    VENDOR = "Juentai"
2600
    MODEL = "JT-6188 Mini"
2601

    
2602

    
2603
class JT6188Plus(chirp_common.Alias):
2604
    VENDOR = "Juentai"
2605
    MODEL = "JT-6188 Plus"
2606

    
2607

    
2608
class SSGT890(chirp_common.Alias):
2609
    VENDOR = "Sainsonic"
2610
    MODEL = "GT-890"
2611

    
2612

    
2613
class ZastoneMP300(chirp_common.Alias):
2614
    VENDOR = "Zastone"
2615
    MODEL = "MP-300"
2616

    
2617

    
2618
# real radios
2619
@directory.register
2620
class UV2501(BTech):
2621
    """Baofeng Tech UV2501"""
2622
    MODEL = "UV-2501"
2623
    _fileid = [UV2501G3_fp,
2624
               UV2501G2_fp,
2625
               UV2501pp2_fp,
2626
               UV2501pp_fp]
2627

    
2628

    
2629
@directory.register
2630
class UV2501_220(BTech):
2631
    """Baofeng Tech UV2501+220"""
2632
    MODEL = "UV-2501+220"
2633
    _magic = MSTRING_220
2634
    _id2 = UV2501_220pp_id
2635
    _fileid = [UV2501_220G3_fp,
2636
               UV2501_220G2_fp,
2637
               UV2501_220_fp,
2638
               UV2501_220pp_fp]
2639

    
2640

    
2641
@directory.register
2642
class UV5001(BTech):
2643
    """Baofeng Tech UV5001"""
2644
    MODEL = "UV-5001"
2645
    _fileid = [UV5001G3_fp,
2646
               UV5001G22_fp,
2647
               UV5001G2_fp,
2648
               UV5001alpha_fp,
2649
               UV5001pp_fp]
2650

    
2651

    
2652
@directory.register
2653
class MINI8900(BTech):
2654
    """WACCOM MINI-8900"""
2655
    VENDOR = "WACCOM"
2656
    MODEL = "MINI-8900"
2657
    _magic = MSTRING_MINI8900
2658
    _fileid = [MINI8900_fp, ]
2659
    # Clones
2660
    ALIASES = [JT6188Plus, ]
2661

    
2662

    
2663
@directory.register
2664
class KTUV980(BTech):
2665
    """QYT KT-UV980"""
2666
    VENDOR = "QYT"
2667
    MODEL = "KT-UV980"
2668
    _vhf_range = (136000000, 175000000)
2669
    _uhf_range = (400000000, 481000000)
2670
    _magic = MSTRING_MINI8900
2671
    _fileid = [KTUV980_fp, ]
2672
    # Clones
2673
    ALIASES = [JT2705M, ]
2674

    
2675
# Please note that there is a version of this radios that is a clone of the
2676
# Waccom Mini8900, maybe an early version?
2677
@directory.register
2678
class KT9800(BTech):
2679
    """QYT KT8900"""
2680
    VENDOR = "QYT"
2681
    MODEL = "KT8900"
2682
    _vhf_range = (136000000, 175000000)
2683
    _uhf_range = (400000000, 481000000)
2684
    _magic = MSTRING_KT8900
2685
    _fileid = [KT8900_fp,
2686
               KT8900_fp1,
2687
               KT8900_fp2,
2688
               KT8900_fp3,
2689
               KT8900_fp4,
2690
               KT8900_fp5]
2691
    _id2 = KT8900_id
2692
    # Clones
2693
    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
2694

    
2695

    
2696
@directory.register
2697
class KT9800R(BTech):
2698
    """QYT KT8900R"""
2699
    VENDOR = "QYT"
2700
    MODEL = "KT8900R"
2701
    _vhf_range = (136000000, 175000000)
2702
    _220_range = (240000000, 271000000)
2703
    _uhf_range = (400000000, 481000000)
2704
    _magic = MSTRING_KT8900R
2705
    _fileid = [KT8900R_fp,
2706
               KT8900R_fp1,
2707
               KT8900R_fp2,
2708
               KT8900R_fp3,
2709
               KT8900R_fp4]
2710
    _id2 = KT8900R_id
2711

    
2712

    
2713
@directory.register
2714
class LT588UV(BTech):
2715
    """LUITON LT-588UV"""
2716
    VENDOR = "LUITON"
2717
    MODEL = "LT-588UV"
2718
    _vhf_range = (136000000, 175000000)
2719
    _uhf_range = (400000000, 481000000)
2720
    _magic = MSTRING_KT8900
2721
    _fileid = [LT588UV_fp,
2722
                                LT588UV_fp1]