Project

General

Profile

Bug #11263 » tdh8.py

24dd7fb6 - Dan Smith, 04/02/2024 02:43 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
            assert mem.power is not None
1017
        except (IndexError, AssertionError):
1018
            LOG.error("Radio reported invalid power level %s (in %s)" %
1019
                      (_mem.lowpower, self._tx_power))
1020
            mem.power = self._tx_power[0]
1021

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

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

    
1033
        # tmode
1034
        lin2 = int(_mem.rxtone)
1035
        rxtone = self._decode_tone(lin2)
1036

    
1037
        lin = int(_mem.txtone)
1038
        txtone = self._decode_tone(lin)
1039

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

    
1052
        chirp_common.split_tone_decode(mem, txtone, rxtone)
1053

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

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

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

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

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

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

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

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

    
1121
        return mem
1122

    
1123
    def _set_mem(self, number):
1124
        return self._memobj.memory[number]
1125

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

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

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

    
1152
        else:
1153
            x_list = (scan_data / 8) - 1
1154
            y_list = 8
1155

    
1156
        return ([x_list, y_list])
1157

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

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

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

    
1170
        _mem.fill_raw(b'\x00')
1171

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

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

    
1188
        _mem.rxfreq = mem.freq / 10
1189
        _namelength = self.get_features().valid_name_length
1190

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

    
1197
        txtone, rxtone = chirp_common.split_tone_encode(mem)
1198

    
1199
        self._encode_tone(_mem.txtone, *txtone)
1200
        self._encode_tone(_mem.rxtone, *rxtone)
1201

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

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

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

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

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

    
1246
    def _my_upper_band(self):
1247
        band_tag = _upper_band_from_image(self)
1248
        return band_tag
1249

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

    
1264
        # group = RadioSettings(fmmode, dtmf)
1265
        group = RadioSettings(basic, abblock, fmmode, dtmf)
1266

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1343
        rs = RadioSetting("txled", "Disp Lcd(TX)",
1344

    
1345
                          RadioSettingValueBoolean(_settings.txled))
1346
        basic.append(rs)
1347

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1600
        rs = RadioSetting(
1601
            "fmvfo", "VFO", RadioSettingValueFloat(
1602
                76.0, 108.0, _fmv, 0.1, 1))
1603
        fmmode.append(rs)
1604

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

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

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

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

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

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

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

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

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

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

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

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

    
1765
        return group
1766

    
1767
    def get_settings(self):
1768
        try:
1769
            return self._get_settings()
1770
        except Exception:
1771
            raise InvalidValueError("Setting Failed!")
1772

    
1773
    def set_settings(self, settings):
1774

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

    
1780
        _settings = self._memobj.settings
1781
        _press = self._memobj.press
1782
        _aoffset = self._memobj.aoffset
1783
        _boffset = self._memobj.boffset
1784
        _vfoa = self._memobj.vfoa
1785
        _vfob = self._memobj.vfob
1786
        _fmmode = self._memobj.fmmode
1787

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

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

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

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

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

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

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

    
1993
                    elif setting == 'fmvfo' and element.value.get_mutable():
1994
                        self._memobj.fmvfo = int(element.value * 10)
1995

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2205

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

    
2214

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

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

    
2234

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

    
2240

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

    
2258
    def process_mmap(self):
2259
        self._memobj = bitwise.parse(MEM_FORMAT_H3, self._mmap)
2260

    
2261

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

    
2270

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

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