Project

General

Profile

Bug #11318 » tdh8.py

Dan Smith, 04/26/2024 02:20 PM

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

    
16
import struct
17
import time
18
import logging
19

    
20

    
21
from chirp import chirp_common, errors, util, directory, memmap
22
from chirp import bitwise
23
from chirp.settings import InvalidValueError, RadioSetting, \
24
    RadioSettingGroup, RadioSettingValueFloat, \
25
    RadioSettingValueList, RadioSettingValueBoolean, \
26
    RadioSettingValueString, RadioSettings
27
from textwrap import dedent
28
from chirp import bandplan_na
29

    
30
LOG = logging.getLogger(__name__)
31

    
32
AIRBAND = (108000000, 135999999)
33
MEM_FORMAT = """
34
#seekto 0x0008;
35
struct {
36
  lbcd rxfreq[4];
37
  lbcd txfreq[4];
38
  lbcd rxtone[2];
39
  lbcd txtone[2];
40
  u8 unused1;
41
  u8 pttid:2,
42
     freqhop:1,
43
     unused3:1,
44
     unused4:1,
45
     bcl:1,
46
     unused5:1,
47
     unused2:1;
48
  u8 unused6:1,
49
     unused7:1,
50
     lowpower:2,
51
     wide:1,
52
     unused8:1,
53
     offset:2;
54
  u8 unused10;
55
} memory[200];
56

    
57
#seekto 0x0CA8;
58
struct {
59
  u8 txled:1,
60
     rxled:1,
61
     unused11:1,
62
     ham:1,
63
     gmrs:1,
64
     unused14:1,
65
     unused15:1,
66
     pritx:1;
67
  u8 scanmode:2,
68
     unused16:1,
69
     keyautolock:1,
70
     unused17:1,
71
     btnvoice:1,
72
     unknown18:1,
73
     voiceprompt:1;
74
  u8 fmworkmode:1,
75
     sync:1,
76
     tonevoice:2,
77
     fmrec:1,
78
     mdfa:1,
79
     aworkmode:2;
80
  u8 ponmsg:2,
81
     unused19:1,
82
     mdfb:1,
83
     unused20:1,
84
     dbrx:1,
85
     bworkmode:2;
86
  u8 ablock;
87
  u8 bblock;
88
  u8 fmroad;
89
  u8 unused21:1,
90
     tailclean:1,
91
     rogerprompt:1,
92
     unused23:1,
93
     unused24:1,
94
     voxgain:3;
95
  u8 astep:4,
96
     bstep:4;
97
  u8 squelch;
98
  u8 tot;
99
  u8 lang;
100
  u8 save;
101
  u8 ligcon;
102
  u8 voxdelay;
103
  u8 onlychmode:1,
104
     unused:6,
105
     alarm:1;
106
} settings;
107

    
108
//#seekto 0x0CB8;
109
struct {
110
    u8 ofseta[4];
111
} aoffset;
112

    
113
//#seekto 0x0CBC;
114
struct {
115
    u8 ofsetb[4];
116
} boffset;
117

    
118
#seekto 0x0CD8;
119
struct{
120
    lbcd fmblock[4];
121
}fmmode[25];
122

    
123
#seekto 0x0D48;
124
struct {
125
  char name[8];
126
  u8 unknown2[8];
127
} names[200];
128

    
129
#seekto 0x1A08;
130
lbit usedflags[200];
131

    
132
#seekto 0x1a28;
133
lbit scanadd[200];
134

    
135
#seekto 0x1B38;
136
lbcd fmvfo[4];
137

    
138
#seekto 0x1B58;
139
struct {
140
  lbcd rxfreqa[4];
141
  lbcd txfreq[4];
142
  u8 rxtone[2];
143
  u8 txtone[2];
144
  u8 unused1;
145
  u8 pttid:2,
146
     specialqta:1,
147
     unused3:1,
148
     unused4:1,
149
     bcl:1,
150
     unused5:1,
151
     unused2:1;
152
  u8 unused6:1,
153
     unused7:1,
154
     lowpower:2,
155
     wide:1,
156
     unused8:1,
157
     offset:2;
158
  u8 unused10;
159
} vfoa;
160

    
161
//#seekto 0x1B68;
162
struct {
163
  lbcd rxfreqb[4];
164
  lbcd txfreq[4];
165
  u8 rxtoneb[2];
166
  u8 txtone[2];
167
  u8 unused1;
168
  u8 pttid:2,
169
     specialqtb:1,
170
     unused3:1,
171
     unused4:1,
172
     bclb:1,
173
     unused5:1,
174
     unused2:1;
175
  u8 unused6:1,
176
     unused7:1,
177
     lowpowerb:2,
178
     wideb:1,
179
     unused8:1,
180
     offsetb:2;
181
  u8 unused10;
182
} vfob;
183

    
184
//#seekto 0x1B78;
185
lbit fmusedflags[32];
186

    
187
#seekto 0x1c08;
188
struct {
189
  char msg1[16];
190
  char msg2[16];
191
  char msg3[16];
192
} poweron_msg;
193

    
194

    
195
#seekto 0x1CC8;
196
struct{
197
  u8 stopkey1;
198
  u8 ssidekey1;
199
  u8 ssidekey2;
200
  u8 ltopkey2;
201
  u8 lsidekey3;
202
  u8 lsidekey4;
203
  u8 unused25[10];
204
} press;
205

    
206
#seekto 0x1E28;
207
struct{
208
    u8 idcode[3];
209
}icode;
210

    
211
#seekto 0x1E31;
212
struct{
213
    u8 gcode;
214
}groupcode;
215

    
216
#seekto 0x1E38;
217
struct{
218
    u8 group1[7];
219
}group1;
220

    
221
#seekto 0x1E48;
222
struct{
223
    u8 group2[7];
224
}group2;
225

    
226
#seekto 0x1E58;
227
struct{
228
    u8 group3[7];
229
}group3;
230

    
231
#seekto 0x1E68;
232
struct{
233
    u8 group4[7];
234
}group4;
235

    
236
#seekto 0x1E78;
237
struct{
238
    u8 group5[7];
239
}group5;
240

    
241
#seekto 0x1E88;
242
struct{
243
    u8 group6[7];
244
}group6;
245

    
246
#seekto 0x1E98;
247
struct{
248
    u8 group7[7];
249
}group7;
250

    
251
#seekto 0x1EA8;
252
struct{
253
    u8 group8[7];
254
}group8;
255

    
256
#seekto 0x1EC8;
257
struct{
258
    u8 scode[7];
259
}startcode;
260

    
261
#seekto 0x1ED8;
262
struct{
263
    u8 ecode[7];
264
}endcode;
265
"""
266

    
267
MEM_FORMAT_H3 = """
268
#seekto 0x0008;
269
struct {
270
  lbcd rxfreq[4];
271
  lbcd txfreq[4];
272
  lbcd rxtone[2];
273
  lbcd txtone[2];
274
  u8 unused1;
275
  u8 pttid:2,
276
     freqhop:1,
277
     unused3:1,
278
     unused4:1,
279
     bcl:1,
280
     unused5:1,
281
     unused2:1;
282
  u8 unused6:1,
283
     scramble:1,
284
     lowpower:2,
285
     wide:1,
286
     unused8:1,
287
     offset:2;
288
  u8 unused10;
289
} memory[200];
290

    
291
#seekto 0x0C98;
292
struct{
293
  u8 stopkey1;
294
  u8 ssidekey1;
295
  u8 ssidekey2;
296
  u8 ltopkey2;
297
  u8 lsidekey3;
298
  u8 lsidekey4;
299
} press;
300

    
301
#seekto 0x0CA8;
302
struct {
303
  u8 txled:1,
304
     rxled:1,
305
     unused11:1,
306
     ham:1,
307
     gmrs:1,
308
     unused14:1,
309
     unused15:1,
310
     pritx:1;
311
  u8 scanmode:2,
312
     unused16:1,
313
     keyautolock:1,
314
     unused17:1,
315
     btnvoice:1,
316
     unknown18:1,
317
     voiceprompt:1;
318
  u8 fmworkmode:1,
319
     sync:1,
320
     tonevoice:2,
321
     fmrec:1,
322
     mdfa:1,
323
     aworkmode:2;
324
  u8 ponmsg:2,
325
     unused19:1,
326
     mdfb:1,
327
     unused20:1,
328
     dbrx:1,
329
     bworkmode:2;
330
  u8 ablock;
331
  u8 bblock;
332
  u8 fmroad;
333
  u8 unused21:1,
334
     tailclean:1,
335
     rogerprompt_:1,
336
     kill:1,
337
     stun:1,
338
     voxgain:3;
339
  u8 astep:4,
340
     bstep:4;
341
  u8 squelch;
342
  u8 tot;
343
  u8 rogerprompt:2,
344
     unused11_4:4,
345
     lang:1,
346
     unused11_1:1;
347
  u8 save;
348
  u8 ligcon;
349
  u8 voxdelay;
350
  u8 onlychmode:1,
351
     breathled:3,
352
     unused:3,
353
     alarm:1;
354
} settings;
355

    
356
//#seekto 0x0CB8;
357
struct {
358
    u8 ofseta[4];
359
} aoffset;
360

    
361
//#seekto 0x0CBC;
362
struct {
363
    u8 ofsetb[4];
364
} boffset;
365

    
366
#seekto 0x0CD8;
367
struct{
368
    lbcd fmblock[4];
369
}fmmode[25];
370

    
371
#seekto 0x0D48;
372
struct {
373
  char name[8];
374
} names[200];
375

    
376
#seekto 0x1808;
377
struct{
378
    u8 stuncode[16];
379
    u8 killcode[16];
380
}skcode;
381

    
382
//#seekto 0x1828;
383
struct{
384
    u8 idcode[3];
385
}icode;
386

    
387
#seekto 0x1837;
388
struct{
389
    u8 gcode;
390
}groupcode;
391

    
392
//#seekto 0x1838;
393
struct{
394
    u8 group1[7];
395
}group1;
396

    
397
#seekto 0x1848;
398
struct{
399
    u8 group2[7];
400
}group2;
401

    
402
#seekto 0x1858;
403
struct{
404
    u8 group3[7];
405
}group3;
406

    
407
#seekto 0x1868;
408
struct{
409
    u8 group4[7];
410
}group4;
411

    
412
#seekto 0x1878;
413
struct{
414
    u8 group5[7];
415
}group5;
416

    
417
#seekto 0x1888;
418
struct{
419
    u8 group6[7];
420
}group6;
421

    
422
#seekto 0x1898;
423
struct{
424
    u8 group7[7];
425
}group7;
426

    
427
#seekto 0x18A8;
428
struct{
429
    u8 group8[7];
430
}group8;
431

    
432
#seekto 0x18C8;
433
struct{
434
    u8 scode[7];
435
}startcode;
436

    
437
#seekto 0x18D8;
438
struct{
439
    u8 ecode[7];
440
}endcode;
441

    
442
#seekto 0x1908;
443
lbit usedflags[200];
444

    
445
#seekto 0x1928;
446
lbit scanadd[200];
447

    
448
#seekto 0x1948;
449
lbit fmusedflags[32];
450

    
451
#seekto 0x1958;
452
struct {
453
  lbcd rxfreqa[4];
454
  lbcd txfreq[4];
455
  u8 rxtone[2];
456
  u8 txtone[2];
457
  u8 unused1;
458
  u8 pttid:2,
459
     specialqta:1,
460
     unused3:1,
461
     unused4:1,
462
     bcl:1,
463
     unused5:1,
464
     unused2:1;
465
  u8 unused6:1,
466
     unused7:1,
467
     lowpower:2,
468
     wide:1,
469
     unused8:1,
470
     offset:2;
471
  u8 unused10;
472
} vfoa;
473

    
474
//#seekto 0x1968;
475
struct {
476
  lbcd rxfreqb[4];
477
  lbcd txfreq[4];
478
  u8 rxtoneb[2];
479
  u8 txtone[2];
480
  u8 unused1;
481
  u8 pttid:2,
482
     specialqtb:1,
483
     unused3:1,
484
     unused4:1,
485
     bclb:1,
486
     unused5:1,
487
     unused2:1;
488
  u8 unused6:1,
489
     unused7:1,
490
     lowpowerb:2,
491
     wideb:1,
492
     unused8:1,
493
     offsetb:2;
494
  u8 unused10;
495
} vfob;
496

    
497
//#seekto 0x1978;
498
lbcd fmvfo[4];
499

    
500
#seekto 0x1c08;
501
struct {
502
  char msg1[16];
503
  char msg2[16];
504
  char msg3[16];
505
} poweron_msg;
506

    
507
#seekto 0x1f28;
508
struct{
509
  u8 micgain;
510
} mic;
511

    
512
"""
513

    
514
# basic settings
515
SQUELCH = ['%s' % x for x in range(0, 10)]
516
LIGHT_LIST = ["CONT", "5s", "10s", "15s", "30s"]
517
VOICE_PRMPT_LIST = ["OFF", "ON"]
518
AUTOLOCK_LIST = ["OFF", "ON"]
519
TIME_OUT_LIST = ["OFF", "60s", "120s", "180s"]
520
MDFA_LIST = ["Frequency", "Name"]
521
MDFB_LIST = ["Frequency", "Name"]
522
SYNC_LIST = ["ON", "OFF"]
523
LANG_LIST = ["Chinese", "English"]
524
BTV_SAVER_LIST = ["OFF", "1:1", "1:2", "1:3", "1:4"]
525
DBRX_LIST = ["OFF", "ON"]
526
ASTEP_LIST = ["2.50K", "5.00K", "6.25K",
527
              "10.00K", "12.00K", "25.00K", "50.00K"]
