btech_kt8900d_1.py

Jim Unroe, 12/02/2016 04:30 pm

Download (95.8 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
# KT8900D
483
KT8900D_fp = "VC2002"
484

    
485
# LUITON LT-588UV
486
LT588UV_fp = "V2G1F4"
487

    
488

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

    
502

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

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

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

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

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

    
529

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

    
533
    data = ""
534

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

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

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

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

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

    
555
    return data
556

    
557

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

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

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

    
581

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

    
589
    return frame
590

    
591

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

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

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

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

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

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

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

    
619

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

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

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

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

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

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

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

    
650
                return True
651

    
652
        return False
653

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

    
659

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

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

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

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

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

    
692
            break
693

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

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

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

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

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

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

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

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

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

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

    
752
    return True
753

    
754

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

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

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

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

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

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

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

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

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

    
790
        # aggregate the data
791
        data += d
792

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

    
798
    return data
799

    
800

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
861

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

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

    
869
    return False
870

    
871

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

    
880
    return (ilow, ihigh)
881

    
882

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

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

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

    
897

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

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

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

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

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

    
940
            """))
941
        return rp
942

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

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

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

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

    
985
        rf.valid_power_levels = POWER_LEVELS
986

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

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

    
994
        return rf
995

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1108
        # Memory number
1109
        mem.number = number
1110

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1192
        return mem
1193

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

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

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

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

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

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

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

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

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

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

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

    
1283
        return mem
1284

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2027

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

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

    
2036
        top.append(group_5tone)
2037

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2417
            i = i + 1
2418

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2534
            i = i + 1
2535

    
2536
        return top
2537

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

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

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

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

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

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

    
2592

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

    
2598

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

    
2603

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

    
2608

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

    
2613

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

    
2618

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

    
2629

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

    
2641

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

    
2652

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

    
2663

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

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

    
2696

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

    
2713

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

    
2724
@directory.register
2725
class KT9800D(BTech):
2726
    """QYT KT8900D"""
2727
    VENDOR = "QYT"
2728
    MODEL = "KT8900D"
2729
    _vhf_range = (136000000, 175000000)
2730
    _220_range = (240000000, 271000000)
2731
    _uhf_range = (400000000, 481000000)
2732
    _magic = MSTRING_KT8900D
2733
    _fileid = [KT8900D_fp,
2734
              ]
2735