Project

General

Profile

New Model #11091 » tdh8_plus_rt730.py

Jim Unroe, 05/03/2024 05: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 logging
18

    
19

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

    
29
LOG = logging.getLogger(__name__)
30

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

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

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

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

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

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

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

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

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

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

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

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

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

    
193

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
511
"""
512

    
513
MEM_FORMAT_RT730 = """
514
#seekto 0x0008;
515
struct {
516
  lbcd rxfreq[4];
517
  lbcd txfreq[4];
518
  lbcd rxtone[2];
519
  lbcd txtone[2];
520
  u8 unused1;
521
  u8 unused2:4,
522
     spec:1,
523
     bcl:1,
524
     unused3:2;
525
  u8 scramble:1,
526
     freqhop:1,
527
     lowpower:2,
528
     wide:1,
529
     unused4:3;
530
  u8 unused5;
531
} memory[200];
532

    
533
#seekto 0x0C98;
534
struct{
535
  u8 ssidekey1;
536
  u8 lsidekey1;
537
  u8 ssidekey2;
538
  u8 lsidekey2;
539
  u8 unused1:6,
540
     rogerprompt:2;
541
} press;
542

    
543
#seekto 0x0CA8;
544
struct {
545
  u8 txled:1,
546
     rxled:1,
547
     unused1:6;
548
  u8 scanmode:2,
549
     unused2:1,
550
     keyautolock:1,
551
     save:1,
552
     btnvoice:1,
553
     unused3:1,
554
     voiceprompt:1;
555
  u8 fmworkmode:1,
556
     ligcon:2,
557
     unused4:1,
558
     fmrec:1,
559
     mdfa:1,
560
     aworkmode:2;
561
  u8 unused5:5,
562
     dbrx:1,
563
     bworkmode:2;
564
  u8 unused6;
565
  u8 unused7;
566
  u8 fmroad;
567
  u8 unused8:1,
568
     tailclean:1,
569
     unused9:3,
570
     voxgain:3;
571
  u8 astep:4,
572
     bstep:4;
573
  u8 squelch;
574
  u8 tot;
575
  u8 unused10:6,
576
     lang:1,
577
     unused11:1;
578
  u8 unused12;
579
  u8 unused13;
580
  u8 voxdelay;
581
  u8 unused14:6,
582
     hoptype:2;
583
} settings;
584

    
585
//#seekto 0x0CB8;
586
struct {
587
    u8 ofseta[4];
588
} aoffset;
589

    
590
//#seekto 0x0CBC;
591
struct {
592
    u8 ofsetb[4];
593
} boffset;
594

    
595
#seekto 0x0CD8;
596
struct{
597
    lbcd fmblock[4];
598
}fmmode[25];
599

    
600
#seekto 0x0D48;
601
struct {
602
  char name[8];
603
} names[200];
604

    
605
#seekto 0x1398;
606
struct {
607
  char msg1[16];
608
  char msg2[16];
609
  char msg3[16];
610
  char msg4[16];
611
} poweron_msg;
612

    
613
#seekto 0x1A28;
614
lbit scanadd[200];
615

    
616
#seekto 0x1B08;
617
lbit usedflags[200];
618

    
619
#seekto 0x1B38;
620
lbcd fmvfo[4];
621

    
622
#seekto 0x1B58;
623
struct {
624
  lbcd rxfreqa[4];
625
  lbcd txfreq[4];
626
  u8 rxtone[2];
627
  u8 txtone[2];
628
  u8 unused1;
629
  u8 pttid:2,
630
     specialqta:1,
631
     unused2:1,
632
     unused3:1,
633
     bcl:1,
634
     unused4:1,
635
     unused5:1;
636
  u8 unused6:1,
637
     unused7:1,
638
     lowpower:2,
639
     wide:1,
640
     unused8:1,
641
     offset:2;
642
  u8 unused9;
643
} vfoa;
644

    
645
//#seekto 0x1B68;
646
struct {
647
  lbcd rxfreqb[4];
648
  lbcd txfreq[4];
649
  u8 rxtoneb[2];
650
  u8 txtone[2];
651
  u8 unused1;
652
  u8 pttid:2,
653
     specialqtb:1,
654
     unused2:1,
655
     unused3:1,
656
     bclb:1,
657
     unused4:1,
658
     unused5:1;
659
  u8 unused6:1,
660
     unused7:1,
661
     lowpowerb:2,
662
     wideb:1,
663
     unused8:1,
664
     offsetb:2;
665
  u8 unused9;
666
} vfob;
667

    
668
//#seekto 0x1B78;
669
lbit fmusedflags[32];
670

    
671
#seekto 0x1f28;
672
struct{
673
  u8 micgain;
674
} mic;
675

    
676
"""
677

    
678
# basic settings
679
SQUELCH = ['%s' % x for x in range(0, 10)]
680
LIGHT_LIST = ["CONT", "5s", "10s", "15s", "30s"]
681
LIGHT730_LIST = ["CONT", "10s", "20s", "30s"]
682
VOICE_PRMPT_LIST = ["OFF", "ON"]
683
AUTOLOCK_LIST = ["OFF", "ON"]
684
TIME_OUT_LIST = ["OFF", "60s", "120s", "180s"]
685
MDFA_LIST = ["Frequency", "Name"]
686
MDFB_LIST = ["Frequency", "Name"]
687
HOP_LIST = ["A", "B", "C", "D"]
688
SYNC_LIST = ["ON", "OFF"]
689
LANG_LIST = ["Chinese", "English"]
690
BTV_SAVER_LIST = ["OFF", "1:1", "1:2", "1:3", "1:4"]
691
DBRX_LIST = ["OFF", "ON"]
692
OFFON_LIST = ["OFF", "ON"]
693
ASTEP_LIST = ["2.50K", "5.00K", "6.25K",
694
              "10.00K", "12.00K", "25.00K", "50.00K"]
695
BSTEP_LIST = ["2.50K", "5.00K", "6.25K",
696
              "10.00K", "12.00K", "25.00K", "50.00K"]
697
SCAN_MODE_LIST = ["TO", "CO", "SE"]
698
PRIO_LIST = ["Edit", "Busy"]
699
SHORT_KEY_LIST = ["None", "FM Radio", "Lamp", "Monitor",
700
                  "TONE", "Alarm", "Weather"]
701
LONG_KEY_LIST = ["None", "FM Radio", "Lamp",
702
                 "Monitor", "TONE", "Alarm", "Weather"]
703
SHORT_KEY730_LIST = ["None", "Scan", "FM", "Warn", "TONE", "Weather",
704
                     "Copy CH"]
705
LONG_KEY730_LIST = SHORT_KEY730_LIST + ["Monitor"]
706
BUSYLOCK_LIST = ["Off", "On"]
707
PRESS_NAME = ["stopkey1", "ssidekey1", "ssidekey2",
708
              "ltopkey2", "lsidekey3", "lsidekey4"]
709

    
710
VFOA_NAME = ["rxfreqa",
711
             "txfreq",
712
             "rxtone",
713
             "txtone",
714
             "pttid",
715
             "specialqta",
716
             "bcl",
717
             "lowpower",
718
             "wide",
719
             "offset"]
720

    
721
VFOB_NAME = ["rxfreqb",
722
             "txfreq",
723
             "rxtoneb",
724
             "txtone",
725
             "pttid",
726
             "specialqtb",
727
             "bclb",
728
             "lowpowerb",
729
             "wideb",
730
             "offsetb"]
731

    
732
# KEY
733
VOX_GAIN = ["OFF", "1", "2", "3", "4", "5"]
734
VOX_DELAY = ["1.05s", "2.0s", "3.0s"]
735
VOX_GAIN730 = ["OFF", "1", "2", "3"]
736
VOX_DELAY730 = ["0.5s", "1.0s", "2.0s", "3.0s"]
737
PTTID_VALUES = ["Off", "BOT", "EOT", "BOTH"]
738
BCLOCK_VALUES = ["Off", "On"]
739
FREQHOP_VALUES = ["Off", "On"]
740
SCAN_VALUES = ["Del", "Add"]
741

    
742
# AB CHANNEL
743
A_OFFSET = ["Off", "-", "+"]
744
A_TX_POWER = ["Low", "Mid", "High"]
745
A_BAND = ["Wide", "Narrow"]
746
A_BUSYLOCK = ["Off", "On"]
747
A_SPEC_QTDQT = ["Off", "On"]
748
A_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
749

    
750
B_OFFSET = ["Off", "-", "+"]
751
B_TX_POWER = ["Low", "Mid", "High"]
752
B_BAND = ["Wide", "Narrow"]
753
B_BUSYLOCK = ["Off", "On"]
754
B_SPEC_QTDQT = ["Off", "On"]
755
B_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
756

    
757
# FM
758
FM_WORKMODE = ["CH", "VFO"]
759
FM_CHANNEL = ['%s' % x for x in range(0, 26)]
760

    
761
# DTMF
762
GROUPCODE = ["", "Off", "*", "#", "A", "B", "C", "D"]
763

    
764
AB_LIST = ["A", "B"]
765
ALMOD_LIST = ["Site", "Tone", "Code"]
766
BANDWIDTH_LIST = ["Wide", "Narrow"]
767
COLOR_LIST = ["Off", "Blue", "Orange", "Purple"]
768
DTMFSPEED_LIST = ["%s ms" % x for x in range(50, 2010, 10)]
769
DTMFST_LIST = ["OFF", "DT-ST", "ANI-ST", "DT+ANI"]
770
MODE_LIST = ["Channel", "Name", "Frequency"]
771
PONMSG_LIST = ["Full", "Message", "Icon"]
772
PTTID_LIST = ["Off", "BOT", "EOT", "Both"]
773
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
774
RTONE_LIST = ["1000 Hz", "1450 Hz", "1750 Hz", "2100 Hz"]
775
RESUME_LIST = ["TO", "CO", "SE"]
776
ROGERRX_LIST = ["Off"] + AB_LIST
777
RPSTE_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
778
SAVE_LIST = ["Off", "1:1", "1:2", "1:3", "1:4"]
779
SCODE_LIST = ["%s" % x for x in range(1, 16)]
780
SHIFTD_LIST = ["Off", "+", "-"]
781
STEDELAY_LIST = ["OFF"] + ["%s ms" % x for x in range(100, 1100, 100)]
782
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
783
STEP_LIST = [str(x) for x in STEPS]
784
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 50.0]
785
STEP291_LIST = [str(x) for x in STEPS]
786
TDRAB_LIST = ["Off"] + AB_LIST
787
TDRCH_LIST = ["CH%s" % x for x in range(1, 129)]
788
TIMEOUT_LIST = ["%s sec" % x for x in range(15, 615, 15)] + \
789
    ["Off (if supported by radio)"]
790
TIMEOUT730_LIST = ["Off"] + ["%s sec" % x for x in range(30, 240, 30)]
791
VOICE_LIST = ["Off", "English", "Chinese"]
792
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
793
WORKMODE_LIST = ["Frequency", "Channel"]
794
# mic
795
MIC_GAIN_LIST = ['%s' % x for x in range(0, 10)]
796
MIC_GAIN_LIST_H8 = ['%s' % x for x in range(0, 33)]
797
H8_LIST = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS"]
798

    
799
GMRS_FREQS = bandplan_na.GMRS_HIRPT
800

    
801
NOAA_FREQS = [162550000, 162400000, 162475000, 162425000, 162450000,
802
              162500000, 162525000, 161650000, 161775000, 161750000,
803
              162000000]
804

    
805
HAM_GMRS_NAME = ["NOAA 1", "NOAA 2", "NOAA 3", "NOAA 4", "NOAA 5", "NOAA 6",
806
                 "NOAA 7", "NOAA 8", "NOAA 9", "NOAA 10", "NOAA 11"]
807

    
808
ALL_MODEL = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS", "TD-H3",
809
             "TD-H3-HAM", "TD-H3-GMRS", "RT-730"]
810

    
811
TD_H8 = b"\x50\x56\x4F\x4A\x48\x1C\x14"
812
TD_H3 = b"\x50\x56\x4F\x4A\x48\x5C\x14"
813
RT_730 = b"\x50\x47\x4F\x4A\x48\xC3\x44"
814

    
815

    
816
def in_range(freq, ranges):
817
    for lo, hi in ranges:
818
        if lo <= freq <= hi:
819
            return True
820
    return False
821

    
822

    
823
def _do_status(radio, block):
824
    status = chirp_common.Status()
825
    status.msg = "Cloning"
826
    status.cur = block
827
    status.max = radio._memsize
828
    radio.status_fn(status)
829

    
830

    
831
def _upper_band_from_data(data):
832
    return data[0x03:0x04]
833

    
834

    
835
def _upper_band_from_image(radio):
836
    return _upper_band_from_data(radio.get_mmap())
837

    
838

    
839
def _firmware_version_from_data(data, version_start, version_stop):
840
    version_tag = data[version_start:version_stop]
841
    return version_tag
842

    
843

    
844
def _firmware_version_from_image(radio):
845
    version = _firmware_version_from_data(radio.get_mmap(),
846
                                          radio._fw_ver_file_start,
847
                                          radio._fw_ver_file_stop)
848
    # LOG.debug("_firmware_version_from_image: " + util.hexprint(version))
849
    return version
850

    
851

    
852
def _do_ident(serial, magic, secondack=True):
853
    serial.timeout = 1
854

    
855
    LOG.info("Sending Magic: %s" % util.hexprint(magic))
856
    serial.write(magic)
857
    ack = serial.read(1)
858

    
859
    if ack != b"\x06":
860
        if ack:
861
            # LOG.debug(repr(ack))
862
            pass
863
        raise errors.RadioError("Radio did not respond")
864

    
865
    serial.write(b"\x02")
866

    
867
    response = b""
868
    for i in range(1, 9):
869
        byte = serial.read(1)
870
        response += byte
871
        if byte == b"\xDD":
872
            break
873

    
874
    if len(response) in [8, 12]:
875
        # DEBUG
876
        LOG.info("Valid response, got this:")
877
        LOG.info(util.hexprint(response))
878
        if len(response) == 12:
879
            ident = response[0] + response[3] + response[5] + response[7:]
880
        else:
881
            ident = response
882
    else:
883
        # bad response
884
        msg = "Unexpected response, got this:"
885
        msg += util.hexprint(response)
886
        LOG.debug(msg)
887
        raise errors.RadioError("Unexpected response from radio.")
888

    
889
    if secondack:
890
        serial.write(b"\x06")
891
        ack = serial.read(1)
892
        if ack != b"\x06":
893
            raise errors.RadioError("Radio refused clone")
894

    
895
    return ident
896

    
897

    
898
def response_mode(mode):
899
    data = mode
900
    return data
901

    
902

    
903
def _read_block(radio, start, size):
904
    serial = radio.pipe
905

    
906
    cmd = struct.pack(">cHb", b'R', start, size)
907
    expectedresponse = b"W" + cmd[1:]
908

    
909
    try:
910
        serial.write(cmd)
911
        response = serial.read(5 + size)
912
        if response[:4] != expectedresponse:
913
            raise errors.RadioError("Error reading block %04x." % (start))
914
        block_data = response[4:-1]
915

    
916
    except Exception:
917
        raise errors.RadioError("Failed to read block at %04x" % start)
918

    
919
    return block_data
920

    
921

    
922
def _get_radio_firmware_version(radio):
923
    if radio.MODEL in ALL_MODEL:
924
        block = _read_block(radio, 0x1B40, 0x20)
925
        version = block[0:6]
926
    return version
927

    
928

    
929
IDENT_BLACKLIST = {
930
    b"\x50\x56\x4F\x4A\x48\x1C\x14": "Radio identifies as TIDRADIO TD-H8",
931
}
932

    
933

    
934
def _do_download(radio):
935
    # Radio must have already been ident'd by detect_from_serial()
936
    data = radio.ident_mode
937
    # Main block
938
    LOG.info("Downloading...")
939

    
940
    for i in range(0, radio._memsize, 0x20):
941
        block = _read_block(radio, i, 0x20)
942
        data += block
943
        _do_status(radio, i)
944
    _do_status(radio, radio._memsize)
945
    LOG.info("done.")
946

    
947
    return memmap.MemoryMapBytes(data)
948

    
949

    
950
def _exit_write_block(radio):
951
    serial = radio.pipe
952
    try:
953
        serial.write(b"E")
954

    
955
    except Exception:
956
        raise errors.RadioError("Radio refused to exit programming mode")
957

    
958

    
959
def _write_block(radio, addr, data):
960
    serial = radio.pipe
961
    cmd = struct.pack(">cHb", b'W', addr, 0x20)
962
    data = radio.get_mmap()[addr + 8: addr + 40]
963
    # The checksum needs to be in the last
964
    check_sum = bytes([sum(data) & 0xFF])
965
    data += check_sum
966
    used_data = cmd + data
967
    serial.write(used_data)
968

    
969
    ack = radio.pipe.read(1)
970
    if ack != b"\x06":
971
        raise errors.RadioError("Radio refused to accept block 0x%04x" % addr)
972

    
973

    
974
def _do_upload(radio):
975
    data = _do_ident(radio.pipe, radio._idents[0])
976
    radio_version = _get_radio_firmware_version(radio)
977
    LOG.info("Radio Version is %s" % repr(radio_version))
978

    
979
    if radio.ident_mode == data:
980
        LOG.info("Successful match.")
981
    else:
982
        msg = ("Model mismatch!")
983
        raise errors.RadioError(msg)
984

    
985
    # Main block
986
    LOG.debug("Uploading...")
987

    
988
    for start_addr, end_addr in radio._ranges_main:
989
        for addr in range(start_addr, end_addr, 0x20):
990
            _write_block(radio, addr, 0x20)
991
            _do_status(radio, addr)
992
    _exit_write_block(radio)
993
    LOG.debug("Upload all done.")
994

    
995

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

    
999

    
1000
@directory.register
1001
class TDH8(chirp_common.CloneModeRadio):
1002
    """TIDRADIO TD-H8"""
1003
    VENDOR = "TIDRADIO"
1004
    MODEL = "TD-H8"
1005
    ident_mode = b'P31183\xff\xff'
1006
    BAUD_RATE = 38400
1007
    NEEDS_COMPAT_SERIAL = False
1008
    _memsize = 0x1eef
1009
    _ranges_main = [(0x0000, 0x1eef)]
1010
    _idents = [TD_H8]
1011
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
1012
    _rxbands = []
1013
    _aux_block = True
1014
    _tri_power = True
1015
    _gmrs = False
1016
    _ham = False
1017
    _mem_params = (0x1F2F)
1018

    
1019
    # offset of fw version in image file
1020
    _fw_ver_file_start = 0x1838
1021
    _fw_ver_file_stop = 0x1846
1022
    _valid_chars = TDH8_CHARSET
1023
    _tx_power = [chirp_common.PowerLevel("Low",  watts=1.00),
1024
                 chirp_common.PowerLevel("Mid",  watts=4.00),
1025
                 chirp_common.PowerLevel("High", watts=8.00)]
1026

    
1027
    @classmethod
1028
    def detect_from_serial(cls, pipe):
1029
        ident = _do_ident(pipe, cls._idents[0])
1030
        for rclass in [cls] + cls.detected_models():
1031
            if (rclass.ident_mode == ident and
1032
                    rclass.MODEL.startswith(cls.MODEL)):
1033
                return rclass
1034
        LOG.error('No model match found for %r', ident)
1035
        raise errors.RadioError('Unsupported model')
1036

    
1037
    @classmethod
1038
    def get_prompts(cls):
1039
        rp = chirp_common.RadioPrompts()
1040
        rp.pre_download = (dedent("""\