528
BSTEP_LIST = ["2.50K", "5.00K", "6.25K",
529
              "10.00K", "12.00K", "25.00K", "50.00K"]
530
SCAN_MODE_LIST = ["TO", "CO", "SE"]
531
PRIO_LIST = ["Edit", "Busy"]
532
SHORT_KEY_LIST = ["None", "FM Radio", "Lamp", "Monitor",
533
                  "TONE", "Alarm", "Weather"]
534
LONG_KEY_LIST = ["None", "FM Radio", "Lamp",
535
                 "Monitor", "TONE", "Alarm", "Weather"]
536
BUSYLOCK_LIST = ["Off", "On"]
537
PRESS_NAME = ["stopkey1", "ssidekey1", "ssidekey2",
538
              "ltopkey2", "lsidekey3", "lsidekey4"]
539

    
540
VFOA_NAME = ["rxfreqa",
541
             "txfreq",
542
             "rxtone",
543
             "txtone",
544
             "pttid",
545
             "specialqta",
546
             "bcl",
547
             "lowpower",
548
             "wide",
549
             "offset"]
550

    
551
VFOB_NAME = ["rxfreqb",
552
             "txfreq",
553
             "rxtoneb",
554
             "txtone",
555
             "pttid",
556
             "specialqtb",
557
             "bclb",
558
             "lowpowerb",
559
             "wideb",
560
             "offsetb"]
561

    
562
# KEY
563
VOX_GAIN = ["OFF", "1", "2", "3", "4", "5"]
564
VOX_DELAY = ["1.05s", "2.0s", "3.0s"]
565
PTTID_VALUES = ["Off", "BOT", "EOT", "BOTH"]
566
BCLOCK_VALUES = ["Off", "On"]
567
FREQHOP_VALUES = ["Off", "On"]
568
SCAN_VALUES = ["Del", "Add"]
569

    
570
# AB CHANNEL
571
A_OFFSET = ["Off", "-", "+"]
572
A_TX_POWER = ["Low", "Mid", "High"]
573
A_BAND = ["Wide", "Narrow"]
574
A_BUSYLOCK = ["Off", "On"]
575
A_SPEC_QTDQT = ["Off", "On"]
576
A_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
577

    
578
B_OFFSET = ["Off", "-", "+"]
579
B_TX_POWER = ["Low", "Mid", "High"]
580
B_BAND = ["Wide", "Narrow"]
581
B_BUSYLOCK = ["Off", "On"]
582
B_SPEC_QTDQT = ["Off", "On"]
583
B_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
584

    
585
# FM
586
FM_WORKMODE = ["CH", "VFO"]
587
FM_CHANNEL = ['%s' % x for x in range(0, 26)]
588

    
589
# DTMF
590
GROUPCODE = ["", "Off", "*", "#", "A", "B", "C", "D"]
591

    
592
AB_LIST = ["A", "B"]
593
ALMOD_LIST = ["Site", "Tone", "Code"]
594
BANDWIDTH_LIST = ["Wide", "Narrow"]
595
COLOR_LIST = ["Off", "Blue", "Orange", "Purple"]
596
DTMFSPEED_LIST = ["%s ms" % x for x in range(50, 2010, 10)]
597
DTMFST_LIST = ["OFF", "DT-ST", "ANI-ST", "DT+ANI"]
598
MODE_LIST = ["Channel", "Name", "Frequency"]
599
PONMSG_LIST = ["Full", "Message", "Icon"]
600
PTTID_LIST = ["Off", "BOT", "EOT", "Both"]
601
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
602
RTONE_LIST = ["1000 Hz", "1450 Hz", "1750 Hz", "2100 Hz"]
603
RESUME_LIST = ["TO", "CO", "SE"]
604
ROGERRX_LIST = ["Off"] + AB_LIST
605
RPSTE_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
606
SAVE_LIST = ["Off", "1:1", "1:2", "1:3", "1:4"]
607
SCODE_LIST = ["%s" % x for x in range(1, 16)]
608
SHIFTD_LIST = ["Off", "+", "-"]
609
STEDELAY_LIST = ["OFF"] + ["%s ms" % x for x in range(100, 1100, 100)]
610
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
611
STEP_LIST = [str(x) for x in STEPS]
612
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 50.0]
613
STEP291_LIST = [str(x) for x in STEPS]
614
TDRAB_LIST = ["Off"] + AB_LIST
615
TDRCH_LIST = ["CH%s" % x for x in range(1, 129)]
616
TIMEOUT_LIST = ["%s sec" % x for x in range(15, 615, 15)] + \
617
    ["Off (if supported by radio)"]
618
VOICE_LIST = ["Off", "English", "Chinese"]
619
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
620
WORKMODE_LIST = ["Frequency", "Channel"]
621
# mic
622
MIC_GAIN_LIST = ['%s' % x for x in range(0, 10)]
623
MIC_GAIN_LIST_H8 = ['%s' % x for x in range(0, 33)]
624
H8_LIST = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS"]
625

    
626
GMRS_FREQS = bandplan_na.GMRS_HIRPT
627

    
628
NOAA_FREQS = [162550000, 162400000, 162475000, 162425000, 162450000,
629
              162500000, 162525000, 161650000, 161775000, 161750000,
630
              162000000]
631

    
632
HAM_GMRS_NAME = ["NOAA 1", "NOAA 2", "NOAA 3", "NOAA 4", "NOAA 5", "NOAA 6",
633
                 "NOAA 7", "NOAA 8", "NOAA 9", "NOAA 10", "NOAA 11"]
634

    
635
ALL_MODEL = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS", "TD-H3",
636
             "TD-H3-HAM", "TD-H3-GMRS"]
637

    
638
TD_H8 = b"\x50\x56\x4F\x4A\x48\x1C\x14"
639
TD_H3 = b"\x50\x56\x4F\x4A\x48\x5C\x14"
640

    
641

    
642
def in_range(freq, ranges):
643
    for lo, hi in ranges:
644
        if lo <= freq <= hi:
645
            return True
646
    return False
647

    
648

    
649
def _do_status(radio, block):
650
    status = chirp_common.Status()
651
    status.msg = "Cloning"
652
    status.cur = block
653
    status.max = radio._memsize
654
    radio.status_fn(status)
655

    
656

    
657
def _upper_band_from_data(data):
658
    return data[0x03:0x04]
659

    
660

    
661
def _upper_band_from_image(radio):
662
    return _upper_band_from_data(radio.get_mmap())
663

    
664

    
665
def _firmware_version_from_data(data, version_start, version_stop):
666
    version_tag = data[version_start:version_stop]
667
    return version_tag
668

    
669

    
670
def _firmware_version_from_image(radio):
671
    version = _firmware_version_from_data(radio.get_mmap(),
672
                                          radio._fw_ver_file_start,
673
                                          radio._fw_ver_file_stop)
674
    # LOG.debug("_firmware_version_from_image: " + util.hexprint(version))
675
    return version
676

    
677

    
678
def _do_ident(radio, magic, secondack=True):
679
    serial = radio.pipe
680
    serial.timeout = 1
681

    
682
    LOG.info("Sending Magic: %s" % util.hexprint(magic))
683
    serial.write(magic)
684
    ack = serial.read(1)
685

    
686
    if ack != b"\x06":
687
        if ack:
688
            # LOG.debug(repr(ack))
689
            pass
690
        raise errors.RadioError("Radio did not respond")
691

    
692
    serial.write(b"\x02")
693

    
694
    response = b""
695
    for i in range(1, 9):
696
        byte = serial.read(1)
697
        response += byte
698
        if byte == b"\xDD":
699
            break
700

    
701
    if len(response) in [8, 12]:
702
        # DEBUG
703
        LOG.info("Valid response, got this:")
704
        LOG.info(util.hexprint(response))
705
        if len(response) == 12:
706
            ident = response[0] + response[3] + response[5] + response[7:]
707
        else:
708
            ident = response
709
    else:
710
        # bad response
711
        msg = "Unexpected response, got this:"
712
        msg += util.hexprint(response)
713
        LOG.debug(msg)
714
        raise errors.RadioError("Unexpected response from radio.")
715

    
716
    if secondack:
717
        serial.write(b"\x06")
718
        ack = serial.read(1)
719
        if ack != b"\x06":
720
            raise errors.RadioError("Radio refused clone")
721

    
722
    return ident
723

    
724

    
725
def response_mode(mode):
726
    data = mode
727
    return data
728

    
729

    
730
def _read_block(radio, start, size):
731
    serial = radio.pipe
732

    
733
    cmd = struct.pack(">cHb", b'R', start, size)
734
    expectedresponse = b"W" + cmd[1:]
735

    
736
    try:
737
        serial.write(cmd)
738
        response = serial.read(5 + size)
739
        if response[:4] != expectedresponse:
740
            raise errors.RadioError("Error reading block %04x." % (start))
741
        block_data = response[4:-1]
742

    
743
    except Exception:
744
        raise errors.RadioError("Failed to read block at %04x" % start)
745

    
746
    return block_data
747

    
748

    
749
def _get_radio_firmware_version(radio):
750
    if radio.MODEL in ALL_MODEL:
751
        block = _read_block(radio, 0x1B40, 0x20)
752
        version = block[0:6]
753
    return version
754

    
755

    
756
IDENT_BLACKLIST = {
757
    b"\x50\x56\x4F\x4A\x48\x1C\x14": "Radio identifies as TIDRADIO TD-H8",
758
}
759

    
760

    
761
def _ident_radio(radio):
762
    for magic in radio._idents:
763
        error = None
764
        try:
765
            data = _do_ident(radio, magic)
766
            return data
767
        except errors.RadioError as e:
768
            error = e
769
            time.sleep(2)
770

    
771
    if error:
772
        raise error
773
    raise errors.RadioError("Radio did not respond")
774

    
775

    
776
def _do_download(radio):
777
    data = _ident_radio(radio)
778
    append_model = False
779
    # HAM OR GMRS
780
    # Determine the walkie-talkie mode
781
    # TDH8 have three mode:ham, gmrs and normal
782

    
783
    LOG.info("Radio mode is " + str(data)[2:8])
784
    LOG.info("Chirp choose mode is " + str(data)[2:8])
785
    # The Ham and GMRS modes are subclasses of this model TDH8.
786
    # We compare the radio identification to the value of that class to
787
    # make sure the user chose the model that matches
788
    # the radio we're talking to right now. If they do not match,
789
    # we refuse to talk to the radio until the user selects the correct model.
790

    
791
    if radio.ident_mode == data:
792
        LOG.info("Successful match.")
793
    else:
794
        msg = ("Model mismatch!")
795
        raise errors.RadioError(msg)
796

    
797
    # Main block
798
    LOG.info("Downloading...")
799

    
800
    for i in range(0, radio._memsize, 0x20):
801
        block = _read_block(radio, i, 0x20)
802
        data += block
803
        _do_status(radio, i)
804
    _do_status(radio, radio._memsize)
805
    LOG.info("done.")
806

    
807
    if append_model:
808
        data += radio.MODEL.ljust(8)
809

    
810
    return memmap.MemoryMapBytes(data)
811

    
812

    
813
def _exit_write_block(radio):
814
    serial = radio.pipe
815
    try:
816
        serial.write(b"E")
817

    
818
    except Exception:
819
        raise errors.RadioError("Radio refused to exit programming mode")
820

    
821

    
822
def _write_block(radio, addr, data):
823
    serial = radio.pipe
824
    cmd = struct.pack(">cHb", b'W', addr, 0x20)
825
    data = radio.get_mmap()[addr + 8: addr + 40]
826
    # The checksum needs to be in the last
827
    check_sum = bytes([sum(data) & 0xFF])
828
    data += check_sum
829
    used_data = cmd + data
830
    serial.write(used_data)
831

    
832
    ack = radio.pipe.read(1)
833
    if ack != b"\x06":
834
        raise errors.RadioError("Radio refused to accept block 0x%04x" % addr)
835

    
836

    
837
def _do_upload(radio):
838
    data = _ident_radio(radio)
839
    radio_version = _get_radio_firmware_version(radio)
840
    LOG.info("Radio Version is %s" % repr(radio_version))
841

    
842
    if radio.ident_mode == data:
843
        LOG.info("Successful match.")
844
    else:
845
        msg = ("Model mismatch!")
846
        raise errors.RadioError(msg)
847

    
848
    # Main block
849
    LOG.debug("Uploading...")
850

    
851
    for start_addr, end_addr in radio._ranges_main:
852
        for addr in range(start_addr, end_addr, 0x20):
853
            _write_block(radio, addr, 0x20)
854
            _do_status(radio, addr)
855
    _exit_write_block(radio)
856
    LOG.debug("Upload all done.")
857

    
858

    
859
TDH8_CHARSET = chirp_common.CHARSET_ALPHANUMERIC + \
860
    "!@#$%^&*()+-=[]:\";'<>?,./"
861

    
862

    
863
@directory.register
864
class TDH8(chirp_common.CloneModeRadio):
865
    """TIDRADIO TD-H8"""
866
    VENDOR = "TIDRADIO"
867
    MODEL = "TD-H8"
868
    ident_mode = b'P31183\xff\xff'
869
    BAUD_RATE = 38400
870
    NEEDS_COMPAT_SERIAL = False
871
    _memsize = 0x1eef
872
    _ranges_main = [(0x0000, 0x1eef)]
873
    _idents = [TD_H8]
874
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
875
    _rxbands = []
876
    _aux_block = True
877
    _tri_power = True
878
    _gmrs = False
879
    _ham = False
880
    _mem_params = (0x1F2F)
881

    
882
    # offset of fw version in image file
883
    _fw_ver_file_start = 0x1838
884
    _fw_ver_file_stop = 0x1846
885
    _valid_chars = TDH8_CHARSET
886
    _tx_power = [chirp_common.PowerLevel("Low",  watts=1.00),
887
                 chirp_common.PowerLevel("Mid",  watts=4.00),
888
                 chirp_common.PowerLevel("High", watts=8.00)]
889

    
890
    @classmethod
891
    def get_prompts(cls):
892
        rp = chirp_common.RadioPrompts()
893
        rp.pre_download = (dedent("""\
894
            1. Turn radio off.
895
            2. Connect cable to mic/spkr connector.
896
            3. Make sure connector is firmly connected.
897
            4. Turn radio on (volume may need to be set at 100%).
898
            5. Ensure that the radio is tuned to channel with no activity.
899
            6. Click OK to download image from device."""))
900
        rp.pre_upload = (dedent("""\
901
            1. Turn radio off.
902
            2. Connect cable to mic/spkr connector.
903
            3. Make sure connector is firmly connected.
904
            4. Turn radio on (volume may need to be set at 100%).
905
            5. Ensure that the radio is tuned to channel with no activity.
906
            6. Click OK to upload image to device."""))
907
        return rp
908

    
909
    def get_features(self):
910
        rf = chirp_common.RadioFeatures()
911
        rf.has_settings = True
912
        rf.has_bank = False
913
        rf.has_cross = True
914
        rf.has_ctone = True
915
        rf.has_rx_dtcs = True
916
        rf.has_tuning_step = False
917
        rf.has_ctone = True
918
        rf.can_odd_split = True
919
        rf.valid_name_length = 8
920
        rf.valid_characters = self._valid_chars
921
        rf.valid_skips = ["", "S"]
922
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
923
        rf.valid_cross_modes = [
924
            "Tone->Tone",
925
            "DTCS->",
926
            "->DTCS",
927
            "Tone->DTCS",
928
            "DTCS->Tone",
929
            "->Tone",
930
            "DTCS->DTCS"]
931
        rf.valid_power_levels = [x for x in self._tx_power if x]
932
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
933
        rf.valid_modes = ["FM", "NFM"]
934
        rf.valid_tuning_steps = STEPS
935

    
936
        rf.valid_bands = self._txbands + self._rxbands
937
        rf.valid_bands.sort()
938
        rf.memory_bounds = (1, 199)
939
        return rf
940

    
941
    def process_mmap(self):
942
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
943

    
944
    def sync_in(self):
945
        try:
946
            self._mmap = _do_download(self)
947
            self.process_mmap()
948
        except Exception as e:
949
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
950

    
951
    def sync_out(self):
952
        try:
953
            _do_upload(self)
954
        except errors.RadioError:
955
            raise
956
        except Exception as e:
957
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
958

    
959
    def get_raw_memory(self, number):
960
        return repr(self._memobj.memory[number])
961

    
962
    # Encoding processing
963
    def _decode_tone(self, val):
964
        if val == 16665 or val == 0:
965
            return '', None, None
966
        elif val >= 12000:
967
            return 'DTCS', val - 12000, 'R'
968
        elif val >= 8000:
969
            return 'DTCS', val - 8000, 'N'
970
        else:
971
            return 'Tone', val / 10.0, None
972

    
973
    # Decoding processing
974
    def _encode_tone(self, memval, mode, value, pol):
975
        if mode == "":
976
            memval[0].set_raw(0xFF)
977
            memval[1].set_raw(0xFF)
978
        elif mode == 'Tone':
979
            memval.set_value(int(value * 10))
980

    
981
        elif mode == 'DTCS':
982
            flag = 0x80 if pol == 'N' else 0xC0
983
            memval.set_value(value)
984
            memval[1].set_bits(flag)
985
        else:
986
            raise Exception("Internal error: invalid mode `%s'" % mode)
987

    
988
    def _get_mem(self, number):
989
        return self._memobj.memory[number]
990

    
991
    def _get_nam(self, number):
992
        return self._memobj.names[number - 1]
993

    
994
    def _get_fm(self, number):
995
        return self._memobj.fmmode[number]
996

    
997
    def _get_get_scanvfo(self, number):
998
        return self._memobj.fmvfo[number]
999

    
1000
    def get_memory(self, number):
1001
        _mem = self._get_mem(number)
1002
        _nam = self._get_nam(number)
1003
        mem = chirp_common.Memory()
1004
        mem.number = number
1005

    
1006
        if _mem.get_raw()[0] == 0xff:
1007
            mem.empty = True
1008
            return mem
1009

    
1010
        # narrow and wide
1011
        mem.mode = _mem.wide and "NFM" or "FM"
1012

    
1013
        # power
1014
        try:
1015
            mem.power = self._tx_power[_mem.lowpower]
1016
            if mem.power is None:
1017
                # Gaps are basically missing power levels
1018
                raise IndexError()
1019
        except IndexError:
1020
            LOG.error("Radio reported invalid power level %s (in %s)" %
1021
                      (_mem.lowpower, self._tx_power))
1022
            mem.power = self._tx_power[0]
1023

    
1024
        # Channel name
1025
        for char in _nam.name:
1026
            if "\x00" in str(char) or "\xFF" in str(char):
1027
                char = ""
1028
            mem.name += str(char)
1029

    
1030
        mem.name = mem.name.rstrip()