1041
            1. Turn radio off.
1042
            2. Connect cable to mic/spkr connector.
1043
            3. Make sure connector is firmly connected.
1044
            4. Turn radio on (volume may need to be set at 100%).
1045
            5. Ensure that the radio is tuned to channel with no activity.
1046
            6. Click OK to download image from device."""))
1047
        rp.pre_upload = (dedent("""\
1048
            1. Turn radio off.
1049
            2. Connect cable to mic/spkr connector.
1050
            3. Make sure connector is firmly connected.
1051
            4. Turn radio on (volume may need to be set at 100%).
1052
            5. Ensure that the radio is tuned to channel with no activity.
1053
            6. Click OK to upload image to device."""))
1054
        return rp
1055

    
1056
    def get_features(self):
1057
        rf = chirp_common.RadioFeatures()
1058
        rf.has_settings = True
1059
        rf.has_bank = False
1060
        rf.has_cross = True
1061
        rf.has_ctone = True
1062
        rf.has_rx_dtcs = True
1063
        rf.has_tuning_step = False
1064
        rf.has_ctone = True
1065
        rf.can_odd_split = True
1066
        rf.valid_name_length = 8
1067
        rf.valid_characters = self._valid_chars
1068
        rf.valid_skips = ["", "S"]
1069
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
1070
        rf.valid_cross_modes = [
1071
            "Tone->Tone",
1072
            "DTCS->",
1073
            "->DTCS",
1074
            "Tone->DTCS",
1075
            "DTCS->Tone",
1076
            "->Tone",
1077
            "DTCS->DTCS"]
1078
        rf.valid_power_levels = [x for x in self._tx_power if x]
1079
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
1080
        rf.valid_modes = ["FM", "NFM"]
1081
        rf.valid_tuning_steps = STEPS
1082

    
1083
        rf.valid_bands = self._txbands + self._rxbands
1084
        rf.valid_bands.sort()
1085
        rf.memory_bounds = (1, 199)
1086
        return rf
1087

    
1088
    def process_mmap(self):
1089
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
1090

    
1091
    def sync_in(self):
1092
        try:
1093
            self._mmap = _do_download(self)
1094
            self.process_mmap()
1095
        except Exception as e:
1096
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
1097

    
1098
    def sync_out(self):
1099
        try:
1100
            _do_upload(self)
1101
        except errors.RadioError:
1102
            raise
1103
        except Exception as e:
1104
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
1105

    
1106
    def get_raw_memory(self, number):
1107
        return repr(self._memobj.memory[number])
1108

    
1109
    # Encoding processing
1110
    def _decode_tone(self, val):
1111
        if val == 16665 or val == 0:
1112
            return '', None, None
1113
        elif val >= 12000:
1114
            return 'DTCS', val - 12000, 'R'
1115
        elif val >= 8000:
1116
            return 'DTCS', val - 8000, 'N'
1117
        else:
1118
            return 'Tone', val / 10.0, None
1119

    
1120
    # Decoding processing
1121
    def _encode_tone(self, memval, mode, value, pol):
1122
        if mode == "":
1123
            memval[0].set_raw(0xFF)
1124
            memval[1].set_raw(0xFF)
1125
        elif mode == 'Tone':
1126
            memval.set_value(int(value * 10))
1127

    
1128
        elif mode == 'DTCS':
1129
            flag = 0x80 if pol == 'N' else 0xC0
1130
            memval.set_value(value)
1131
            memval[1].set_bits(flag)
1132
        else:
1133
            raise Exception("Internal error: invalid mode `%s'" % mode)
1134

    
1135
    def _get_mem(self, number):
1136
        return self._memobj.memory[number]
1137

    
1138
    def _get_nam(self, number):
1139
        return self._memobj.names[number - 1]
1140

    
1141
    def _get_fm(self, number):
1142
        return self._memobj.fmmode[number]
1143

    
1144
    def _get_get_scanvfo(self, number):
1145
        return self._memobj.fmvfo[number]
1146

    
1147
    def get_memory(self, number):
1148
        _mem = self._get_mem(number)
1149
        _nam = self._get_nam(number)
1150
        mem = chirp_common.Memory()
1151
        mem.number = number
1152

    
1153
        if _mem.get_raw()[0] == 0xff:
1154
            mem.empty = True
1155
            return mem
1156

    
1157
        # narrow and wide
1158
        mem.mode = _mem.wide and "NFM" or "FM"
1159

    
1160
        # power
1161
        try:
1162
            mem.power = self._tx_power[_mem.lowpower]
1163
            if mem.power is None:
1164
                # Gaps are basically missing power levels
1165
                raise IndexError()
1166
        except IndexError:
1167
            LOG.error("Radio reported invalid power level %s (in %s)" %
1168
                      (_mem.lowpower, self._tx_power))
1169
            mem.power = self._tx_power[0]
1170

    
1171
        # Channel name
1172
        for char in _nam.name:
1173
            if "\x00" in str(char) or "\xFF" in str(char):
1174
                char = ""
1175
            mem.name += str(char)
1176

    
1177
        mem.name = mem.name.rstrip()
1178
        if self.ident_mode != b'P31183\xff\xff' and \
1179
                (mem.number >= 189 and mem.number <= 199):
1180
            mem.name = HAM_GMRS_NAME[mem.number - 200]
1181

    
1182
        # tmode
1183
        lin2 = int(_mem.rxtone)
1184
        rxtone = self._decode_tone(lin2)
1185

    
1186
        lin = int(_mem.txtone)
1187
        txtone = self._decode_tone(lin)
1188

    
1189
        if txtone[0] == "Tone" and not rxtone[0]:
1190
            mem.tmode = "Tone"
1191
        elif txtone[0] == rxtone[0] and txtone[0] == "Tone" \
1192
                and mem.rtone == mem.ctone:
1193
            mem.tmode = "TSQL"
1194
        elif txtone[0] == rxtone[0] and txtone[0] == "DTCS" \
1195
                and mem.dtcs == mem.rx_dtcs:
1196
            mem.tmode = "DTCS"
1197
        elif rxtone[0] or txtone[0]:
1198
            mem.tmode = "Cross"
1199
            mem.cross_mode = "%s->%s" % (txtone[0], rxtone[0])
1200

    
1201
        chirp_common.split_tone_decode(mem, txtone, rxtone)
1202

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

    
1205
        mem.freq = int(_mem.rxfreq) * 10
1206
        if _mem.txfreq.get_raw() == b'\xff\xff\xff\xff':
1207
            mem.offset = 0
1208
            mem.duplex = 'off'
1209
        else:
1210
            chirp_common.split_to_offset(mem,
1211
                                         int(_mem.rxfreq) * 10,
1212
                                         int(_mem.txfreq) * 10)
1213

    
1214
        if self._gmrs:
1215
            # mem.duplex = ""
1216
            # mem.offset = 0
1217
            if mem.number >= 1 and mem.number <= 30:
1218
                mem.immutable.append('freq')
1219
                if mem.number >= 8 and mem.number <= 14:
1220
                    mem.mode = 'NFM'
1221
                    mem.power = self._tx_power[0]
1222
                    mem.immutable = ['freq', 'mode', 'power',
1223
                                     'duplex', 'offset']
1224
            elif mem.number >= 31 and mem.number <= 54:
1225
                # mem.immutable = ['duplex', 'offset']
1226
                mem.duplex = '+'
1227
                mem.offset = 5000000
1228
            elif mem.number >= 189 and mem.number <= 199:
1229
                ham_freqs = NOAA_FREQS[mem.number - 189]
1230
                mem.freq = ham_freqs
1231
                mem.immutable = ['name', 'power', 'duplex', 'freq',
1232
                                 'rx_dtcs', 'vfo', 'tmode', 'empty',
1233
                                 'offset', 'rtone', 'ctone', 'dtcs',
1234
                                 'dtcs_polarity', 'cross_mode']
1235
        elif self._ham:
1236
            if mem.number >= 189 and mem.number <= 199:
1237
                ham_freqs = NOAA_FREQS[mem.number - 189]
1238
                mem.freq = ham_freqs
1239
                mem.immutable = ['name', 'power', 'freq', 'rx_dtcs', 'vfo',
1240
                                 'tmode', 'empty', 'offset', 'rtone', 'ctone',
1241
                                 'dtcs', 'dtcs_polarity', 'cross_mode']
1242

    
1243
        # other function
1244
        # pttid
1245
        mem.extra = RadioSettingGroup("Extra", "extra")
1246

    
1247
        if self.MODEL != "RT-730":
1248
            rs = RadioSetting("pttid", "PTT ID",
1249
                              RadioSettingValueList(PTTID_VALUES,
1250
                                                    PTTID_VALUES[_mem.pttid]))
1251
            mem.extra.append(rs)
1252

    
1253
        # Busylock
1254
        rs = RadioSetting("bcl", "Busy Lock",
1255
                          RadioSettingValueList(BCLOCK_VALUES,
1256
                                                BCLOCK_VALUES[_mem.bcl]))
1257
        mem.extra.append(rs)
1258

    
1259
        if self.MODEL != "RT-730":
1260
            rs = RadioSetting(
1261
                "freqhop", "Frequency Hop", RadioSettingValueList(
1262
                    FREQHOP_VALUES, FREQHOP_VALUES[_mem.freqhop]))
1263
            mem.extra.append(rs)
1264

    
1265
        if in_range(mem.freq, self._rxbands):
1266
            mem.duplex = 'off'
1267
            mem.immutable.append('duplex')
1268
        if in_range(mem.freq, [AIRBAND]):
1269
            mem.mode = 'AM'
1270
            mem.immutable.append('mode')
1271

    
1272
        return mem
1273

    
1274
    def _set_mem(self, number):
1275
        return self._memobj.memory[number]
1276

    
1277
    def _set_nam(self, number):
1278
        return self._memobj.names[number - 1]
1279

    
1280
    def _get_scan_list(self, scan_data):
1281
        # scan_val_list - Get all scans Add data 1-200 digits
1282
        scan_val_list = []
1283
        for x in range(25):
1284
            a = self._get_scan(x)
1285
            for i in range(0, 8):
1286
                scan_val = (getattr(a, 'scan%i' % (i+1)))
1287
                used_scan_val = str(scan_val)[3]
1288
                scan_val_list.append(used_scan_val)
1289

    
1290
        # used_scan_list - 25 structures, split the scan added
1291
        # data into 25 groups of 8 bits each
1292
        used_scan_list = []
1293
        count_num = 1
1294
        for i in range(0, len(scan_val_list), 8):
1295
            used_scan_list.append(scan_val_list[i:i + 8])
1296
            count_num += 1
1297
        # Determine whether it is a standard number that can be divisible
1298
        # Which group is the scan addition located in the modified channel
1299
        if scan_data % 8 != 0:
1300
            x_list = scan_data / 8
1301
            y_list = scan_data % 8
1302

    
1303
        else:
1304
            x_list = (scan_data / 8) - 1
1305
            y_list = 8
1306

    
1307
        return ([x_list, y_list])
1308

    
1309
    def set_memory(self, mem):
1310
        _mem = self._get_mem(mem.number)
1311
        _nam = self._get_nam(mem.number)
1312

    
1313
        # When the channel is empty, you need to set "usedflags" to 0,
1314
        # When the channel is used , you need to set "usedflags" to 1.
1315
        self._memobj.usedflags[mem.number - 1] = int(not mem.empty)
1316

    
1317
        if mem.empty:
1318
            _mem.fill_raw(b'\xFF')
1319
            return
1320

    
1321
        _mem.fill_raw(b'\x00')
1322

    
1323
        if mem.duplex == "":
1324
            _mem.rxfreq = _mem.txfreq = mem.freq / 10
1325
        elif mem.duplex == "split":
1326
            _mem.txfreq = mem.offset / 10
1327
        elif mem.duplex == "+":
1328
            _mem.txfreq = (mem.freq + mem.offset) / 10
1329
        elif mem.duplex == "-":
1330
            _mem.txfreq = (mem.freq - mem.offset) / 10
1331
        elif mem.duplex == 'off':
1332
            _mem.txfreq.fill_raw(b'\xFF')
1333
        else:
1334
            _mem.txfreq = mem.freq / 10
1335

    
1336
        if in_range(mem.freq, self._rxbands):
1337
            _mem.txfreq.fill_raw(b'\xFF')
1338

    
1339
        _mem.rxfreq = mem.freq / 10
1340
        _namelength = self.get_features().valid_name_length
1341

    
1342
        for i in range(_namelength):
1343
            try:
1344
                _nam.name[i] = mem.name[i]
1345
            except IndexError:
1346
                _nam.name[i] = "\xFF"
1347

    
1348
        txtone, rxtone = chirp_common.split_tone_encode(mem)
1349

    
1350
        self._encode_tone(_mem.txtone, *txtone)
1351
        self._encode_tone(_mem.rxtone, *rxtone)
1352

    
1353
        if mem.mode == "FM":
1354
            _mem.wide = 0
1355
        else:
1356
            _mem.wide = 1
1357

    
1358
        try:
1359
            _mem.lowpower = self._tx_power.index(mem.power or
1360
                                                 self._tx_power[-1])
1361
        except ValueError:
1362
            _mem.lowpower = 0
1363
            LOG.warning('Unsupported power %r', mem.power)
1364

    
1365
        # Skip/Scanadd Setting
1366
        self._memobj.scanadd[mem.number - 1] = mem.skip != 'S'
1367

    
1368
        for setting in mem.extra:
1369
            if (self.ident_mode == b'P31185\xff\xff' or
1370
                self.ident_mode == b'P31184\xff\xff') and \
1371
                    mem.number >= 189 and mem.number <= 199:
1372
                if setting.get_name() == 'pttid':
1373
                    setting.value = 'Off'
1374
                    setattr(_mem, setting.get_name(), setting.value)
1375
                elif setting.get_name() == 'bcl':
1376
                    setting.value = 'Off'
1377
                    setattr(_mem, setting.get_name(), setting.value)
1378
                elif setting.get_name() == 'freqhop':
1379
                    setting.value = 'Off'
1380
                    setattr(_mem, setting.get_name(), setting.value)
1381
            else:
1382
                setattr(_mem, setting.get_name(), setting.value)
1383

    
1384
    def _is_orig(self):
1385
        version_tag = _firmware_version_from_image(self)
1386
        try:
1387
            if b'BFB' in version_tag:
1388
                idx = version_tag.index(b"BFB") + 3
1389
                version = int(version_tag[idx:idx + 3])
1390
                return version < 291
1391
            return False
1392
        except Exception:
1393
            pass
1394
        raise errors.RadioError("Unable to parse version string %s" %
1395
                                version_tag)
1396

    
1397
    def _my_upper_band(self):
1398
        band_tag = _upper_band_from_image(self)
1399
        return band_tag
1400

    
1401
    def _get_settings(self):
1402
        _settings = self._memobj.settings
1403
        _press = self._memobj.press
1404
        _aoffset = self._memobj.aoffset
1405
        _boffset = self._memobj.boffset
1406
        _vfoa = self._memobj.vfoa
1407
        _vfob = self._memobj.vfob
1408
        if self.MODEL != "RT-730":
1409
            _gcode = self._memobj.groupcode
1410
        _msg = self._memobj.poweron_msg
1411
        basic = RadioSettingGroup("basic", "Basic Settings")
1412
        abblock = RadioSettingGroup("abblock", "A/B Channel")
1413
        fmmode = RadioSettingGroup("fmmode", "FM")
1414
        dtmf = RadioSettingGroup("dtmf", "DTMF")
1415

    
1416
        # group = RadioSettings(fmmode, dtmf)
1417
        group = RadioSettings(basic)
1418

    
1419
        rs = RadioSetting("squelch", "Squelch Level",
1420
                          RadioSettingValueList(
1421
                              SQUELCH, SQUELCH[_settings.squelch]))
1422
        basic.append(rs)
1423

    
1424
        if self.MODEL != "RT-730":
1425
            rs = RadioSetting("ligcon", "Light Control",
1426
                              RadioSettingValueList(
1427
                                  LIGHT_LIST, LIGHT_LIST[_settings.ligcon]))
1428
            basic.append(rs)
1429

    
1430
        rs = RadioSetting("voiceprompt", "Voice Prompt",
1431
                          RadioSettingValueList(
1432
                              VOICE_PRMPT_LIST, VOICE_PRMPT_LIST[
1433
                                  _settings.voiceprompt]))
1434
        basic.append(rs)
1435

    
1436
        rs = RadioSetting("keyautolock", "Auto Lock",
1437
                          RadioSettingValueList(
1438
                              AUTOLOCK_LIST, AUTOLOCK_LIST[
1439
                                  _settings.keyautolock]))
1440
        basic.append(rs)
1441

    
1442
        if self.MODEL != "RT-730":
1443
            rs = RadioSetting("mdfa", "MDF-A",
1444
                              RadioSettingValueList(
1445
                                  MDFA_LIST, MDFA_LIST[_settings.mdfa]))
1446
            basic.append(rs)
1447

    
1448
            rs = RadioSetting("mdfb", "MDF-B",
1449
                              RadioSettingValueList(
1450
                                  MDFB_LIST, MDFB_LIST[_settings.mdfb]))
1451
            basic.append(rs)
1452

    
1453
            rs = RadioSetting("sync", "SYNC",
1454
                              RadioSettingValueList(
1455
                                  SYNC_LIST, SYNC_LIST[_settings.sync]))
1456
            basic.append(rs)
1457

    
1458
            rs = RadioSetting("save", "Battery Save",
1459
                              RadioSettingValueList(
1460
                                  BTV_SAVER_LIST,
1461
                                  BTV_SAVER_LIST[_settings.save]))
1462
            basic.append(rs)
1463

    
1464
        rs = RadioSetting("dbrx", "Double Rx",
1465
                          RadioSettingValueList(
1466
                              DBRX_LIST, DBRX_LIST[_settings.dbrx]))
1467
        basic.append(rs)
1468

    
1469
        if self.MODEL != "RT-730":
1470
            rs = RadioSetting("astep", "A Step",
1471
                              RadioSettingValueList(
1472
                                  ASTEP_LIST, ASTEP_LIST[_settings.astep]))
1473
            basic.append(rs)
1474

    
1475
            rs = RadioSetting("bstep", "B Step",
1476
                              RadioSettingValueList(
1477
                                  BSTEP_LIST, BSTEP_LIST[_settings.bstep]))
1478
            basic.append(rs)
1479

    
1480
        rs = RadioSetting("scanmode", "Scan Mode",
1481
                          RadioSettingValueList(
1482
                              SCAN_MODE_LIST, SCAN_MODE_LIST[
1483
                                  _settings.scanmode]))
1484
        basic.append(rs)
1485

    
1486
        if self.MODEL != "RT-730":
1487
            rs = RadioSetting("pritx", "Priority TX",
1488
                              RadioSettingValueList(
1489
                                  PRIO_LIST, PRIO_LIST[_settings.pritx]))
1490
            basic.append(rs)
1491

    
1492
        rs = RadioSetting("btnvoice", "Beep",
1493
                          RadioSettingValueBoolean(_settings.btnvoice))
1494
        basic.append(rs)
1495

    
1496
        if self.MODEL != "RT-730":
1497
            rs = RadioSetting("rogerprompt", "Roger",
1498
                              RadioSettingValueBoolean(_settings.rogerprompt))
1499
            basic.append(rs)
1500

    
1501
        rs = RadioSetting("txled", "Disp Lcd(TX)",
1502

    
1503
                          RadioSettingValueBoolean(_settings.txled))
1504
        basic.append(rs)
1505

    
1506
        rs = RadioSetting("rxled", "Disp Lcd(RX)",
1507
                          RadioSettingValueBoolean(_settings.rxled))
1508
        basic.append(rs)
1509

    
1510
        if self.MODEL != "RT-730":
1511
            rs = RadioSetting("onlychmode", "Only CH Mode",
1512
                              RadioSettingValueBoolean(_settings.onlychmode))
1513
            basic.append(rs)
1514
            rs = RadioSetting("ssidekey1", "SHORT_KEY_PF1",
1515
                              RadioSettingValueList(
1516
                                  SHORT_KEY_LIST, SHORT_KEY_LIST[
1517
                                      _press.ssidekey1]))
1518
            basic.append(rs)
1519
            rs = RadioSetting("lsidekey3", "LONG_KEY_PF1",
1520
                              RadioSettingValueList(
1521
                                  LONG_KEY_LIST,
1522
                                  LONG_KEY_LIST[_press.lsidekey3]))
1523
            basic.append(rs)
1524

    
1525
        if self.MODEL in H8_LIST:
1526
            rs = RadioSetting("stopkey1", "SHORT_KEY_TOP",
1527
                              RadioSettingValueList(SHORT_KEY_LIST,
1528
                                                    SHORT_KEY_LIST[0]))
1529
            basic.append(rs)
1530

    
1531
            rs = RadioSetting("ltopkey2", "LONG_KEY_TOP",
1532
                              RadioSettingValueList(
1533
                                  LONG_KEY_LIST,
1534
                                  LONG_KEY_LIST[_press.ltopkey2]))
1535
            basic.append(rs)
1536

    
1537
            rs = RadioSetting("ssidekey2", "SHORT_KEY_PF2",
1538
                              RadioSettingValueList(
1539
                                  SHORT_KEY_LIST,
1540
                                  SHORT_KEY_LIST[_press.ssidekey2]))
1541
            basic.append(rs)
1542

    
1543
            rs = RadioSetting("lsidekey4", "LONG_KEY_PF2",
1544
                              RadioSettingValueList(
1545
                                LONG_KEY_LIST,
1546
                                LONG_KEY_LIST[_press.lsidekey4]))
1547
            basic.append(rs)
1548

    
1549
        if self.MODEL != "RT-730":
1550
            rs = RadioSetting("voxgain", "VOX Gain",
1551
                              RadioSettingValueList(
1552
                                  VOX_GAIN, VOX_GAIN[_settings.voxgain]))
1553
            basic.append(rs)
1554

    
1555
            rs = RadioSetting("voxdelay", "VOX Delay",
1556
                              RadioSettingValueList(
1557
                                  VOX_DELAY, VOX_DELAY[_settings.voxdelay]))
1558
            basic.append(rs)
1559

    
1560
            rs = RadioSetting("ponmsg", "Power-On Message",
1561
                              RadioSettingValueList(
1562
                                  PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
1563
            basic.append(rs)
1564

    
1565
            # mic gain
1566
            if self.MODEL not in H8_LIST:
1567
                _mic = self._memobj.mic
1568
                rs = RadioSetting("micgain", "MIC GAIN",
1569
                                  RadioSettingValueList(
1570
                                      MIC_GAIN_LIST,
1571
                                      current_index=_mic.micgain))
1572
                basic.append(rs)
1573

    
1574
            if self.MODEL not in H8_LIST:
1575
                rs = RadioSetting("kill", "Kill",
1576
                                  RadioSettingValueBoolean(_settings.kill))
1577
                basic.append(rs)
1578
                rs = RadioSetting("stun", "Stun",
1579
                                  RadioSettingValueBoolean(_settings.stun))
1580
                basic.append(rs)
1581

    
1582
        def _filter(name):
1583
            filtered = ""
1584
            for char in str(name):
1585
                if char in chirp_common.CHARSET_ASCII:
1586
                    filtered += char
1587
                else:
1588
                    filtered += " "
1589
            return filtered
1590

    
1591
        rs = RadioSetting("poweron_msg.msg1", "Power-On Message 1",
1592
                          RadioSettingValueString(0, 16, _filter(_msg.msg1)))
1593
        basic.append(rs)
1594
        rs = RadioSetting("poweron_msg.msg2", "Power-On Message 2",
1595
                          RadioSettingValueString(0, 16, _filter(_msg.msg2)))
1596
        basic.append(rs)
1597
        rs = RadioSetting("poweron_msg.msg3", "Power-On Message 3",
1598
                          RadioSettingValueString(0, 16, _filter(_msg.msg3)))
1599
        basic.append(rs)
1600
        if self.MODEL == "RT-730":
1601
            rsvs = RadioSettingValueString(0, 16, _filter(_msg.msg4))
1602
            rs = RadioSetting("poweron_msg.msg4", "Power-On Message 4", rsvs)
1603
            basic.append(rs)
1604

    
1605
        if self.MODEL == "RT-730":
1606
            rs = RadioSetting("ligcon", "Light Control",
1607
                              RadioSettingValueList(
1608
                                  LIGHT730_LIST,
1609
                                  LIGHT730_LIST[_settings.ligcon]))
1610
            basic.append(rs)
1611

    
1612
            rs = RadioSetting("tot", "Time-out Timer",
1613
                              RadioSettingValueList(
1614
                                  TIMEOUT730_LIST, TIMEOUT730_LIST[
1615
                                      _settings.tot]))
1616
            basic.append(rs)
1617

    
1618
            rs = RadioSetting("press.rogerprompt", "Roger",
1619
                              RadioSettingValueList(
1620
                                  PTTID_LIST, PTTID_LIST[_press.rogerprompt]))
1621
            basic.append(rs)
1622

    
1623
            rs = RadioSetting("lang", "Language",
1624
                              RadioSettingValueList(
1625
                                  LANG_LIST, LANG_LIST[_settings.lang]))
1626
            basic.append(rs)
1627

    
1628
            rs = RadioSetting("save", "Battery Save",
1629
                              RadioSettingValueList(
1630
                                  OFFON_LIST, OFFON_LIST[_settings.save]))
1631
            basic.append(rs)
1632

    
1633
            rs = RadioSetting("mdfa", "Channel Names",
1634
                              RadioSettingValueList(
1635
                                  OFFON_LIST, OFFON_LIST[_settings.mdfa]))
1636
            basic.append(rs)
1637

    
1638
            rs = RadioSetting("hoptype", "Hop Type",
1639
                              RadioSettingValueList(
1640
                                  HOP_LIST, HOP_LIST[_settings.hoptype]))
1641
            basic.append(rs)
1642

    
1643
            rs = RadioSetting("tailclean", "QT/DQT Tail",
1644
                              RadioSettingValueBoolean(_settings.tailclean))
1645
            basic.append(rs)
1646

    
1647
            rs = RadioSetting("press.ssidekey1", "PF1 Key(Short)",
1648
                              RadioSettingValueList(
1649
                                  SHORT_KEY730_LIST, SHORT_KEY730_LIST[
1650
                                      _press.ssidekey1]))
1651
            basic.append(rs)
1652
            rs = RadioSetting("press.lsidekey1", "PF1 Key(Long)",
1653
                              RadioSettingValueList(
1654
                                  LONG_KEY730_LIST,
1655
                                  LONG_KEY730_LIST[_press.lsidekey1]))
1656
            basic.append(rs)
1657
            rs = RadioSetting("press.ssidekey2", "PF2 Key(Short)",
1658
                              RadioSettingValueList(
1659
                                  SHORT_KEY730_LIST, SHORT_KEY730_LIST[
1660
                                      _press.ssidekey2]))
1661
            basic.append(rs)
1662
            rs = RadioSetting("press.lsidekey2", "PF2 Key(Long)",
1663
                              RadioSettingValueList(
1664
                                  LONG_KEY730_LIST,
1665
                                  LONG_KEY730_LIST[_press.lsidekey2]))
1666
            basic.append(rs)
1667

    
1668
            rs = RadioSetting("voxgain", "VOX Gain",
1669
                              RadioSettingValueList(
1670
                                  VOX_GAIN730,
1671
                                  VOX_GAIN730[_settings.voxgain]))
1672
            basic.append(rs)
1673

    
1674
            rs = RadioSetting("voxdelay", "VOX Delay",
1675
                              RadioSettingValueList(
1676
                                  VOX_DELAY730,
1677
                                  VOX_DELAY730[_settings.voxdelay]))
1678
            basic.append(rs)
1679

    
1680
        if self.MODEL != "RT-730":
1681
            group.append(abblock)
1682

    
1683
            # A channel
1684
            a_freq = int(_vfoa.rxfreqa)
1685
            freqa = "%i.%05i" % (a_freq / 100000, a_freq % 100000)
1686
            if freqa == "0.00000":
1687
                val1a = RadioSettingValueString(0, 7, '0.00000')
1688
            else:
1689
                val1a = RadioSettingValueFloat(
1690
                    136, 520, float(freqa), 0.00001, 5)
1691
            rs = RadioSetting("rxfreqa", "A Channel - Frequency", val1a)
1692
            abblock.append(rs)
1693

    
1694
            # Offset
1695
            # If the offset is 12.345
1696
            # Then the data obtained is [0x45, 0x23, 0x01, 0x00]
1697
            a_set_val = _aoffset.ofseta
1698
            a_set_list = len(_aoffset.ofseta) - 1
1699
            real_val = ''
1700
            for i in range(a_set_list, -1, -1):
1701
                real_val += str(a_set_val[i])[2:]
1702
            if real_val == "FFFFFFFF":
1703
                rs = RadioSetting("ofseta", "A Offset Frequency",
1704
                                  RadioSettingValueString(0, 7, ""))
1705

    
1706
            else:
1707
                real_val = int(real_val)
1708
                real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
1709
                rs = RadioSetting("ofseta", "A Offset Frequency",
1710
                                  RadioSettingValueFloat(
1711
                                      0.00000, 59.99750, real_val, 0.00001, 5))
1712
            abblock.append(rs)
1713

    
1714
            rs = RadioSetting("offset", "A Offset",
1715
                              RadioSettingValueList(
1716
                                  A_OFFSET, A_OFFSET[_vfoa.offset]))
1717
            abblock.append(rs)
1718

    
1719
            rs = RadioSetting("lowpower", "A TX Power",
1720
                              RadioSettingValueList(
1721
                                  A_TX_POWER, A_TX_POWER[_vfoa.lowpower]))
1722
            abblock.append(rs)
1723

    
1724
            rs = RadioSetting("wide", "A Band",
1725
                              RadioSettingValueList(
1726
                                  A_BAND, A_BAND[_vfoa.wide]))
1727
            abblock.append(rs)
1728

    
1729
            rs = RadioSetting("bcl", "A Busy Lock",
1730
                              RadioSettingValueList(
1731
                                  A_BUSYLOCK, A_BUSYLOCK[_vfoa.bcl]))
1732
            abblock.append(rs)
1733

    
1734
            rs = RadioSetting("specialqta", "A Special QT/DQT",
1735
                              RadioSettingValueList(
1736
                                  A_SPEC_QTDQT,
1737
                                  A_SPEC_QTDQT[_vfoa.specialqta]))
1738
            abblock.append(rs)
1739

    
1740
            rs = RadioSetting("aworkmode", "A Work Mode",
1741
                              RadioSettingValueList(
1742
                                  A_WORKMODE, A_WORKMODE[_settings.aworkmode]))
1743
            abblock.append(rs)
1744

    
1745
            # B channel
1746
            b_freq = int(str(int(_vfob.rxfreqb)).ljust(8, '0'))
1747
            freqb = "%i.%05i" % (b_freq / 100000, b_freq % 100000)
1748
            if freqb == "0.00000":
1749
                val1a = RadioSettingValueString(0, 7, '0.00000')
1750
            else:
1751
                val1a = RadioSettingValueFloat(
1752
                    136, 520, float(freqb), 0.00001, 5)
1753
            rs = RadioSetting("rxfreqb", "B Channel - Frequency", val1a)
1754
            abblock.append(rs)
1755

    
1756
            # Offset frequency
1757
            # If the offset is 12.345
1758
            # Then the data obtained is [0x45, 0x23, 0x01, 0x00]
1759
            # Need to use the following anonymous function to process data
1760
            b_set_val = _boffset.ofsetb
1761
            b_set_list = len(_boffset.ofsetb) - 1
1762
            real_val = ''
1763
            for i in range(b_set_list, -1, -1):
1764
                real_val += str(b_set_val[i])[2:]
1765
            if real_val == "FFFFFFFF":
1766
                rs = RadioSetting("ofsetb", "B Offset Frequency",
1767
                                  RadioSettingValueString(0, 7, " "))
1768
            else:
1769
                real_val = int(real_val)
1770
                real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
1771
                rs = RadioSetting("ofsetb", "B Offset Frequency",
1772
                                  RadioSettingValueFloat(
1773
                                      0.00000, 59.99750, real_val, 0.00001, 5))
1774
            abblock.append(rs)
1775

    
1776
            rs = RadioSetting("offsetb", "B Offset",
1777
                              RadioSettingValueList(
1778
                                  B_OFFSET, B_OFFSET[_vfob.offsetb]))
1779
            abblock.append(rs)
1780

    
1781
            rs = RadioSetting("lowpowerb", "B TX Power",
1782
                              RadioSettingValueList(
1783
                                  B_TX_POWER, B_TX_POWER[_vfob.lowpowerb]))
1784
            abblock.append(rs)
1785

    
1786
            rs = RadioSetting("wideb", "B Band",
1787
                              RadioSettingValueList(
1788
                                  B_BAND, B_BAND[_vfob.wideb]))
1789
            abblock.append(rs)
1790

    
1791
            rs = RadioSetting("bclb", "B Busy Lock",
1792
                              RadioSettingValueList(
1793
                                  B_BUSYLOCK, B_BUSYLOCK[_vfob.bclb]))
1794
            abblock.append(rs)
1795

    
1796
            rs = RadioSetting("specialqtb", "B Special QT/DQT",
1797
                              RadioSettingValueList(
1798
                                  B_SPEC_QTDQT,
1799
                                  B_SPEC_QTDQT[_vfob.specialqtb]))
1800
            abblock.append(rs)
1801

    
1802
            rs = RadioSetting("bworkmode", "B Work Mode",
1803
                              RadioSettingValueList(
1804
                                  B_WORKMODE, B_WORKMODE[_settings.bworkmode]))
1805
            abblock.append(rs)
1806

    
1807
        group.append(fmmode)
1808

    
1809
        rs = RadioSetting("fmworkmode", "Work Mode",
1810
                          RadioSettingValueList(
1811
                              FM_WORKMODE, FM_WORKMODE[_settings.fmworkmode]))
1812
        fmmode.append(rs)
1813

    
1814
        rs = RadioSetting("fmroad", "Channel",
1815
                          RadioSettingValueList(
1816
                              FM_CHANNEL, FM_CHANNEL[_settings.fmroad]))
1817
        fmmode.append(rs)
1818

    
1819
        rs = RadioSetting("fmrec", "Forbid Receive",
1820
                          RadioSettingValueBoolean(_settings.fmrec))
1821
        fmmode.append(rs)
1822

    
1823
        # FM
1824
        numeric = '0123456789.'
1825
        for i in range(25):
1826
            if self._memobj.fmusedflags[i]:
1827
                _fm = self._get_fm(i).fmblock
1828
                try:
1829
                    if not (760 < int(_fm) < 1080):
1830
                        raise ValueError()
1831
                    val = '%.1f' % (int(_fm) / 10)
1832
                except ValueError:
1833
                    LOG.warning('FM channel index %i is invalid', i)
1834
                    val = ''
1835
            else:
1836
                val = ''
1837
            rs = RadioSetting('block%02i' % i, "Channel %i" % (i + 1),
1838
                              RadioSettingValueString(0, 5,
1839
                                                      val,
1840
                                                      False, charset=numeric))
1841
            fmmode.append(rs)
1842

    
1843
        try:
1844
            _fmv = int(self._memobj.fmvfo) / 10
1845
        except ValueError:
1846
            LOG.warning('FM VFO is invalid')
1847
            _fmv = 0
1848

    
1849
        rs = RadioSetting(
1850
            "fmvfo", "VFO", RadioSettingValueFloat(
1851
                76.0, 108.0, _fmv, 0.1, 1))
1852
        fmmode.append(rs)
1853

    
1854
        if self.MODEL != "RT-730":
1855
            group.append(dtmf)
1856

    
1857
            # DTMF
1858
            gcode_val = str(_gcode.gcode)[2:]
1859
            if gcode_val == "FF":
1860
                gcode_val = "Off"
1861
            elif gcode_val == "0F":
1862
                gcode_val = "#"
1863
            elif gcode_val == "0E":
1864
                gcode_val = "*"
1865
            elif gcode_val == '00':
1866
                gcode_val = ""
1867
            else:
1868
                gcode_val = gcode_val[1]
1869
            rs = RadioSetting("gcode", "Group Code",
1870
                              RadioSettingValueList(GROUPCODE,
1871
                                                    gcode_val))
1872
            dtmf.append(rs)
1873

    
1874
            icode_list = self._memobj.icode.idcode
1875
            used_icode = ''
1876
            for i in icode_list:
1877
                if i == 0xFF:
1878
                    continue
1879
                used_icode += str(i)[3]
1880
            dtmfcharsani = "0123456789ABCD "
1881
            i_val = RadioSettingValueString(0, 3, used_icode)
1882
            rs = RadioSetting("icode", "ID Code", i_val)
1883
            i_val.set_charset(dtmfcharsani)
1884
            dtmf.append(rs)
1885

    
1886
            gcode_list_1 = self._memobj.group1.group1
1887
            used_group1 = ''
1888
            for i in gcode_list_1:
1889
                if i == 0xFF:
1890
                    continue
1891
                used_group1 += str(i)[3]
1892
            group1_val = RadioSettingValueString(0, 7, used_group1)
1893
            rs = RadioSetting("group1", "1", group1_val)
1894
            group1_val.set_charset(dtmfcharsani)
1895
            dtmf.append(rs)
1896

    
1897
            gcode_list_2 = self._memobj.group2.group2
1898
            used_group2 = ''
1899
            for i in gcode_list_2:
1900
                if i == 0xFF:
1901
                    continue
1902
                used_group2 += str(i)[3]
1903
            group2_val = RadioSettingValueString(0, 7, used_group2)
1904
            rs = RadioSetting("group2", "2", group2_val)
1905
            group2_val.set_charset(dtmfcharsani)
1906
            dtmf.append(rs)
1907

    
1908
            gcode_list_3 = self._memobj.group3.group3
1909
            used_group3 = ''
1910
            for i in gcode_list_3:
1911
                if i == 0xFF:
1912
                    continue
1913
                used_group3 += str(i)[3]
1914
            group3_val = RadioSettingValueString(0, 7, used_group3)
1915
            rs = RadioSetting("group3", "3", group3_val)
1916
            group3_val.set_charset(dtmfcharsani)
1917
            dtmf.append(rs)
1918

    
1919
            gcode_list_4 = self._memobj.group4.group4
1920
            used_group4 = ''
1921
            for i in gcode_list_4:
1922
                if i == 0xFF:
1923
                    continue
1924
                used_group4 += str(i)[3]
1925
            group4_val = RadioSettingValueString(0, 7, used_group4)
1926
            rs = RadioSetting("group4", "4", group4_val)
1927
            group4_val.set_charset(dtmfcharsani)
1928
            dtmf.append(rs)
1929

    
1930
            gcode_list_5 = self._memobj.group5.group5
1931
            used_group5 = ''
1932
            for i in gcode_list_5:
1933
                if i == 0xFF:
1934
                    continue
1935
                used_group5 += str(i)[3]
1936
            group5_val = RadioSettingValueString(0, 7, used_group5)
1937
            rs = RadioSetting("group5", "5", group5_val)
1938
            group5_val.set_charset(dtmfcharsani)
1939
            dtmf.append(rs)
1940

    
1941
            gcode_list_6 = self._memobj.group6.group6
1942
            used_group6 = ''
1943
            for i in gcode_list_6:
1944
                if i == 0xFF:
1945
                    continue
1946
                used_group6 += str(i)[3]
1947
            group6_val = RadioSettingValueString(0, 7, used_group6)
1948
            rs = RadioSetting("group6", "6", group6_val)
1949
            group6_val.set_charset(dtmfcharsani)
1950
            dtmf.append(rs)
1951

    
1952
            gcode_list_7 = self._memobj.group7.group7
1953
            used_group7 = ''
1954
            for i in gcode_list_7:
1955
                if i == 0xFF:
1956
                    continue
1957
                used_group7 += str(i)[3]
1958
            group7_val = RadioSettingValueString(0, 7, used_group7)
1959
            rs = RadioSetting("group7", "7", group7_val)
1960
            group7_val.set_charset(dtmfcharsani)
1961
            dtmf.append(rs)
1962

    
1963
            gcode_list_8 = self._memobj.group8.group8
1964
            used_group8 = ''
1965
            for i in gcode_list_8:
1966
                if i == 0xFF:
1967
                    continue
1968
                used_group8 += str(i)[3]
1969
            group8_val = RadioSettingValueString(0, 7, used_group8)
1970
            rs = RadioSetting("group8", "8", group7_val)
1971
            group8_val.set_charset(dtmfcharsani)
1972
            dtmf.append(rs)
1973

    
1974
            scode_list = self._memobj.startcode.scode
1975
            used_scode = ''
1976
            for i in scode_list:
1977
                if i == 0xFF:
1978
                    continue
1979
                used_scode += str(i)[3]
1980
            scode_val = RadioSettingValueString(0, 7, used_scode)
1981
            rs = RadioSetting("scode", "PTT ID Starting(BOT)", scode_val)
1982
            scode_val.set_charset(dtmfcharsani)
1983
            dtmf.append(rs)
1984

    
1985
            ecode_list = self._memobj.endcode.ecode
1986
            used_ecode = ''
1987
            for i in ecode_list:
1988
                if i == 0xFF:
1989
                    continue
1990
                used_ecode += str(i)[3]
1991
            ecode_val = RadioSettingValueString(0, 7, used_ecode)
1992
            rs = RadioSetting("ecode", "PTT ID Ending(BOT)", ecode_val)
1993
            dtmf.append(rs)
1994
            # H3
1995
            if self.MODEL not in H8_LIST:
1996
                # stuncode
1997
                ecode_list = self._memobj.skcode.stuncode
1998
                used_ecode = ''
1999
                for i in ecode_list:
2000
                    if i == 0xFF:
2001
                        continue
2002
                    used_ecode += str(i)[3]
2003
                ecode_val = RadioSettingValueString(0, 16, used_ecode)
2004
                rs = RadioSetting("stuncode", "Stun Code", ecode_val)
2005
                dtmf.append(rs)
2006
                # killcode
2007
                ecode_list = self._memobj.skcode.killcode
2008
                used_ecode = ''
2009
                for i in ecode_list:
2010
                    if i == 0xFF:
2011
                        continue
2012
                    used_ecode += str(i)[3]
2013
                ecode_val = RadioSettingValueString(0, 16, used_ecode)
2014
                rs = RadioSetting("killcode", "Kill Code", ecode_val)
2015
                dtmf.append(rs)
2016

    
2017
        return group
2018

    
2019
    def get_settings(self):
2020
        try:
2021
            return self._get_settings()
2022
        except Exception:
2023
            raise InvalidValueError("Setting Failed!")
2024

    
2025
    def set_settings(self, settings):
2026

    
2027
        def fm_validate(value):
2028
            if 760 > value or value > 1080:
2029
                msg = ("FM Channel must be between 76.0-108.0")
2030
                raise InvalidValueError(msg)
2031

    
2032
        _settings = self._memobj.settings
2033
        _press = self._memobj.press
2034
        _aoffset = self._memobj.aoffset
2035
        _boffset = self._memobj.boffset
2036
        _vfoa = self._memobj.vfoa
2037
        _vfob = self._memobj.vfob
2038
        _fmmode = self._memobj.fmmode
2039

    
2040
        for element in settings:
2041
            if not isinstance(element, RadioSetting):
2042
                if element.get_name() == "fm_preset":
2043
                    self._set_fm_preset(element)
2044
                else:
2045
                    self.set_settings(element)
2046
                    continue
2047
            else:
2048
                try:
2049
                    name = element.get_name()
2050
                    if "." in name:
2051
                        bits = name.split(".")
2052
                        obj = self._memobj
2053
                        for bit in bits[:-1]:
2054
                            if "/" in bit:
2055
                                bit, index = bit.split("/", 1)
2056
                                index = int(index)
2057
                                obj = getattr(obj, bit)[index]
2058
                            else:
2059
                                obj = getattr(obj, bit)
2060
                        setting = bits[-1]
2061
                    elif name in PRESS_NAME:
2062
                        obj = _press
2063
                        setting = element.get_name()
2064

    
2065
                    elif name in VFOA_NAME:
2066
                        obj = _vfoa
2067
                        setting = element.get_name()
2068
                    elif name == "ofseta":
2069
                        obj = _aoffset
2070
                        setting = element.get_name()
2071
                    elif name in VFOB_NAME:
2072
                        obj = _vfob
2073
                        setting = element.get_name()
2074
                    elif name == "ofsetb":
2075
                        obj = _boffset
2076
                        setting = element.get_name()
2077
                    elif "block" in name:
2078
                        obj = _fmmode
2079
                        setting = element.get_name()
2080
                    elif "fmvfo" in name:
2081
                        obj = self._memobj.fmvfo
2082
                        setting = element.get_name()
2083
                    elif "gcode" in name:
2084
                        obj = self._memobj.groupcode.gcode
2085
                        setting = element.get_name()
2086
                    elif "idcode" in name:
2087
                        obj = self._memobj.icode.idcode
2088
                        setting = element.get_name()
2089
                    elif "scode" in name:
2090
                        obj = self._memobj.startcode.scode
2091
                        setting = element.get_name()
2092
                    elif "ecode" in name:
2093
                        obj = self._memobj.endcode.ecode
2094
                        setting = element.get_name()
2095
                    elif "group1" in name:
2096
                        obj = self._memobj.group1.group1
2097
                        setting = element.get_name()
2098
                    elif "group2" in name:
2099
                        obj = self._memobj.group2.group2
2100
                        setting = element.get_name()
2101
                    elif "group3" in name:
2102
                        obj = self._memobj.group3.group3
2103
                        setting = element.get_name()
2104
                    elif "group4" in name:
2105
                        obj = self._memobj.group4.group4
2106
                        setting = element.get_name()
2107
                    elif "group5" in name:
2108
                        obj = self._memobj.group5.group5
2109
                        setting = element.get_name()
2110
                    elif "group6" in name:
2111
                        obj = self._memobj.group6.group6
2112
                        setting = element.get_name()
2113
                    elif "group7" in name:
2114
                        obj = self._memobj.group7.group7
2115
                        setting = element.get_name()
2116
                    elif "group8" in name:
2117
                        obj = self._memobj.group8.group8
2118
                        setting = element.get_name()
2119
                    elif "micgain" in name:
2120
                        obj = self._memobj.mic.micgain
2121
                        setting = element.get_name()
2122
                    elif "killcode" in name:
2123
                        obj = self._memobj.skcode.killcode
2124
                        setting = element.get_name()
2125
                    elif "stuncode" in name:
2126
                        obj = self._memobj.skcode.stuncode
2127
                        setting = element.get_name()
2128
                    else:
2129
                        obj = _settings
2130
                        setting = element.get_name()
2131
                    if element.has_apply_callback():
2132
                        LOG.debug("Using apply callback")
2133
                        element.run_apply_callback()
2134

    
2135
                    # Channel A
2136
                    elif setting == "rxfreqa" and element.value.get_mutable():
2137
                        val = int(str(element.value).replace(
2138
                            '.', '').ljust(8, '0'))
2139
                        if (val >= 13600000 and val <= 17400000) or \
2140
                                (val >= 40000000 and val <= 52000000):
2141
                            setattr(obj, setting, val)
2142
                        else:
2143
                            msg = (
2144
                                "Frequency must be between "
2145
                                "136.00000-174.00000 or 400.00000-520.00000")
2146
                            raise InvalidValueError(msg)
2147

    
2148
                    elif setting == "ofseta" and element.value.get_mutable():
2149
                        if '.' in str(element.value):
2150
                            val = str(element.value).replace(' ', '')
2151
                            if len(
2152
                                val[val.index(".") + 1:]
2153
                                ) >= 1 and int(val[val.index(".") + 1:]
2154
                                               ) != 0:
2155
                                val = '00' + val.replace('.', '')
2156
                            else:
2157
                                val = '0' + val.replace('.', '')
2158
                            val = val.ljust(8, '0')
2159
                            lenth_val = 0
2160
                            list_val = []
2161
                        else:
2162
                            val = '0' + str(element.value).replace(' ', '')
2163
                            val = val.ljust(8, '0')
2164
                            lenth_val = 0
2165
                            list_val = []
2166
                        if (int(val) >= 0 and int(val) <= 5999750):
2167
                            if int(val) == 0:
2168
                                _aoffset.ofseta = [0xFF, 0xFF, 0xFF, 0xFF]
2169
                            else:
2170
                                while lenth_val < (len(val)):
2171
                                    list_val.insert(
2172
                                        0, val[lenth_val:lenth_val + 2])
2173
                                    lenth_val += 2
2174
                                for i in range(len(list_val)):
2175
                                    list_val[i] = int(list_val[i], 16)
2176
                                _aoffset.ofseta = list_val
2177
                        else:
2178
                            msg = ("Offset must be between 0.00000-59.99750")
2179
                            raise InvalidValueError(msg)
2180

    
2181
                    # B channel
2182
                    elif setting == "rxfreqb" and element.value.get_mutable():
2183
                        val = 0
2184
                        val = int(str(element.value).replace(
2185
                            '.', '').ljust(8, '0'))
2186
                        if (val >= 13600000 and val <= 17400000) or \
2187
                                (val >= 40000000 and val <= 52000000):
2188
                            setattr(obj, setting, val)
2189
                        else:
2190
                            msg = (
2191
                                "Frequency must be between "
2192
                                "136.00000-174.00000 or 400.00000-520.00000")
2193
                            raise InvalidValueError(msg)
2194
                        # setattr(obj, setting, val)
2195

    
2196
                    elif setting == "ofsetb" and element.value.get_mutable():
2197
                        if '.' in str(element.value):
2198
                            val = str(element.value).replace(' ', '')
2199
                            if len(val[val.index(".") + 1:]
2200
                                   ) >= 1 and int(val[val.index(".") + 1:]
2201
                                                  ) != 0:
2202
                                val = '00' + \
2203
                                    str(element.value).replace('.', '')
2204
                            else:
2205
                                val = '0' + str(element.value).replace('.', '')
2206
                            val = val.ljust(8, '0')
2207
                            lenth_val = 0
2208
                            list_val = []
2209
                        else:
2210
                            val = '0' + str(element.value).replace(' ', '')
2211
                            val = val.ljust(8, '0')
2212
                            lenth_val = 0
2213
                            list_val = []
2214
                        if (int(val) >= 0 and int(val) <= 5999750):
2215
                            if int(val) == 0:
2216
                                _boffset.ofsetb = [0xFF, 0xFF, 0xFF, 0xFF]
2217
                            else:
2218
                                while lenth_val < (len(val)):
2219
                                    list_val.insert(
2220
                                        0, val[lenth_val:lenth_val + 2])
2221
                                    lenth_val += 2
2222
                                for i in range(len(list_val)):
2223
                                    list_val[i] = int(list_val[i], 16)
2224
                                _boffset.ofsetb = list_val
2225
                        else:
2226
                            msg = ("Offset must be between 0.00000-59.99750")
2227
                            raise InvalidValueError(msg)
2228

    
2229
                    # FM
2230
                    elif "block" in name:
2231
                        num = int(name[-2:], 10)
2232
                        val = str(element.value)
2233
                        if val.strip():
2234
                            try:
2235
                                val = int(float(val) * 10)
2236
                            except ValueError:
2237
                                raise InvalidValueError(
2238
                                    'Value must be between 76.0-108.0')
2239
                            fm_validate(val)
2240
                        else:
2241
                            val = 0
2242
                        self._memobj.fmmode[num].fmblock = val
2243
                        self._memobj.fmusedflags[num] = bool(val)
2244

    
2245
                    elif setting == 'fmvfo' and element.value.get_mutable():
2246
                        self._memobj.fmvfo = int(element.value * 10)
2247

    
2248
                    elif setting == 'gcode' and element.value.get_mutable():
2249
                        val = str(element.value)
2250
                        if val == 'Off':
2251
                            gcode_used = 0xFF
2252
                        elif val == 'A':
2253
                            gcode_used = 0x0A
2254
                        elif val == 'B':
2255
                            gcode_used = 0x0B
2256
                        elif val == 'C':
2257
                            gcode_used = 0x0C
2258
                        elif val == 'D':
2259
                            gcode_used = 0x0D
2260
                        elif val == '#':
2261
                            gcode_used = 0x0F
2262
                        elif val == '*':
2263
                            gcode_used = 0x0E
2264
                        elif val == '':
2265
                            gcode_used = 0x00
2266
                        self._memobj.groupcode.gcode = gcode_used
2267

    
2268
                    elif setting == 'icode' and element.value.get_mutable():
2269
                        val = str(element.value)
2270
                        list_val = []
2271
                        lenth_val = 0
2272
                        while lenth_val < (len(val)):
2273
                            if val[lenth_val] != ' ':
2274
                                list_val.append(int(val[lenth_val], 16))
2275
                                lenth_val += 1
2276
                            else:
2277
                                list_val.append(0xFF)
2278
                                lenth_val += 1
2279
                        self._memobj.icode.idcode = list_val
2280

    
2281
                    elif setting == 'scode' and element.value.get_mutable():
2282
                        val = str(element.value)
2283
                        list_val = []
2284
                        lenth_val = 0
2285
                        while lenth_val < (len(val)):
2286
                            if val[lenth_val] != ' ':
2287
                                list_val.append(int(val[lenth_val], 16))
2288
                                lenth_val += 1
2289
                            else:
2290
                                list_val.append(0xFF)
2291
                                lenth_val += 1
2292
                        self._memobj.startcode.scode = list_val
2293

    
2294
                    elif setting == 'ecode' and element.value.get_mutable():
2295
                        val = str(element.value)
2296
                        list_val = []
2297
                        lenth_val = 0
2298
                        while lenth_val < (len(val)):
2299
                            if val[lenth_val] != ' ':
2300
                                list_val.append(int(val[lenth_val], 16))
2301
                                lenth_val += 1
2302
                            else:
2303
                                list_val.append(0xFF)
2304
                                lenth_val += 1
2305
                        self._memobj.endcode.ecode = list_val
2306

    
2307
                    elif setting == 'group1' and element.value.get_mutable():
2308
                        val = str(element.value)
2309
                        list_val = []
2310
                        lenth_val = 0
2311
                        while lenth_val < (len(val)):
2312
                            if val[lenth_val] != ' ':
2313
                                list_val.append(int(val[lenth_val], 16))
2314
                                lenth_val += 1
2315
                            else:
2316
                                list_val.append(0xFF)
2317
                                lenth_val += 1
2318
                        self._memobj.group1.group1 = list_val
2319

    
2320
                    elif setting == 'group2' and element.value.get_mutable():
2321
                        val = str(element.value)
2322
                        list_val = []
2323
                        lenth_val = 0
2324
                        while lenth_val < (len(val)):
2325
                            if val[lenth_val] != ' ':
2326
                                list_val.append(int(val[lenth_val], 16))
2327
                                lenth_val += 1
2328
                            else:
2329
                                list_val.append(0xFF)
2330
                                lenth_val += 1
2331
                        self._memobj.group2.group2 = list_val
2332

    
2333
                    elif setting == 'group3' and element.value.get_mutable():
2334
                        val = str(element.value)
2335
                        list_val = []
2336
                        lenth_val = 0
2337
                        while lenth_val < (len(val)):
2338
                            if val[lenth_val] != ' ':
2339
                                list_val.append(int(val[lenth_val], 16))
2340
                                lenth_val += 1
2341
                            else:
2342
                                list_val.append(0xFF)
2343
                                lenth_val += 1
2344
                        self._memobj.group3.group3 = list_val
2345

    
2346
                    elif setting == 'group4' and element.value.get_mutable():
2347
                        val = str(element.value)
2348
                        list_val = []
2349
                        lenth_val = 0
2350
                        while lenth_val < (len(val)):
2351
                            if val[lenth_val] != ' ':
2352
                                list_val.append(int(val[lenth_val], 16))
2353
                                lenth_val += 1
2354
                            else:
2355
                                list_val.append(0xFF)
2356
                                lenth_val += 1
2357
                        self._memobj.group4.group4 = list_val
2358

    
2359
                    elif setting == 'group5' and element.value.get_mutable():
2360
                        val = str(element.value)
2361
                        list_val = []
2362
                        lenth_val = 0
2363
                        while lenth_val < (len(val)):
2364
                            if val[lenth_val] != ' ':
2365
                                list_val.append(int(val[lenth_val], 16))
2366
                                lenth_val += 1
2367
                            else:
2368
                                list_val.append(0xFF)
2369
                                lenth_val += 1
2370
                        self._memobj.group5.group5 = list_val
2371

    
2372
                    elif setting == 'group6' and element.value.get_mutable():
2373
                        val = str(element.value)
2374
                        list_val = []
2375
                        lenth_val = 0
2376
                        while lenth_val < (len(val)):
2377
                            if val[lenth_val] != ' ':
2378
                                list_val.append(int(val[lenth_val], 16))
2379
                                lenth_val += 1
2380
                            else:
2381
                                list_val.append(0xFF)
2382
                                lenth_val += 1
2383
                        self._memobj.group6.group6 = list_val
2384

    
2385
                    elif setting == 'group7' and element.value.get_mutable():
2386
                        val = str(element.value)
2387
                        list_val = []
2388
                        lenth_val = 0
2389
                        while lenth_val < (len(val)):
2390
                            if val[lenth_val] != ' ':
2391
                                list_val.append(int(val[lenth_val], 16))
2392
                                lenth_val += 1
2393
                            else:
2394
                                list_val.append(0xFF)
2395
                                lenth_val += 1
2396
                        self._memobj.group7.group7 = list_val
2397

    
2398
                    elif setting == 'group8' and element.value.get_mutable():
2399
                        val = str(element.value)
2400
                        list_val = []
2401
                        lenth_val = 0
2402
                        while lenth_val < (len(val)):
2403
                            if val[lenth_val] != ' ':
2404
                                list_val.append(int(val[lenth_val], 16))
2405
                                lenth_val += 1
2406
                            else:
2407
                                list_val.append(0xFF)
2408
                                lenth_val += 1
2409
                        self._memobj.group8.group8 = list_val
2410
                    elif setting == 'micgain':
2411
                        self._memobj.mic.micgain = (
2412
                            str(element.value))
2413
                    elif setting == 'stuncode' and element.value.get_mutable():
2414
                        val = str(element.value)
2415
                        list_val = []
2416
                        lenth_val = 0
2417
                        while lenth_val < (len(val)):
2418
                            if val[lenth_val] != ' ':
2419
                                list_val.append(int(val[lenth_val], 16))
2420
                                lenth_val += 1
2421
                            else:
2422
                                list_val.append(0xFF)
2423
                                lenth_val += 1
2424
                        self._memobj.skcode.stuncode = list_val
2425
                    elif setting == 'killcode' and element.value.get_mutable():
2426
                        val = str(element.value)
2427
                        list_val = []
2428
                        lenth_val = 0
2429
                        while lenth_val < (len(val)):
2430
                            if val[lenth_val] != ' ':
2431
                                list_val.append(int(val[lenth_val], 16))
2432
                                lenth_val += 1
2433
                            else:
2434
                                list_val.append(0xFF)
2435
                                lenth_val += 1
2436
                        self._memobj.skcode.killcode = list_val
2437
                    elif element.value.get_mutable():
2438
                        setattr(obj, setting, element.value)
2439
                except Exception:
2440
                    LOG.debug(element.get_name())
2441
                    raise
2442

    
2443
    def _set_fm_preset(self, settings):
2444
        for element in settings:
2445
            try:
2446
                val = element.value
2447
                if self._memobj.fm_presets <= 108.0 * 10 - 650:
2448
                    value = int(val.get_value() * 10 - 650)
2449
                else:
2450
                    value = int(val.get_value() * 10)
2451
                LOG.debug("Setting fm_presets = %s" % (value))
2452
                self._memobj.fm_presets = value
2453
            except Exception:
2454
                LOG.debug(element.get_name())
2455
                raise
2456

    
2457

    
2458
@directory.register
2459
@directory.detected_by(TDH8)
2460
class TDH8_HAM(TDH8):
2461
    VENDOR = "TIDRADIO"
2462
    MODEL = "TD-H8-HAM"
2463
    ident_mode = b'P31185\xff\xff'
2464
    _ham = True
2465
    _txbands = [(144000000, 149000000), (420000000, 451000000)]
2466

    
2467

    
2468
@directory.register
2469
@directory.detected_by(TDH8)
2470
class TDH8_GMRS(TDH8):
2471
    VENDOR = "TIDRADIO"
2472
    MODEL = "TD-H8-GMRS"
2473
    ident_mode = b'P31184\xff\xff'
2474
    _gmrs = True
2475
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
2476
    _tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
2477
                 chirp_common.PowerLevel("High", watts=8.00)]
2478

    
2479
    def validate_memory(self, mem):
2480
        msgs = super().validate_memory(mem)
2481
        if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
2482
            msgs.append(chirp_common.ValidationError(
2483
                "The frequency in channels 31-54 must be between"
2484
                "462.55000-462.72500 in 0.025 increments."))
2485
        return msgs
2486

    
2487

    
2488
@directory.register
2489
class UV68(TDH8):
2490
    VENDOR = "TID"
2491
    MODEL = "TD-UV68"
2492

    
2493

    
2494
@directory.register
2495
class TDH3(TDH8):
2496
    VENDOR = "TIDRADIO"
2497
    MODEL = "TD-H3"
2498
    _memsize = 0x1fef
2499
    _ranges_main = [(0x0000, 0x1fef)]
2500
    _idents = [TD_H3]
2501
    _txbands = [(136000000, 600000000)]
2502
    _rxbands = [(50000000, 107999000), (108000000, 136000000)]
2503
    _aux_block = True
2504
    _tri_power = True
2505
    _gmrs = False
2506
    _ham = False
2507
    _mem_params = (0x1F2F)
2508
    _tx_power = [chirp_common.PowerLevel("Low",  watts=1.00),
2509
                 chirp_common.PowerLevel("High",  watts=4.00)]
2510

    
2511
    def process_mmap(self):
2512
        self._memobj = bitwise.parse(MEM_FORMAT_H3, self._mmap)
2513

    
2514

    
2515
@directory.register
2516
@directory.detected_by(TDH3)
2517
class TDH3_HAM(TDH3):
2518
    VENDOR = "TIDRADIO"
2519
    MODEL = "TD-H3-HAM"
2520
    ident_mode = b'P31185\xff\xff'
2521
    _ham = True
2522
    _txbands = [(144000000, 149000000), (420000000, 451000000)]
2523

    
2524

    
2525
@directory.register
2526
@directory.detected_by(TDH3)
2527
class TDH3_GMRS(TDH3):
2528
    VENDOR = "TIDRADIO"
2529
    MODEL = "TD-H3-GMRS"
2530
    ident_mode = b'P31184\xff\xff'
2531
    _gmrs = True
2532
    _txbands = [(136000000, 175000000), (400000000, 521000000)]
2533

    
2534
    def validate_memory(self, mem):
2535
        msgs = super().validate_memory(mem)
2536
        if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
2537
            msgs.append(chirp_common.ValidationError(
2538
                "The frequency in channels 31-54 must be between"
2539
                "462.55000-462.72500 in 0.025 increments."))
2540
        return msgs
2541

    
2542

    
2543
@directory.register
2544
class RT730(TDH8):
2545
    VENDOR = "Radtel"
2546
    MODEL = "RT-730"
2547
    _memsize = 0x6400
2548
    _ranges_main = [(0x0000, 0x6400)]
2549
    _idents = [RT_730]
2550
    _txbands = [(136000000, 174000000), (174000000, 300000000),
2551
                (300000000, 400000000), (400000000, 520000000),
2552
                (520000000, 630000000)]
2553
    _rxbands = [(10000000, 108000000), (108000000, 136000000)]
2554
    _tri_power = True
2555
    _gmrs = False
2556
    _ham = False
2557

    
2558
    def process_mmap(self):
2559
        self._memobj = bitwise.parse(MEM_FORMAT_RT730, self._mmap)
(3-3/3)