1031
        if self.ident_mode != b'P31183\xff\xff' and \
1032
                (mem.number >= 189 and mem.number <= 199):
1033
            mem.name = HAM_GMRS_NAME[mem.number - 200]
1034

    
1035
        # tmode
1036
        lin2 = int(_mem.rxtone)
1037
        rxtone = self._decode_tone(lin2)
1038

    
1039
        lin = int(_mem.txtone)
1040
        txtone = self._decode_tone(lin)
1041

    
1042
        if txtone[0] == "Tone" and not rxtone[0]:
1043
            mem.tmode = "Tone"
1044
        elif txtone[0] == rxtone[0] and txtone[0] == "Tone" \
1045
                and mem.rtone == mem.ctone:
1046
            mem.tmode = "TSQL"
1047
        elif txtone[0] == rxtone[0] and txtone[0] == "DTCS" \
1048
                and mem.dtcs == mem.rx_dtcs:
1049
            mem.tmode = "DTCS"
1050
        elif rxtone[0] or txtone[0]:
1051
            mem.tmode = "Cross"
1052
            mem.cross_mode = "%s->%s" % (txtone[0], rxtone[0])
1053

    
1054
        chirp_common.split_tone_decode(mem, txtone, rxtone)
1055

    
1056
        mem.skip = '' if self._memobj.scanadd[mem.number - 1] else 'S'
1057

    
1058
        mem.freq = int(_mem.rxfreq) * 10
1059
        if _mem.txfreq.get_raw() == b'\xff\xff\xff\xff':
1060
            mem.offset = 0
1061
            mem.duplex = 'off'
1062
        else:
1063
            chirp_common.split_to_offset(mem,
1064
                                         int(_mem.rxfreq) * 10,
1065
                                         int(_mem.txfreq) * 10)
1066

    
1067
        if self._gmrs:
1068
            # mem.duplex = ""
1069
            # mem.offset = 0
1070
            if mem.number >= 1 and mem.number <= 30:
1071
                mem.immutable.append('freq')
1072
                if mem.number >= 8 and mem.number <= 14:
1073
                    mem.mode = 'NFM'
1074
                    mem.power = self._tx_power[0]
1075
                    mem.immutable = ['freq', 'mode', 'power',
1076
                                     'duplex', 'offset']
1077
            elif mem.number >= 31 and mem.number <= 54:
1078
                # mem.immutable = ['duplex', 'offset']
1079
                mem.duplex = '+'
1080
                mem.offset = 5000000
1081
            elif mem.number >= 189 and mem.number <= 199:
1082
                ham_freqs = NOAA_FREQS[mem.number - 189]
1083
                mem.freq = ham_freqs
1084
                mem.immutable = ['name', 'power', 'duplex', 'freq',
1085
                                 'rx_dtcs', 'vfo', 'tmode', 'empty',
1086
                                 'offset', 'rtone', 'ctone', 'dtcs',
1087
                                 'dtcs_polarity', 'cross_mode']
1088
        elif self._ham:
1089
            if mem.number >= 189 and mem.number <= 199:
1090
                ham_freqs = NOAA_FREQS[mem.number - 189]
1091
                mem.freq = ham_freqs
1092
                mem.immutable = ['name', 'power', 'freq', 'rx_dtcs', 'vfo',
1093
                                 'tmode', 'empty', 'offset', 'rtone', 'ctone',
1094
                                 'dtcs', 'dtcs_polarity', 'cross_mode']
1095

    
1096
        # other function
1097
        # pttid
1098
        mem.extra = RadioSettingGroup("Extra", "extra")
1099

    
1100
        rs = RadioSetting("pttid", "PTT ID",
1101
                          RadioSettingValueList(PTTID_VALUES,
1102
                                                PTTID_VALUES[_mem.pttid]))
1103
        mem.extra.append(rs)
1104

    
1105
        # Busylock
1106
        rs = RadioSetting("bcl", "Busy Lock",
1107
                          RadioSettingValueList(BCLOCK_VALUES,
1108
                                                BCLOCK_VALUES[_mem.bcl]))
1109
        mem.extra.append(rs)
1110

    
1111
        rs = RadioSetting(
1112
            "freqhop", "Frequency Hop", RadioSettingValueList(
1113
                FREQHOP_VALUES, FREQHOP_VALUES[_mem.freqhop]))
1114
        mem.extra.append(rs)
1115

    
1116
        if in_range(mem.freq, self._rxbands):
1117
            mem.duplex = 'off'
1118
            mem.immutable.append('duplex')
1119
        if in_range(mem.freq, [AIRBAND]):
1120
            mem.mode = 'AM'
1121
            mem.immutable.append('mode')
1122

    
1123
        return mem
1124

    
1125
    def _set_mem(self, number):
1126
        return self._memobj.memory[number]
1127

    
1128
    def _set_nam(self, number):
1129
        return self._memobj.names[number - 1]
1130

    
1131
    def _get_scan_list(self, scan_data):
1132
        # scan_val_list - Get all scans Add data 1-200 digits
1133
        scan_val_list = []
1134
        for x in range(25):
1135
            a = self._get_scan(x)
1136
            for i in range(0, 8):
1137
                scan_val = (getattr(a, 'scan%i' % (i+1)))
1138
                used_scan_val = str(scan_val)[3]
1139
                scan_val_list.append(used_scan_val)
1140

    
1141
        # used_scan_list - 25 structures, split the scan added
1142
        # data into 25 groups of 8 bits each
1143
        used_scan_list = []
1144
        count_num = 1
1145
        for i in range(0, len(scan_val_list), 8):
1146
            used_scan_list.append(scan_val_list[i:i + 8])
1147
            count_num += 1
1148
        # Determine whether it is a standard number that can be divisible
1149
        # Which group is the scan addition located in the modified channel
1150
        if scan_data % 8 != 0:
1151
            x_list = scan_data / 8
1152
            y_list = scan_data % 8
1153

    
1154
        else:
1155
            x_list = (scan_data / 8) - 1
1156
            y_list = 8
1157

    
1158
        return ([x_list, y_list])
1159

    
1160
    def set_memory(self, mem):
1161
        _mem = self._get_mem(mem.number)
1162
        _nam = self._get_nam(mem.number)
1163

    
1164
        # When the channel is empty, you need to set "usedflags" to 0,
1165
        # When the channel is used , you need to set "usedflags" to 1.
1166
        self._memobj.usedflags[mem.number - 1] = int(not mem.empty)
1167

    
1168
        if mem.empty:
1169
            _mem.fill_raw(b'\xFF')
1170
            return
1171

    
1172
        _mem.fill_raw(b'\x00')
1173

    
1174
        if mem.duplex == "":
1175
            _mem.rxfreq = _mem.txfreq = mem.freq / 10
1176
        elif mem.duplex == "split":
1177
            _mem.txfreq = mem.offset / 10
1178
        elif mem.duplex == "+":
1179
            _mem.txfreq = (mem.freq + mem.offset) / 10
1180
        elif mem.duplex == "-":
1181
            _mem.txfreq = (mem.freq - mem.offset) / 10
1182
        elif mem.duplex == 'off':
1183
            _mem.txfreq.fill_raw(b'\xFF')
1184
        else:
1185
            _mem.txfreq = mem.freq / 10
1186

    
1187
        if in_range(mem.freq, self._rxbands):
1188
            _mem.txfreq.fill_raw(b'\xFF')
1189

    
1190
        _mem.rxfreq = mem.freq / 10
1191
        _namelength = self.get_features().valid_name_length
1192

    
1193
        for i in range(_namelength):
1194
            try:
1195
                _nam.name[i] = mem.name[i]
1196
            except IndexError:
1197
                _nam.name[i] = "\xFF"
1198

    
1199
        txtone, rxtone = chirp_common.split_tone_encode(mem)
1200

    
1201
        self._encode_tone(_mem.txtone, *txtone)
1202
        self._encode_tone(_mem.rxtone, *rxtone)
1203

    
1204
        if mem.mode == "FM":
1205
            _mem.wide = 0
1206
        else:
1207
            _mem.wide = 1
1208

    
1209
        try:
1210
            _mem.lowpower = self._tx_power.index(mem.power or
1211
                                                 self._tx_power[-1])
1212
        except ValueError:
1213
            _mem.lowpower = 0
1214
            LOG.warning('Unsupported power %r', mem.power)
1215

    
1216
        # Skip/Scanadd Setting
1217
        self._memobj.scanadd[mem.number - 1] = mem.skip != 'S'
1218

    
1219
        for setting in mem.extra:
1220
            if (self.ident_mode == b'P31185\xff\xff' or
1221
                self.ident_mode == b'P31184\xff\xff') and \
1222
                    mem.number >= 189 and mem.number <= 199:
1223
                if setting.get_name() == 'pttid':
1224
                    setting.value = 'Off'
1225
                    setattr(_mem, setting.get_name(), setting.value)
1226
                elif setting.get_name() == 'bcl':
1227
                    setting.value = 'Off'
1228
                    setattr(_mem, setting.get_name(), setting.value)
1229
                elif setting.get_name() == 'freqhop':
1230
                    setting.value = 'Off'
1231
                    setattr(_mem, setting.get_name(), setting.value)
1232
            else:
1233
                setattr(_mem, setting.get_name(), setting.value)
1234

    
1235
    def _is_orig(self):
1236
        version_tag = _firmware_version_from_image(self)
1237
        try:
1238
            if b'BFB' in version_tag:
1239
                idx = version_tag.index(b"BFB") + 3
1240
                version = int(version_tag[idx:idx + 3])
1241
                return version < 291
1242
            return False
1243
        except Exception:
1244
            pass
1245
        raise errors.RadioError("Unable to parse version string %s" %
1246
                                version_tag)
1247

    
1248
    def _my_upper_band(self):
1249
        band_tag = _upper_band_from_image(self)
1250
        return band_tag
1251

    
1252
    def _get_settings(self):
1253
        _settings = self._memobj.settings
1254
        _press = self._memobj.press
1255
        _aoffset = self._memobj.aoffset
1256
        _boffset = self._memobj.boffset
1257
        _vfoa = self._memobj.vfoa
1258
        _vfob = self._memobj.vfob
1259
        _gcode = self._memobj.groupcode
1260
        _msg = self._memobj.poweron_msg
1261
        basic = RadioSettingGroup("basic", "Basic Settings")
1262
        abblock = RadioSettingGroup("abblock", "A/B Channel")
1263
        fmmode = RadioSettingGroup("fmmode", "FM")
1264
        dtmf = RadioSettingGroup("dtmf", "DTMF")
1265

    
1266
        # group = RadioSettings(fmmode, dtmf)
1267
        group = RadioSettings(basic, abblock, fmmode, dtmf)
1268

    
1269
        rs = RadioSetting("squelch", "Squelch Level",
1270
                          RadioSettingValueList(
1271
                              SQUELCH, SQUELCH[_settings.squelch]))
1272
        basic.append(rs)
1273

    
1274
        rs = RadioSetting("ligcon", "Light Control",
1275
                          RadioSettingValueList(
1276
                              LIGHT_LIST, LIGHT_LIST[_settings.ligcon]))
1277
        basic.append(rs)
1278

    
1279
        rs = RadioSetting("voiceprompt", "Voice Prompt",
1280
                          RadioSettingValueList(
1281
                              VOICE_PRMPT_LIST, VOICE_PRMPT_LIST[
1282
                                  _settings.voiceprompt]))
1283
        basic.append(rs)
1284

    
1285
        rs = RadioSetting("keyautolock", "Auto Lock",
1286
                          RadioSettingValueList(
1287
                              AUTOLOCK_LIST, AUTOLOCK_LIST[
1288
                                  _settings.keyautolock]))
1289
        basic.append(rs)
1290

    
1291
        rs = RadioSetting("mdfa", "MDF-A",
1292
                          RadioSettingValueList(
1293
                              MDFA_LIST, MDFA_LIST[_settings.mdfa]))
1294
        basic.append(rs)
1295

    
1296
        rs = RadioSetting("mdfb", "MDF-B",
1297
                          RadioSettingValueList(
1298
                              MDFB_LIST, MDFB_LIST[_settings.mdfb]))
1299
        basic.append(rs)
1300

    
1301
        rs = RadioSetting("sync", "SYNC",
1302
                          RadioSettingValueList(
1303
                              SYNC_LIST, SYNC_LIST[_settings.sync]))
1304
        basic.append(rs)
1305

    
1306
        rs = RadioSetting("save", "Battery Save",
1307
                          RadioSettingValueList(
1308
                              BTV_SAVER_LIST, BTV_SAVER_LIST[_settings.save]))
1309
        basic.append(rs)
1310

    
1311
        rs = RadioSetting("dbrx", "Double Rx",
1312
                          RadioSettingValueList(
1313
                              DBRX_LIST, DBRX_LIST[_settings.dbrx]))
1314
        basic.append(rs)
1315

    
1316
        rs = RadioSetting("astep", "A Step",
1317
                          RadioSettingValueList(
1318
                              ASTEP_LIST, ASTEP_LIST[_settings.astep]))
1319
        basic.append(rs)
1320

    
1321
        rs = RadioSetting("bstep", "B Step",
1322
                          RadioSettingValueList(
1323
                              BSTEP_LIST, BSTEP_LIST[_settings.bstep]))
1324
        basic.append(rs)
1325

    
1326
        rs = RadioSetting("scanmode", "Scan Mode",
1327
                          RadioSettingValueList(
1328
                              SCAN_MODE_LIST, SCAN_MODE_LIST[
1329
                                  _settings.scanmode]))
1330
        basic.append(rs)
1331

    
1332
        rs = RadioSetting("pritx", "Priority TX",
1333
                          RadioSettingValueList(
1334
                              PRIO_LIST, PRIO_LIST[_settings.pritx]))
1335
        basic.append(rs)
1336

    
1337
        rs = RadioSetting("btnvoice", "Beep",
1338
                          RadioSettingValueBoolean(_settings.btnvoice))
1339
        basic.append(rs)
1340

    
1341
        rs = RadioSetting("rogerprompt", "Roger",
1342
                          RadioSettingValueBoolean(_settings.rogerprompt))
1343
        basic.append(rs)
1344

    
1345
        rs = RadioSetting("txled", "Disp Lcd(TX)",
1346

    
1347
                          RadioSettingValueBoolean(_settings.txled))
1348
        basic.append(rs)
1349

    
1350
        rs = RadioSetting("rxled", "Disp Lcd(RX)",
1351
                          RadioSettingValueBoolean(_settings.rxled))
1352
        basic.append(rs)
1353

    
1354
        rs = RadioSetting("onlychmode", "Only CH Mode",
1355
                          RadioSettingValueBoolean(_settings.onlychmode))
1356
        basic.append(rs)
1357
        rs = RadioSetting("ssidekey1", "SHORT_KEY_PF1",
1358
                          RadioSettingValueList(
1359
                              SHORT_KEY_LIST, SHORT_KEY_LIST[
1360
                                  _press.ssidekey1]))
1361
        basic.append(rs)
1362
        rs = RadioSetting("lsidekey3", "LONG_KEY_PF1",
1363
                          RadioSettingValueList(
1364
                              LONG_KEY_LIST,
1365
                              LONG_KEY_LIST[_press.lsidekey3]))
1366
        basic.append(rs)
1367
        if self.MODEL in H8_LIST:
1368
            rs = RadioSetting("stopkey1", "SHORT_KEY_TOP",
1369
                              RadioSettingValueList(SHORT_KEY_LIST,
1370
                                                    SHORT_KEY_LIST[0]))
1371
            basic.append(rs)
1372

    
1373
            rs = RadioSetting("ltopkey2", "LONG_KEY_TOP",
1374
                              RadioSettingValueList(
1375
                                  LONG_KEY_LIST,
1376
                                  LONG_KEY_LIST[_press.ltopkey2]))
1377
            basic.append(rs)
1378

    
1379
            rs = RadioSetting("ssidekey2", "SHORT_KEY_PF2",
1380
                              RadioSettingValueList(
1381
                                  SHORT_KEY_LIST,
1382
                                  SHORT_KEY_LIST[_press.ssidekey2]))
1383
            basic.append(rs)
1384

    
1385
            rs = RadioSetting("lsidekey4", "LONG_KEY_PF2",
1386
                              RadioSettingValueList(
1387
                                LONG_KEY_LIST,
1388
                                LONG_KEY_LIST[_press.lsidekey4]))
1389
            basic.append(rs)
1390

    
1391
        rs = RadioSetting("voxgain", "VOX Gain",
1392
                          RadioSettingValueList(
1393
                              VOX_GAIN, VOX_GAIN[_settings.voxgain]))
1394
        basic.append(rs)
1395

    
1396
        rs = RadioSetting("voxdelay", "VOX Delay",
1397
                          RadioSettingValueList(
1398
                              VOX_DELAY, VOX_DELAY[_settings.voxdelay]))
1399
        basic.append(rs)
1400

    
1401
        rs = RadioSetting("ponmsg", "Power-On Message",
1402
                          RadioSettingValueList(
1403
                              PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
1404
        basic.append(rs)
1405

    
1406
        # mic gain
1407
        if self.MODEL not in H8_LIST:
1408
            _mic = self._memobj.mic
1409
            rs = RadioSetting("micgain", "MIC GAIN",
1410
                              RadioSettingValueList(
1411
                                  MIC_GAIN_LIST, current_index=_mic.micgain))
1412
            basic.append(rs)
1413

    
1414
        if self.MODEL not in H8_LIST:
1415
            rs = RadioSetting("kill", "Kill",
1416
                              RadioSettingValueBoolean(_settings.kill))
1417
            basic.append(rs)
1418
            rs = RadioSetting("stun", "Stun",
1419
                              RadioSettingValueBoolean(_settings.stun))
1420
            basic.append(rs)
1421

    
1422
        def _filter(name):
1423
            filtered = ""
1424
            for char in str(name):
1425
                if char in chirp_common.CHARSET_ASCII:
1426
                    filtered += char
1427
                else:
1428
                    filtered += " "
1429
            return filtered
1430

    
1431
        rs = RadioSetting("poweron_msg.msg1", "Power-On Message 1",
1432
                          RadioSettingValueString(0, 16, _filter(_msg.msg1)))
1433
        basic.append(rs)
1434
        rs = RadioSetting("poweron_msg.msg2", "Power-On Message 2",
1435
                          RadioSettingValueString(0, 16, _filter(_msg.msg2)))
1436
        basic.append(rs)
1437
        rs = RadioSetting("poweron_msg.msg3", "Power-On Message 3",
1438
                          RadioSettingValueString(0, 16, _filter(_msg.msg3)))
1439
        basic.append(rs)
1440

    
1441
        # A channel
1442
        a_freq = int(_vfoa.rxfreqa)
1443
        freqa = "%i.%05i" % (a_freq / 100000, a_freq % 100000)
1444
        if freqa == "0.00000":
1445
            val1a = RadioSettingValueString(0, 7, '0.00000')
1446
        else:
1447
            val1a = RadioSettingValueFloat(
1448
                136, 520, float(freqa), 0.00001, 5)
1449
        rs = RadioSetting("rxfreqa", "A Channel - Frequency", val1a)
1450
        abblock.append(rs)
1451

    
1452
        # Offset
1453
        # If the offset is 12.345
1454
        # Then the data obtained is [0x45, 0x23, 0x01, 0x00]
1455
        a_set_val = _aoffset.ofseta
1456
        a_set_list = len(_aoffset.ofseta) - 1
1457
        real_val = ''
1458
        for i in range(a_set_list, -1, -1):
1459
            real_val += str(a_set_val[i])[2:]
1460
        if real_val == "FFFFFFFF":
1461
            rs = RadioSetting("ofseta", "A Offset Frequency",
1462
                              RadioSettingValueString(0, 7, ""))
1463

    
1464
        else:
1465
            real_val = int(real_val)
1466
            real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
1467
            rs = RadioSetting("ofseta", "A Offset Frequency",
1468
                              RadioSettingValueFloat(
1469
                                  0.00000, 59.99750, real_val, 0.00001, 5))
1470
        abblock.append(rs)
1471

    
1472
        rs = RadioSetting("offset", "A Offset",
1473
                          RadioSettingValueList(
1474
                              A_OFFSET, A_OFFSET[_vfoa.offset]))
1475
        abblock.append(rs)
1476

    
1477
        rs = RadioSetting("lowpower", "A TX Power",
1478
                          RadioSettingValueList(
1479
                              A_TX_POWER, A_TX_POWER[_vfoa.lowpower]))
1480
        abblock.append(rs)
1481

    
1482
        rs = RadioSetting("wide", "A Band",
1483
                          RadioSettingValueList(
1484
                              A_BAND, A_BAND[_vfoa.wide]))
1485
        abblock.append(rs)
1486

    
1487
        rs = RadioSetting("bcl", "A Busy Lock",
1488
                          RadioSettingValueList(
1489
                              A_BUSYLOCK, A_BUSYLOCK[_vfoa.bcl]))
1490
        abblock.append(rs)
1491

    
1492
        rs = RadioSetting("specialqta", "A Special QT/DQT",
1493
                          RadioSettingValueList(
1494
                              A_SPEC_QTDQT, A_SPEC_QTDQT[_vfoa.specialqta]))
1495
        abblock.append(rs)
1496

    
1497
        rs = RadioSetting("aworkmode", "A Work Mode",
1498
                          RadioSettingValueList(
1499
                              A_WORKMODE, A_WORKMODE[_settings.aworkmode]))
1500
        abblock.append(rs)
1501

    
1502
        # B channel
1503
        b_freq = int(str(int(_vfob.rxfreqb)).ljust(8, '0'))
1504
        freqb = "%i.%05i" % (b_freq / 100000, b_freq % 100000)
1505
        if freqb == "0.00000":
1506
            val1a = RadioSettingValueString(0, 7, '0.00000')
1507
        else:
1508
            val1a = RadioSettingValueFloat(
1509
                136, 520, float(freqb), 0.00001, 5)
1510
        rs = RadioSetting("rxfreqb", "B Channel - Frequency", val1a)
1511
        abblock.append(rs)
1512

    
1513
        # Offset frequency
1514
        # If the offset is 12.345
1515
        # Then the data obtained is [0x45, 0x23, 0x01, 0x00]
1516
        # Need to use the following anonymous function to process data
1517
        b_set_val = _boffset.ofsetb
1518
        b_set_list = len(_boffset.ofsetb) - 1
1519
        real_val = ''
1520
        for i in range(b_set_list, -1, -1):
1521
            real_val += str(b_set_val[i])[2:]
1522
        if real_val == "FFFFFFFF":
1523
            rs = RadioSetting("ofsetb", "B Offset Frequency",
1524
                              RadioSettingValueString(0, 7, " "))
1525
        else:
1526
            real_val = int(real_val)
1527
            real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
1528
            rs = RadioSetting("ofsetb", "B Offset Frequency",
1529
                              RadioSettingValueFloat(
1530
                                  0.00000, 59.99750, real_val, 0.00001, 5))
1531
        abblock.append(rs)
1532

    
1533
        rs = RadioSetting("offsetb", "B Offset",
1534
                          RadioSettingValueList(
1535
                              B_OFFSET, B_OFFSET[_vfob.offsetb]))
1536
        abblock.append(rs)
1537

    
1538
        rs = RadioSetting("lowpowerb", "B TX Power",
1539
                          RadioSettingValueList(
1540
                              B_TX_POWER, B_TX_POWER[_vfob.lowpowerb]))
1541
        abblock.append(rs)
1542

    
1543
        rs = RadioSetting("wideb", "B Band",
1544
                          RadioSettingValueList(
1545
                              B_BAND, B_BAND[_vfob.wideb]))
1546
        abblock.append(rs)
1547

    
1548
        rs = RadioSetting("bclb", "B Busy Lock",
1549
                          RadioSettingValueList(
1550
                              B_BUSYLOCK, B_BUSYLOCK[_vfob.bclb]))
1551
        abblock.append(rs)
1552

    
1553
        rs = RadioSetting("specialqtb", "B Special QT/DQT",
1554
                          RadioSettingValueList(
1555
                              B_SPEC_QTDQT, B_SPEC_QTDQT[_vfob.specialqtb]))
1556
        abblock.append(rs)
1557

    
1558
        rs = RadioSetting("bworkmode", "B Work Mode",
1559
                          RadioSettingValueList(
1560
                              B_WORKMODE, B_WORKMODE[_settings.bworkmode]))
1561
        abblock.append(rs)
1562

    
1563
        rs = RadioSetting("fmworkmode", "Work Mode",
1564
                          RadioSettingValueList(
1565
                              FM_WORKMODE, FM_WORKMODE[_settings.fmworkmode]))
1566
        fmmode.append(rs)
1567

    
1568
        rs = RadioSetting("fmroad", "Channel",
1569
                          RadioSettingValueList(
1570
                              FM_CHANNEL, FM_CHANNEL[_settings.fmroad]))
1571
        fmmode.append(rs)
1572

    
1573
        rs = RadioSetting("fmrec", "Forbid Receive",
1574
                          RadioSettingValueBoolean(_settings.fmrec))
1575
        fmmode.append(rs)
1576

    
1577
        # FM
1578
        numeric = '0123456789.'
1579
        for i in range(25):
1580
            if self._memobj.fmusedflags[i]:
1581
                _fm = self._get_fm(i).fmblock
1582
                try:
1583
                    if not (760 < int(_fm) < 1080):
1584
                        raise ValueError()
1585
                    val = '%.1f' % (int(_fm) / 10)
1586
                except ValueError:
1587
                    LOG.warning('FM channel index %i is invalid', i)
1588
                    val = ''
1589
            else:
1590
                val = ''
1591
            rs = RadioSetting('block%02i' % i, "Channel %i" % (i + 1),
1592
                              RadioSettingValueString(0, 5,
1593
                                                      val,
1594
                                                      False, charset=numeric))
1595
            fmmode.append(rs)
1596

    
1597
        try:
1598
            _fmv = int(self._memobj.fmvfo) / 10
1599
        except ValueError:
1600
            LOG.warning('FM VFO is invalid')
1601
            _fmv = 0
1602

    
1603
        rs = RadioSetting(
1604
            "fmvfo", "VFO", RadioSettingValueFloat(
1605
                76.0, 108.0, _fmv, 0.1, 1))
1606
        fmmode.append(rs)
1607

    
1608
        # DTMF
1609
        gcode_val = str(_gcode.gcode)[2:]
1610
        if gcode_val == "FF":
1611
            gcode_val = "Off"
1612
        elif gcode_val == "0F":
1613
            gcode_val = "#"
1614
        elif gcode_val == "0E":
1615
            gcode_val = "*"
1616
        elif gcode_val == '00':
1617
            gcode_val = ""
1618
        else:
1619
            gcode_val = gcode_val[1]
1620
        rs = RadioSetting("gcode", "Group Code",
1621
                          RadioSettingValueList(GROUPCODE,
1622
                                                gcode_val))
1623
        dtmf.append(rs)
1624

    
1625
        icode_list = self._memobj.icode.idcode
1626
        used_icode = ''
1627
        for i in icode_list:
1628
            if i == 0xFF:
1629
                continue
1630
            used_icode += str(i)[3]
1631
        dtmfcharsani = "0123456789ABCD "
1632
        i_val = RadioSettingValueString(0, 3, used_icode)
1633
        rs = RadioSetting("icode", "ID Code", i_val)
1634
        i_val.set_charset(dtmfcharsani)
1635
        dtmf.append(rs)
1636

    
1637
        gcode_list_1 = self._memobj.group1.group1
1638
        used_group1 = ''
1639
        for i in gcode_list_1:
1640
            if i == 0xFF:
1641
                continue
1642
            used_group1 += str(i)[3]
1643
        group1_val = RadioSettingValueString(0, 7, used_group1)
1644
        rs = RadioSetting("group1", "1", group1_val)
1645
        group1_val.set_charset(dtmfcharsani)
1646
        dtmf.append(rs)
1647

    
1648
        gcode_list_2 = self._memobj.group2.group2
1649
        used_group2 = ''
1650
        for i in gcode_list_2:
1651
            if i == 0xFF:
1652
                continue
1653
            used_group2 += str(i)[3]
1654
        group2_val = RadioSettingValueString(0, 7, used_group2)
1655
        rs = RadioSetting("group2", "2", group2_val)
1656
        group2_val.set_charset(dtmfcharsani)
1657
        dtmf.append(rs)
1658

    
1659
        gcode_list_3 = self._memobj.group3.group3
1660
        used_group3 = ''
1661
        for i in gcode_list_3:
1662
            if i == 0xFF:
1663
                continue
1664
            used_group3 += str(i)[3]
1665
        group3_val = RadioSettingValueString(0, 7, used_group3)
1666
        rs = RadioSetting("group3", "3", group3_val)
1667
        group3_val.set_charset(dtmfcharsani)
1668
        dtmf.append(rs)
1669

    
1670
        gcode_list_4 = self._memobj.group4.group4
1671
        used_group4 = ''
1672
        for i in gcode_list_4:
1673
            if i == 0xFF:
1674
                continue
1675
            used_group4 += str(i)[3]
1676
        group4_val = RadioSettingValueString(0, 7, used_group4)
1677
        rs = RadioSetting("group4", "4", group4_val)
1678
        group4_val.set_charset(dtmfcharsani)
1679
        dtmf.append(rs)
1680

    
1681
        gcode_list_5 = self._memobj.group5.group5
1682
        used_group5 = ''
1683
        for i in gcode_list_5:
1684
            if i == 0xFF:
1685
                continue
1686
            used_group5 += str(i)[3]
1687
        group5_val = RadioSettingValueString(0, 7, used_group5)
1688
        rs = RadioSetting("group5", "5", group5_val)
1689
        group5_val.set_charset(dtmfcharsani)
1690
        dtmf.append(rs)
1691

    
1692
        gcode_list_6 = self._memobj.group6.group6
1693
        used_group6 = ''
1694
        for i in gcode_list_6:
1695
            if i == 0xFF:
1696
                continue
1697
            used_group6 += str(i)[3]
1698
        group6_val = RadioSettingValueString(0, 7, used_group6)
1699
        rs = RadioSetting("group6", "6", group6_val)
1700
        group6_val.set_charset(dtmfcharsani)
1701
        dtmf.append(rs)
1702

    
1703
        gcode_list_7 = self._memobj.group7.group7
1704
        used_group7 = ''
1705
        for i in gcode_list_7:
1706
            if i == 0xFF:
1707
                continue
1708
            used_group7 += str(i)[3]
1709
        group7_val = RadioSettingValueString(0, 7, used_group7)
1710
        rs = RadioSetting("group7", "7", group7_val)
1711
        group7_val.set_charset(dtmfcharsani)
1712
        dtmf.append(rs)
1713

    
1714
        gcode_list_8 = self._memobj.group8.group8
1715
        used_group8 = ''
1716
        for i in gcode_list_8:
1717
            if i == 0xFF:
1718
                continue
1719
            used_group8 += str(i)[3]
1720
        group8_val = RadioSettingValueString(0, 7, used_group8)
1721
        rs = RadioSetting("group8", "8", group7_val)
1722
        group8_val.set_charset(dtmfcharsani)
1723
        dtmf.append(rs)
1724

    
1725
        scode_list = self._memobj.startcode.scode
1726
        used_scode = ''
1727
        for i in scode_list:
1728
            if i == 0xFF:
1729
                continue
1730
            used_scode += str(i)[3]
1731
        scode_val = RadioSettingValueString(0, 7, used_scode)
1732
        rs = RadioSetting("scode", "PTT ID Starting(BOT)", scode_val)
1733
        scode_val.set_charset(dtmfcharsani)
1734
        dtmf.append(rs)
1735

    
1736
        ecode_list = self._memobj.endcode.ecode
1737
        used_ecode = ''
1738
        for i in ecode_list:
1739
            if i == 0xFF:
1740
                continue
1741
            used_ecode += str(i)[3]
1742
        ecode_val = RadioSettingValueString(0, 7, used_ecode)
1743
        rs = RadioSetting("ecode", "PTT ID Ending(BOT)", ecode_val)
1744
        dtmf.append(rs)
1745
        # H3
1746
        if self.MODEL not in H8_LIST:
1747
            # stuncode
1748
            ecode_list = self._memobj.skcode.stuncode
1749
            used_ecode = ''
1750
            for i in ecode_list:
1751
                if i == 0xFF:
1752
                    continue
1753
                used_ecode += str(i)[3]
1754
            ecode_val = RadioSettingValueString(0, 16, used_ecode)
1755
            rs = RadioSetting("stuncode", "Stun Code", ecode_val)
1756
            dtmf.append(rs)
1757
            # killcode
1758
            ecode_list = self._memobj.skcode.killcode
1759
            used_ecode = ''
1760
            for i in ecode_list:
1761
                if i == 0xFF:
1762
                    continue
1763
                used_ecode += str(i)[3]
1764
            ecode_val = RadioSettingValueString(0, 16, used_ecode)
1765
            rs = RadioSetting("killcode", "Kill Code", ecode_val)
1766
            dtmf.append(rs)
1767

    
1768
        return group
1769

    
1770
    def get_settings(self):
1771
        try:
1772
            return self._get_settings()
1773
        except Exception:
1774
            raise InvalidValueError("Setting Failed!")
1775

    
1776
    def set_settings(self, settings):
1777

    
1778
        def fm_validate(value):
1779
            if 760 > value or value > 1080:
1780
                msg = ("FM Channel must be between 76.0-108.0")
1781
                raise InvalidValueError(msg)
1782

    
1783
        _settings = self._memobj.settings
1784
        _press = self._memobj.press
1785
        _aoffset = self._memobj.aoffset
1786
        _boffset = self._memobj.boffset
1787
        _vfoa = self._memobj.vfoa
1788
        _vfob = self._memobj.vfob
1789
        _fmmode = self._memobj.fmmode
1790

    
1791
        for element in settings:
1792
            if not isinstance(element, RadioSetting):
1793
                if element.get_name() == "fm_preset":
1794
                    self._set_fm_preset(element)
1795
                else:
1796
                    self.set_settings(element)
1797
                    continue
1798
            else:
1799
                try:
1800
                    name = element.get_name()
1801
                    if "." in name:
1802
                        bits = name.split(".")
1803
                        obj = self._memobj
1804
                        for bit in bits[:-1]:
1805
                            if "/" in bit:
1806
                                bit, index = bit.split("/", 1)
1807
                                index = int(index)
1808
                                obj = getattr(obj, bit)[index]
1809
                            else:
1810
                                obj = getattr(obj, bit)
1811
                        setting = bits[-1]
1812
                    elif name in PRESS_NAME:
1813
                        obj = _press
1814
                        setting = element.get_name()
1815

    
1816
                    elif name in VFOA_NAME:
1817
                        obj = _vfoa
1818
                        setting = element.get_name()
1819
                    elif name == "ofseta":
1820
                        obj = _aoffset
1821
                        setting = element.get_name()
1822
                    elif name in VFOB_NAME:
1823
                        obj = _vfob
1824
                        setting = element.get_name()
1825
                    elif name == "ofsetb":
1826
                        obj = _boffset
1827
                        setting = element.get_name()
1828
                    elif "block" in name:
1829
                        obj = _fmmode
1830
                        setting = element.get_name()
1831
                    elif "fmvfo" in name:
1832
                        obj = self._memobj.fmvfo
1833
                        setting = element.get_name()
1834
                    elif "gcode" in name:
1835
                        obj = self._memobj.groupcode.gcode
1836
                        setting = element.get_name()
1837
                    elif "idcode" in name:
1838
                        obj = self._memobj.icode.idcode
1839
                        setting = element.get_name()
1840
                    elif "scode" in name:
1841
                        obj = self._memobj.startcode.scode
1842
                        setting = element.get_name()
1843
                    elif "ecode" in name:
1844
                        obj = self._memobj.endcode.ecode
1845
                        setting = element.get_name()
1846
                    elif "group1" in name:
1847
                        obj = self._memobj.group1.group1
1848
                        setting = element.get_name()
1849
                    elif "group2" in name:
1850
                        obj = self._memobj.group2.group2
1851
                        setting = element.get_name()
1852
                    elif "group3" in name:
1853
                        obj = self._memobj.group3.group3
1854
                        setting = element.get_name()
1855
                    elif "group4" in name:
1856
                        obj = self._memobj.group4.group4
1857
                        setting = element.get_name()
1858
                    elif "group5" in name:
1859
                        obj = self._memobj.group5.group5
1860
                        setting = element.get_name()
1861
                    elif "group6" in name:
1862
                        obj = self._memobj.group6.group6
1863
                        setting = element.get_name()
1864
                    elif "group7" in name:
1865
                        obj = self._memobj.group7.group7
1866
                        setting = element.get_name()
1867
                    elif "group8" in name:
1868
                        obj = self._memobj.group8.group8
1869
                        setting = element.get_name()
1870
                    elif "micgain" in name:
1871
                        obj = self._memobj.mic.micgain
1872
                        setting = element.get_name()
1873
                    elif "killcode" in name:
1874
                        obj = self._memobj.skcode.killcode
1875
                        setting = element.get_name()
1876
                    elif "stuncode" in name:
1877
                        obj = self._memobj.skcode.stuncode
1878
                        setting = element.get_name()
1879
                    else:
1880
                        obj = _settings
1881
                        setting = element.get_name()
1882
                    if element.has_apply_callback():
1883
                        LOG.debug("Using apply callback")
1884
                        element.run_apply_callback()
1885

    
1886
                    # Channel A
1887
                    elif setting == "rxfreqa" and element.value.get_mutable():
1888
                        val = int(str(element.value).replace(
1889
                            '.', '').ljust(8, '0'))
1890
                        if (val >= 13600000 and val <= 17400000) or \
1891
                                (val >= 40000000 and val <= 52000000):
1892
                            setattr(obj, setting, val)
1893
                        else:
1894
                            msg = (
1895
                                "Frequency must be between "
1896
                                "136.00000-174.00000 or 400.00000-520.00000")
1897
                            raise InvalidValueError(msg)
1898

    
1899
                    elif setting == "ofseta" and element.value.get_mutable():
1900
                        if '.' in str(element.value):
1901
                            val = str(element.value).replace(' ', '')
1902
                            if len(
1903
                                val[val.index(".") + 1:]
1904
                                ) >= 1 and int(val[val.index(".") + 1:]
1905
                                               ) != 0:
1906
                                val = '00' + val.replace('.', '')
1907
                            else:
1908
                                val = '0' + val.replace('.', '')
1909
                            val = val.ljust(8, '0')
1910
                            lenth_val = 0
1911
                            list_val = []
1912
                        else:
1913
                            val = '0' + str(element.value).replace(' ', '')
1914
                            val = val.ljust(8, '0')
1915
                            lenth_val = 0
1916
                            list_val = []
1917
                        if (int(val) >= 0 and int(val) <= 5999750):
1918
                            if int(val) == 0:
1919
                                _aoffset.ofseta = [0xFF, 0xFF, 0xFF, 0xFF]
1920
                            else:
1921
                                while lenth_val < (len(val)):
1922
                                    list_val.insert(
1923
                                        0, val[lenth_val:lenth_val + 2])
1924
                                    lenth_val += 2
1925
                                for i in range(len(list_val)):
1926
                                    list_val[i] = int(list_val[i], 16)
1927
                                _aoffset.ofseta = list_val
1928
                        else:
1929
                            msg = ("Offset must be between 0.00000-59.99750")
1930
                            raise InvalidValueError(msg)
1931

    
1932
                    # B channel
1933
                    elif setting == "rxfreqb" and element.value.get_mutable():
1934
                        val = 0
1935
                        val = int(str(element.value).replace(
1936
                            '.', '').ljust(8, '0'))
1937
                        if (val >= 13600000 and val <= 17400000) or \
1938
                                (val >= 40000000 and val <= 52000000):
1939
                            setattr(obj, setting, val)
1940
                        else:
1941
                            msg = (
1942
                                "Frequency must be between "
1943
                                "136.00000-174.00000 or 400.00000-520.00000")
1944
                            raise InvalidValueError(msg)
1945
                        # setattr(obj, setting, val)
1946

    
1947
                    elif setting == "ofsetb" and element.value.get_mutable():
1948
                        if '.' in str(element.value):
1949
                            val = str(element.value).replace(' ', '')
1950
                            if len(val[val.index(".") + 1:]
1951
                                   ) >= 1 and int(val[val.index(".") + 1:]
1952
                                                  ) != 0:
1953
                                val = '00' + \
1954
                                    str(element.value).replace('.', '')
1955
                            else:
1956
                                val = '0' + str(element.value).replace('.', '')
1957
                            val = val.ljust(8, '0')
1958
                            lenth_val = 0
1959
                            list_val = []
1960
                        else:
1961
                            val = '0' + str(element.value).replace(' ', '')
1962
                            val = val.ljust(8, '0')
1963
                            lenth_val = 0
1964
                            list_val = []
1965
                        if (int(val) >= 0 and int(val) <= 5999750):
1966
                            if int(val) == 0:
1967
                                _boffset.ofsetb = [0xFF, 0xFF, 0xFF, 0xFF]
1968
                            else:
1969
                                while lenth_val < (len(val)):
1970
                                    list_val.insert(
1971
                                        0, val[lenth_val:lenth_val + 2])
1972
                                    lenth_val += 2
1973
                                for i in range(len(list_val)):
1974
                                    list_val[i] = int(list_val[i], 16)
1975
                                _boffset.ofsetb = list_val
1976
                        else:
1977
                            msg = ("Offset must be between 0.00000-59.99750")
1978
                            raise InvalidValueError(msg)
1979

    
1980
                    # FM
1981
                    elif "block" in name:
1982
                        num = int(name[-2:], 10)
1983
                        val = str(element.value)
1984
                        if val.strip():
1985
                            try:
1986
                                val = int(float(val) * 10)
1987
                            except ValueError:
1988
                                raise InvalidValueError(
1989
                                    'Value must be between 76.0-108.0')
1990
                            fm_validate(val)
1991
                        else:
1992
                            val = 0
1993
                        self._memobj.fmmode[num].fmblock = val
1994
                        self._memobj.fmusedflags[num] = bool(val)
1995

    
1996
                    elif setting == 'fmvfo' and element.value.get_mutable():
1997
                        self._memobj.fmvfo = int(element.value * 10)
1998

    
1999
                    elif setting == 'gcode' and element.value.get_mutable():
2000
                        val = str(element.value)
2001
                        if val == 'Off':
2002
                            gcode_used = 0xFF
2003
                        elif val == 'A':
2004
                            gcode_used = 0x0A
2005
                        elif val == 'B':
2006
                            gcode_used = 0x0B
2007
                        elif val == 'C':
2008
                            gcode_used = 0x0C
2009
                        elif val == 'D':
2010
                            gcode_used = 0x0D
2011
                        elif val == '#':
2012
                            gcode_used = 0x0F
2013
                        elif val == '*':
2014
                            gcode_used = 0x0E
2015
                        elif val == '':
2016
                            gcode_used = 0x00
2017
                        self._memobj.groupcode.gcode = gcode_used
2018

    
2019
                    elif setting == 'icode' and element.value.get_mutable():
2020
                        val = str(element.value)
2021
                        list_val = []
2022
                        lenth_val = 0
2023
                        while lenth_val < (len(val)):
2024
                            if val[lenth_val] != ' ':
2025
                                list_val.append(int(val[lenth_val], 16))
2026
                                lenth_val += 1
2027
                            else:
2028
                                list_val.append(0xFF)
2029
                                lenth_val += 1
2030
                        self._memobj.icode.idcode = list_val
2031

    
2032
                    elif setting == 'scode' and element.value.get_mutable():
2033
                        val = str(element.value)
2034
                        list_val = []
2035
                        lenth_val = 0
2036
                        while lenth_val < (len(val)):
2037
                            if val[lenth_val] != ' ':
2038
                                list_val.append(int(val[lenth_val], 16))
2039
                                lenth_val += 1
2040
                            else:
2041
                                list_val.append(0xFF)
2042
                                lenth_val += 1
2043
                        self._memobj.startcode.scode = list_val
2044

    
2045
                    elif setting == 'ecode' and element.value.get_mutable():
2046
                        val = str(element.value)
2047
                        list_val = []
2048
                        lenth_val = 0
2049
                        while lenth_val < (len(val)):
2050
                            if val[lenth_val] != ' ':
2051
                                list_val.append(int(val[lenth_val], 16))
2052
                                lenth_val += 1
2053
                            else:
2054
                                list_val.append(0xFF)
2055
                                lenth_val += 1
2056
                        self._memobj.endcode.ecode = list_val
2057

    
2058
                    elif setting == 'group1' and element.value.get_mutable():
2059
                        val = str(element.value)
2060
                        list_val = []
2061
                        lenth_val = 0
2062
                        while lenth_val < (len(val)):
2063
                            if val[lenth_val] != ' ':
2064
                                list_val.append(int(val[lenth_val], 16))
2065
                                lenth_val += 1
2066
                            else:
2067
                                list_val.append(0xFF)
2068
                                lenth_val += 1
2069
                        self._memobj.group1.group1 = list_val
2070

    
2071
                    elif setting == 'group2' and element.value.get_mutable():
2072
                        val = str(element.value)
2073
                        list_val = []
2074
                        lenth_val = 0
2075
                        while lenth_val < (len(val)):
2076
                            if val[lenth_val] != ' ':
2077
                                list_val.append(int(val[lenth_val], 16))
2078
                                lenth_val += 1
2079
                            else:
2080
                                list_val.append(0xFF)
2081
                                lenth_val += 1
2082
                        self._memobj.group2.group2 = list_val
2083

    
2084
                    elif setting == 'group3' and element.value.get_mutable():
2085
                        val = str(element.value)
2086
                        list_val = []
2087
                        lenth_val = 0
2088
                        while lenth_val < (len(val)):
2089
                            if val[lenth_val] != ' ':
2090
                                list_val.append(int(val[lenth_val], 16))
2091
                                lenth_val += 1
2092
                            else:
2093
                                list_val.append(0xFF)
2094
                                lenth_val += 1
2095
                        self._memobj.group3.group3 = list_val
2096

    
2097
                    elif setting == 'group4' and element.value.get_mutable():
2098
                        val = str(element.value)
2099
                        list_val = []
2100
                        lenth_val = 0
2101
                        while lenth_val < (len(val)):
2102
                            if val[lenth_val] != ' ':
2103
                                list_val.append(int(val[lenth_val], 16))
2104
                                lenth_val += 1
2105
                            else:
2106
                                list_val.append(0xFF)
2107
                                lenth_val += 1
2108
                        self._memobj.group4.group4 = list_val
2109

    
2110
                    elif setting == 'group5' and element.value.get_mutable():
2111
                        val = str(element.value)
2112
                        list_val = []
2113
                        lenth_val = 0
2114
                        while lenth_val < (len(val)):
2115
                            if val[lenth_val] != ' ':
2116
                                list_val.append(int(val[lenth_val], 16))
2117
                                lenth_val += 1
2118
                            else:
2119
                                list_val.append(0xFF)
2120
                                lenth_val += 1
2121
                        self._memobj.group5.group5 = list_val
2122

    
2123
                    elif setting == 'group6' and element.value.get_mutable():
2124
                        val = str(element.value)
2125
                        list_val = []
2126
                        lenth_val = 0
2127
                        while lenth_val < (len(val)):
2128
                            if val[lenth_val] != ' ':
2129
                                list_val.append(int(val[lenth_val], 16))
2130
                                lenth_val += 1
2131
                            else:
2132
                                list_val.append(0xFF)
2133
                                lenth_val += 1
2134
                        self._memobj.group6.group6 = list_val
2135

    
2136
                    elif setting == 'group7' and element.value.get_mutable():
2137
                        val = str(element.value)
2138
                        list_val = []
2139
                        lenth_val = 0
2140
                        while lenth_val < (len(val)):
2141
                            if val[lenth_val] != ' ':
2142
                                list_val.append(int(val[lenth_val], 16))
2143
                                lenth_val += 1
2144
                            else:
2145
                                list_val.append(0xFF)
2146
                                lenth_val += 1
2147
                        self._memobj.group7.group7 = list_val
2148

    
2149
                    elif setting == 'group8' and element.value.get_mutable():
2150
                        val = str(element.value)
2151
                        list_val = []
2152
                        lenth_val = 0
2153
                        while lenth_val < (len(val)):
2154
                            if val[lenth_val] != ' ':
2155
                                list_val.append(int(val[lenth_val], 16))
2156
                                lenth_val += 1
2157
                            else:
2158
                                list_val.append(0xFF)
2159
                                lenth_val += 1
2160
                        self._memobj.group8.group8 = list_val
2161
                    elif setting == 'micgain':
2162
                        self._memobj.mic.micgain = (
2163
                            str(element.value))
2164
                    elif setting == 'stuncode' and element.value.get_mutable():
2165
                        val = str(element.value)
2166
                        list_val = []
2167
                        lenth_val = 0
2168
                        while lenth_val < (len(val)):
2169
                            if val[lenth_val] != ' ':
2170
                                list_val.append(int(val[lenth_val], 16))
2171
                                lenth_val += 1
2172
                            else:
2173
                                list_val.append(0xFF)
2174
                                lenth_val += 1
2175
                        self._memobj.skcode.stuncode = list_val
2176
                    elif setting == 'killcode' and element.value.get_mutable():
2177
                        val = str(element.value)
2178
                        list_val = []
2179
                        lenth_val = 0
2180
                        while lenth_val < (len(val)):
2181
                            if val[lenth_val] != ' ':
2182
                                list_val.append(int(val[lenth_val], 16))
2183
                                lenth_val += 1
2184
                            else:
2185
                                list_val.append(0xFF)
2186
                                lenth_val += 1
2187
                        self._memobj.skcode.killcode = list_val
2188
                    elif element.value.get_mutable():
2189
                        setattr(obj, setting, element.value)
2190
                except Exception:
2191
                    LOG.debug(element.get_name())
2192
                    raise
2193

    
2194
    def _set_fm_preset(self, settings):
2195
        for element in settings:
2196
            try:
2197
                val = element.value
2198
                if self._memobj.fm_presets <= 108.0 * 10 - 650:
2199
                    value = int(val.get_value() * 10 - 650)
2200
                else:
2201
                    value = int(val.get_value() * 10)
2202
                LOG.debug("Setting fm_presets = %s" % (value))
2203
                self._memobj.fm_presets = value
2204
            except Exception:
2205
                LOG.debug(element.get_name())
2206
                raise
2207

    
2208

    
2209
@directory.register
2210
class TDH8_HAM(TDH8):
2211
    VENDOR = "TIDRADIO"
2212
    MODEL = "TD-H8-HAM"
2213
    ident_mode = b'P31185\xff\xff'
2214
    _ham = True
2215
    _txbands = [(144000000, 149000000), (420000000, 451000000)]
2216

    
2217

    
2218
@directory.register
2219
class TDH8_GMRS(TDH8):
2220
    VENDOR = "TIDRADIO"
2221
    MODEL = "TD-H8-GMRS"
2222
    ident_mode = b'P31184\xff\xff'
2223
    _gmrs = True
2224
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
2225
    _tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
2226
                 chirp_common.PowerLevel("High", watts=8.00)]
2227

    
2228
    def validate_memory(self, mem):
2229
        msgs = super().validate_memory(mem)
2230
        if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
2231
            msgs.append(chirp_common.ValidationError(
2232
                "The frequency in channels 31-54 must be between"
2233
                "462.55000-462.72500 in 0.025 increments."))
2234
        return msgs
2235

    
2236

    
2237
@directory.register
2238
class UV68(TDH8):
2239
    VENDOR = "TID"
2240
    MODEL = "TD-UV68"
2241

    
2242

    
2243
@directory.register
2244
class TDH3(TDH8):
2245
    VENDOR = "TIDRADIO"
2246
    MODEL = "TD-H3"
2247
    _memsize = 0x1fef
2248
    _ranges_main = [(0x0000, 0x1fef)]
2249
    _idents = [TD_H3]
2250
    _txbands = [(136000000, 600000000)]
2251
    _rxbands = [(50000000, 107999000), (108000000, 136000000)]
2252
    _aux_block = True
2253
    _tri_power = True
2254
    _gmrs = False
2255
    _ham = False
2256
    _mem_params = (0x1F2F)
2257
    _tx_power = [chirp_common.PowerLevel("Low",  watts=1.00),
2258
                 chirp_common.PowerLevel("High",  watts=4.00)]
2259

    
2260
    def process_mmap(self):
2261
        self._memobj = bitwise.parse(MEM_FORMAT_H3, self._mmap)
2262

    
2263

    
2264
@directory.register
2265
class TDH3_HAM(TDH3):
2266
    VENDOR = "TIDRADIO"
2267
    MODEL = "TD-H3-HAM"
2268
    ident_mode = b'P31185\xff\xff'
2269
    _ham = True
2270
    _txbands = [(144000000, 149000000), (420000000, 451000000)]
2271

    
2272

    
2273
@directory.register
2274
class TDH3_GMRS(TDH3):
2275
    VENDOR = "TIDRADIO"
2276
    MODEL = "TD-H3-GMRS"
2277
    ident_mode = b'P31184\xff\xff'
2278
    _gmrs = True
2279
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
2280

    
2281
    def validate_memory(self, mem):
2282
        msgs = super().validate_memory(mem)
2283
        if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
2284
            msgs.append(chirp_common.ValidationError(
2285
                "The frequency in channels 31-54 must be between"
2286
                "462.55000-462.72500 in 0.025 increments."))
2287
        return msgs
(2-2/2)