1
|
# Copyright 2012 Dan Smith <dsmith@danplanet.com>
|
2
|
#
|
3
|
# This program is free software: you can redistribute it and/or modify
|
4
|
# it under the terms of the GNU General Public License as published by
|
5
|
# the Free Software Foundation, either version 2 of the License, or
|
6
|
# (at your option) any later version.
|
7
|
#
|
8
|
# This program is distributed in the hope that it will be useful,
|
9
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
# GNU General Public License for more details.
|
12
|
#
|
13
|
# You should have received a copy of the GNU General Public License
|
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
|
16
|
import struct
|
17
|
import time
|
18
|
import logging
|
19
|
|
20
|
|
21
|
from chirp import chirp_common, errors, util, directory, memmap
|
22
|
from chirp import bitwise
|
23
|
from chirp.settings import InvalidValueError, RadioSetting, \
|
24
|
RadioSettingGroup, RadioSettingValueFloat, \
|
25
|
RadioSettingValueList, RadioSettingValueBoolean, \
|
26
|
RadioSettingValueString, RadioSettings
|
27
|
from textwrap import dedent
|
28
|
from chirp import bandplan_na
|
29
|
|
30
|
LOG = logging.getLogger(__name__)
|
31
|
|
32
|
AIRBAND = (108000000, 135999999)
|
33
|
MEM_FORMAT = """
|
34
|
#seekto 0x0008;
|
35
|
struct {
|
36
|
lbcd rxfreq[4];
|
37
|
lbcd txfreq[4];
|
38
|
lbcd rxtone[2];
|
39
|
lbcd txtone[2];
|
40
|
u8 unused1;
|
41
|
u8 pttid:2,
|
42
|
freqhop:1,
|
43
|
unused3:1,
|
44
|
unused4:1,
|
45
|
bcl:1,
|
46
|
unused5:1,
|
47
|
unused2:1;
|
48
|
u8 unused6:1,
|
49
|
unused7:1,
|
50
|
lowpower:2,
|
51
|
wide:1,
|
52
|
unused8:1,
|
53
|
offset:2;
|
54
|
u8 unused10;
|
55
|
} memory[200];
|
56
|
|
57
|
#seekto 0x0CA8;
|
58
|
struct {
|
59
|
u8 txled:1,
|
60
|
rxled:1,
|
61
|
unused11:1,
|
62
|
ham:1,
|
63
|
gmrs:1,
|
64
|
unused14:1,
|
65
|
unused15:1,
|
66
|
pritx:1;
|
67
|
u8 scanmode:2,
|
68
|
unused16:1,
|
69
|
keyautolock:1,
|
70
|
unused17:1,
|
71
|
btnvoice:1,
|
72
|
unknown18:1,
|
73
|
voiceprompt:1;
|
74
|
u8 fmworkmode:1,
|
75
|
sync:1,
|
76
|
tonevoice:2,
|
77
|
fmrec:1,
|
78
|
mdfa:1,
|
79
|
aworkmode:2;
|
80
|
u8 ponmsg:2,
|
81
|
unused19:1,
|
82
|
mdfb:1,
|
83
|
unused20:1,
|
84
|
dbrx:1,
|
85
|
bworkmode:2;
|
86
|
u8 ablock;
|
87
|
u8 bblock;
|
88
|
u8 fmroad;
|
89
|
u8 unused21:1,
|
90
|
tailclean:1,
|
91
|
rogerprompt:1,
|
92
|
unused23:1,
|
93
|
unused24:1,
|
94
|
voxgain:3;
|
95
|
u8 astep:4,
|
96
|
bstep:4;
|
97
|
u8 squelch;
|
98
|
u8 tot;
|
99
|
u8 lang;
|
100
|
u8 save;
|
101
|
u8 ligcon;
|
102
|
u8 voxdelay;
|
103
|
u8 onlychmode:1,
|
104
|
unused:6,
|
105
|
alarm:1;
|
106
|
} settings;
|
107
|
|
108
|
//#seekto 0x0CB8;
|
109
|
struct {
|
110
|
u8 ofseta[4];
|
111
|
} aoffset;
|
112
|
|
113
|
//#seekto 0x0CBC;
|
114
|
struct {
|
115
|
u8 ofsetb[4];
|
116
|
} boffset;
|
117
|
|
118
|
#seekto 0x0CD8;
|
119
|
struct{
|
120
|
lbcd fmblock[4];
|
121
|
}fmmode[25];
|
122
|
|
123
|
#seekto 0x0D48;
|
124
|
struct {
|
125
|
char name[8];
|
126
|
u8 unknown2[8];
|
127
|
} names[200];
|
128
|
|
129
|
#seekto 0x1A08;
|
130
|
lbit usedflags[200];
|
131
|
|
132
|
#seekto 0x1a28;
|
133
|
lbit scanadd[200];
|
134
|
|
135
|
#seekto 0x1B38;
|
136
|
lbcd fmvfo[4];
|
137
|
|
138
|
#seekto 0x1B58;
|
139
|
struct {
|
140
|
lbcd rxfreqa[4];
|
141
|
lbcd txfreq[4];
|
142
|
u8 rxtone[2];
|
143
|
u8 txtone[2];
|
144
|
u8 unused1;
|
145
|
u8 pttid:2,
|
146
|
specialqta:1,
|
147
|
unused3:1,
|
148
|
unused4:1,
|
149
|
bcl:1,
|
150
|
unused5:1,
|
151
|
unused2:1;
|
152
|
u8 unused6:1,
|
153
|
unused7:1,
|
154
|
lowpower:2,
|
155
|
wide:1,
|
156
|
unused8:1,
|
157
|
offset:2;
|
158
|
u8 unused10;
|
159
|
} vfoa;
|
160
|
|
161
|
//#seekto 0x1B68;
|
162
|
struct {
|
163
|
lbcd rxfreqb[4];
|
164
|
lbcd txfreq[4];
|
165
|
u8 rxtoneb[2];
|
166
|
u8 txtone[2];
|
167
|
u8 unused1;
|
168
|
u8 pttid:2,
|
169
|
specialqtb:1,
|
170
|
unused3:1,
|
171
|
unused4:1,
|
172
|
bclb:1,
|
173
|
unused5:1,
|
174
|
unused2:1;
|
175
|
u8 unused6:1,
|
176
|
unused7:1,
|
177
|
lowpowerb:2,
|
178
|
wideb:1,
|
179
|
unused8:1,
|
180
|
offsetb:2;
|
181
|
u8 unused10;
|
182
|
} vfob;
|
183
|
|
184
|
//#seekto 0x1B78;
|
185
|
lbit fmusedflags[32];
|
186
|
|
187
|
#seekto 0x1c08;
|
188
|
struct {
|
189
|
char msg1[16];
|
190
|
char msg2[16];
|
191
|
char msg3[16];
|
192
|
} poweron_msg;
|
193
|
|
194
|
|
195
|
#seekto 0x1CC8;
|
196
|
struct{
|
197
|
u8 stopkey1;
|
198
|
u8 ssidekey1;
|
199
|
u8 ssidekey2;
|
200
|
u8 ltopkey2;
|
201
|
u8 lsidekey3;
|
202
|
u8 lsidekey4;
|
203
|
u8 unused25[10];
|
204
|
} press;
|
205
|
|
206
|
#seekto 0x1E28;
|
207
|
struct{
|
208
|
u8 idcode[3];
|
209
|
}icode;
|
210
|
|
211
|
#seekto 0x1E31;
|
212
|
struct{
|
213
|
u8 gcode;
|
214
|
}groupcode;
|
215
|
|
216
|
#seekto 0x1E38;
|
217
|
struct{
|
218
|
u8 group1[7];
|
219
|
}group1;
|
220
|
|
221
|
#seekto 0x1E48;
|
222
|
struct{
|
223
|
u8 group2[7];
|
224
|
}group2;
|
225
|
|
226
|
#seekto 0x1E58;
|
227
|
struct{
|
228
|
u8 group3[7];
|
229
|
}group3;
|
230
|
|
231
|
#seekto 0x1E68;
|
232
|
struct{
|
233
|
u8 group4[7];
|
234
|
}group4;
|
235
|
|
236
|
#seekto 0x1E78;
|
237
|
struct{
|
238
|
u8 group5[7];
|
239
|
}group5;
|
240
|
|
241
|
#seekto 0x1E88;
|
242
|
struct{
|
243
|
u8 group6[7];
|
244
|
}group6;
|
245
|
|
246
|
#seekto 0x1E98;
|
247
|
struct{
|
248
|
u8 group7[7];
|
249
|
}group7;
|
250
|
|
251
|
#seekto 0x1EA8;
|
252
|
struct{
|
253
|
u8 group8[7];
|
254
|
}group8;
|
255
|
|
256
|
#seekto 0x1EC8;
|
257
|
struct{
|
258
|
u8 scode[7];
|
259
|
}startcode;
|
260
|
|
261
|
#seekto 0x1ED8;
|
262
|
struct{
|
263
|
u8 ecode[7];
|
264
|
}endcode;
|
265
|
"""
|
266
|
|
267
|
MEM_FORMAT_H3 = """
|
268
|
#seekto 0x0008;
|
269
|
struct {
|
270
|
lbcd rxfreq[4];
|
271
|
lbcd txfreq[4];
|
272
|
lbcd rxtone[2];
|
273
|
lbcd txtone[2];
|
274
|
u8 unused1;
|
275
|
u8 pttid:2,
|
276
|
freqhop:1,
|
277
|
unused3:1,
|
278
|
unused4:1,
|
279
|
bcl:1,
|
280
|
unused5:1,
|
281
|
unused2:1;
|
282
|
u8 unused6:1,
|
283
|
scramble:1,
|
284
|
lowpower:2,
|
285
|
wide:1,
|
286
|
unused8:1,
|
287
|
offset:2;
|
288
|
u8 unused10;
|
289
|
} memory[200];
|
290
|
|
291
|
#seekto 0x0C98;
|
292
|
struct{
|
293
|
u8 stopkey1;
|
294
|
u8 ssidekey1;
|
295
|
u8 ssidekey2;
|
296
|
u8 ltopkey2;
|
297
|
u8 lsidekey3;
|
298
|
u8 lsidekey4;
|
299
|
} press;
|
300
|
|
301
|
#seekto 0x0CA8;
|
302
|
struct {
|
303
|
u8 txled:1,
|
304
|
rxled:1,
|
305
|
unused11:1,
|
306
|
ham:1,
|
307
|
gmrs:1,
|
308
|
unused14:1,
|
309
|
unused15:1,
|
310
|
pritx:1;
|
311
|
u8 scanmode:2,
|
312
|
unused16:1,
|
313
|
keyautolock:1,
|
314
|
unused17:1,
|
315
|
btnvoice:1,
|
316
|
unknown18:1,
|
317
|
voiceprompt:1;
|
318
|
u8 fmworkmode:1,
|
319
|
sync:1,
|
320
|
tonevoice:2,
|
321
|
fmrec:1,
|
322
|
mdfa:1,
|
323
|
aworkmode:2;
|
324
|
u8 ponmsg:2,
|
325
|
unused19:1,
|
326
|
mdfb:1,
|
327
|
unused20:1,
|
328
|
dbrx:1,
|
329
|
bworkmode:2;
|
330
|
u8 ablock;
|
331
|
u8 bblock;
|
332
|
u8 fmroad;
|
333
|
u8 unused21:1,
|
334
|
tailclean:1,
|
335
|
rogerprompt_:1,
|
336
|
kill:1,
|
337
|
stun:1,
|
338
|
voxgain:3;
|
339
|
u8 astep:4,
|
340
|
bstep:4;
|
341
|
u8 squelch;
|
342
|
u8 tot;
|
343
|
u8 rogerprompt:2,
|
344
|
unused11_4:4,
|
345
|
lang:1,
|
346
|
unused11_1:1;
|
347
|
u8 save;
|
348
|
u8 ligcon;
|
349
|
u8 voxdelay;
|
350
|
u8 onlychmode:1,
|
351
|
breathled:3,
|
352
|
unused:3,
|
353
|
alarm:1;
|
354
|
} settings;
|
355
|
|
356
|
//#seekto 0x0CB8;
|
357
|
struct {
|
358
|
u8 ofseta[4];
|
359
|
} aoffset;
|
360
|
|
361
|
//#seekto 0x0CBC;
|
362
|
struct {
|
363
|
u8 ofsetb[4];
|
364
|
} boffset;
|
365
|
|
366
|
#seekto 0x0CD8;
|
367
|
struct{
|
368
|
lbcd fmblock[4];
|
369
|
}fmmode[25];
|
370
|
|
371
|
#seekto 0x0D48;
|
372
|
struct {
|
373
|
char name[8];
|
374
|
} names[200];
|
375
|
|
376
|
#seekto 0x1808;
|
377
|
struct{
|
378
|
u8 stuncode[16];
|
379
|
u8 killcode[16];
|
380
|
}skcode;
|
381
|
|
382
|
//#seekto 0x1828;
|
383
|
struct{
|
384
|
u8 idcode[3];
|
385
|
}icode;
|
386
|
|
387
|
#seekto 0x1837;
|
388
|
struct{
|
389
|
u8 gcode;
|
390
|
}groupcode;
|
391
|
|
392
|
//#seekto 0x1838;
|
393
|
struct{
|
394
|
u8 group1[7];
|
395
|
}group1;
|
396
|
|
397
|
#seekto 0x1848;
|
398
|
struct{
|
399
|
u8 group2[7];
|
400
|
}group2;
|
401
|
|
402
|
#seekto 0x1858;
|
403
|
struct{
|
404
|
u8 group3[7];
|
405
|
}group3;
|
406
|
|
407
|
#seekto 0x1868;
|
408
|
struct{
|
409
|
u8 group4[7];
|
410
|
}group4;
|
411
|
|
412
|
#seekto 0x1878;
|
413
|
struct{
|
414
|
u8 group5[7];
|
415
|
}group5;
|
416
|
|
417
|
#seekto 0x1888;
|
418
|
struct{
|
419
|
u8 group6[7];
|
420
|
}group6;
|
421
|
|
422
|
#seekto 0x1898;
|
423
|
struct{
|
424
|
u8 group7[7];
|
425
|
}group7;
|
426
|
|
427
|
#seekto 0x18A8;
|
428
|
struct{
|
429
|
u8 group8[7];
|
430
|
}group8;
|
431
|
|
432
|
#seekto 0x18C8;
|
433
|
struct{
|
434
|
u8 scode[7];
|
435
|
}startcode;
|
436
|
|
437
|
#seekto 0x18D8;
|
438
|
struct{
|
439
|
u8 ecode[7];
|
440
|
}endcode;
|
441
|
|
442
|
#seekto 0x1908;
|
443
|
lbit usedflags[200];
|
444
|
|
445
|
#seekto 0x1928;
|
446
|
lbit scanadd[200];
|
447
|
|
448
|
#seekto 0x1948;
|
449
|
lbit fmusedflags[32];
|
450
|
|
451
|
#seekto 0x1958;
|
452
|
struct {
|
453
|
lbcd rxfreqa[4];
|
454
|
lbcd txfreq[4];
|
455
|
u8 rxtone[2];
|
456
|
u8 txtone[2];
|
457
|
u8 unused1;
|
458
|
u8 pttid:2,
|
459
|
specialqta:1,
|
460
|
unused3:1,
|
461
|
unused4:1,
|
462
|
bcl:1,
|
463
|
unused5:1,
|
464
|
unused2:1;
|
465
|
u8 unused6:1,
|
466
|
unused7:1,
|
467
|
lowpower:2,
|
468
|
wide:1,
|
469
|
unused8:1,
|
470
|
offset:2;
|
471
|
u8 unused10;
|
472
|
} vfoa;
|
473
|
|
474
|
//#seekto 0x1968;
|
475
|
struct {
|
476
|
lbcd rxfreqb[4];
|
477
|
lbcd txfreq[4];
|
478
|
u8 rxtoneb[2];
|
479
|
u8 txtone[2];
|
480
|
u8 unused1;
|
481
|
u8 pttid:2,
|
482
|
specialqtb:1,
|
483
|
unused3:1,
|
484
|
unused4:1,
|
485
|
bclb:1,
|
486
|
unused5:1,
|
487
|
unused2:1;
|
488
|
u8 unused6:1,
|
489
|
unused7:1,
|
490
|
lowpowerb:2,
|
491
|
wideb:1,
|
492
|
unused8:1,
|
493
|
offsetb:2;
|
494
|
u8 unused10;
|
495
|
} vfob;
|
496
|
|
497
|
//#seekto 0x1978;
|
498
|
lbcd fmvfo[4];
|
499
|
|
500
|
#seekto 0x1c08;
|
501
|
struct {
|
502
|
char msg1[16];
|
503
|
char msg2[16];
|
504
|
char msg3[16];
|
505
|
} poweron_msg;
|
506
|
|
507
|
#seekto 0x1f28;
|
508
|
struct{
|
509
|
u8 micgain;
|
510
|
} mic;
|
511
|
|
512
|
"""
|
513
|
|
514
|
# basic settings
|
515
|
SQUELCH = ['%s' % x for x in range(0, 10)]
|
516
|
LIGHT_LIST = ["CONT", "5s", "10s", "15s", "30s"]
|
517
|
VOICE_PRMPT_LIST = ["OFF", "ON"]
|
518
|
AUTOLOCK_LIST = ["OFF", "ON"]
|
519
|
TIME_OUT_LIST = ["OFF", "60s", "120s", "180s"]
|
520
|
MDFA_LIST = ["Frequency", "Name"]
|
521
|
MDFB_LIST = ["Frequency", "Name"]
|
522
|
SYNC_LIST = ["ON", "OFF"]
|
523
|
LANG_LIST = ["Chinese", "English"]
|
524
|
BTV_SAVER_LIST = ["OFF", "1:1", "1:2", "1:3", "1:4"]
|
525
|
DBRX_LIST = ["OFF", "ON"]
|
526
|
ASTEP_LIST = ["2.50K", "5.00K", "6.25K",
|
527
|
"10.00K", "12.00K", "25.00K", "50.00K"]
|
528
|
BSTEP_LIST = ["2.50K", "5.00K", "6.25K",
|
529
|
"10.00K", "12.00K", "25.00K", "50.00K"]
|
530
|
SCAN_MODE_LIST = ["TO", "CO", "SE"]
|
531
|
PRIO_LIST = ["Edit", "Busy"]
|
532
|
SHORT_KEY_LIST = ["None", "FM Radio", "Lamp", "Monitor",
|
533
|
"TONE", "Alarm", "Weather"]
|
534
|
LONG_KEY_LIST = ["None", "FM Radio", "Lamp",
|
535
|
"Monitor", "TONE", "Alarm", "Weather"]
|
536
|
BUSYLOCK_LIST = ["Off", "On"]
|
537
|
PRESS_NAME = ["stopkey1", "ssidekey1", "ssidekey2",
|
538
|
"ltopkey2", "lsidekey3", "lsidekey4"]
|
539
|
|
540
|
VFOA_NAME = ["rxfreqa",
|
541
|
"txfreq",
|
542
|
"rxtone",
|
543
|
"txtone",
|
544
|
"pttid",
|
545
|
"specialqta",
|
546
|
"bcl",
|
547
|
"lowpower",
|
548
|
"wide",
|
549
|
"offset"]
|
550
|
|
551
|
VFOB_NAME = ["rxfreqb",
|
552
|
"txfreq",
|
553
|
"rxtoneb",
|
554
|
"txtone",
|
555
|
"pttid",
|
556
|
"specialqtb",
|
557
|
"bclb",
|
558
|
"lowpowerb",
|
559
|
"wideb",
|
560
|
"offsetb"]
|
561
|
|
562
|
# KEY
|
563
|
VOX_GAIN = ["OFF", "1", "2", "3", "4", "5"]
|
564
|
VOX_DELAY = ["1.05s", "2.0s", "3.0s"]
|
565
|
PTTID_VALUES = ["Off", "BOT", "EOT", "BOTH"]
|
566
|
BCLOCK_VALUES = ["Off", "On"]
|
567
|
FREQHOP_VALUES = ["Off", "On"]
|
568
|
SCAN_VALUES = ["Del", "Add"]
|
569
|
|
570
|
# AB CHANNEL
|
571
|
A_OFFSET = ["Off", "-", "+"]
|
572
|
A_TX_POWER = ["Low", "Mid", "High"]
|
573
|
A_BAND = ["Wide", "Narrow"]
|
574
|
A_BUSYLOCK = ["Off", "On"]
|
575
|
A_SPEC_QTDQT = ["Off", "On"]
|
576
|
A_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
|
577
|
|
578
|
B_OFFSET = ["Off", "-", "+"]
|
579
|
B_TX_POWER = ["Low", "Mid", "High"]
|
580
|
B_BAND = ["Wide", "Narrow"]
|
581
|
B_BUSYLOCK = ["Off", "On"]
|
582
|
B_SPEC_QTDQT = ["Off", "On"]
|
583
|
B_WORKMODE = ["VFO", "VFO+CH", "CH Mode"]
|
584
|
|
585
|
# FM
|
586
|
FM_WORKMODE = ["CH", "VFO"]
|
587
|
FM_CHANNEL = ['%s' % x for x in range(0, 26)]
|
588
|
|
589
|
# DTMF
|
590
|
GROUPCODE = ["", "Off", "*", "#", "A", "B", "C", "D"]
|
591
|
|
592
|
AB_LIST = ["A", "B"]
|
593
|
ALMOD_LIST = ["Site", "Tone", "Code"]
|
594
|
BANDWIDTH_LIST = ["Wide", "Narrow"]
|
595
|
COLOR_LIST = ["Off", "Blue", "Orange", "Purple"]
|
596
|
DTMFSPEED_LIST = ["%s ms" % x for x in range(50, 2010, 10)]
|
597
|
DTMFST_LIST = ["OFF", "DT-ST", "ANI-ST", "DT+ANI"]
|
598
|
MODE_LIST = ["Channel", "Name", "Frequency"]
|
599
|
PONMSG_LIST = ["Full", "Message", "Icon"]
|
600
|
PTTID_LIST = ["Off", "BOT", "EOT", "Both"]
|
601
|
PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
|
602
|
RTONE_LIST = ["1000 Hz", "1450 Hz", "1750 Hz", "2100 Hz"]
|
603
|
RESUME_LIST = ["TO", "CO", "SE"]
|
604
|
ROGERRX_LIST = ["Off"] + AB_LIST
|
605
|
RPSTE_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
606
|
SAVE_LIST = ["Off", "1:1", "1:2", "1:3", "1:4"]
|
607
|
SCODE_LIST = ["%s" % x for x in range(1, 16)]
|
608
|
SHIFTD_LIST = ["Off", "+", "-"]
|
609
|
STEDELAY_LIST = ["OFF"] + ["%s ms" % x for x in range(100, 1100, 100)]
|
610
|
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
|
611
|
STEP_LIST = [str(x) for x in STEPS]
|
612
|
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 50.0]
|
613
|
STEP291_LIST = [str(x) for x in STEPS]
|
614
|
TDRAB_LIST = ["Off"] + AB_LIST
|
615
|
TDRCH_LIST = ["CH%s" % x for x in range(1, 129)]
|
616
|
TIMEOUT_LIST = ["%s sec" % x for x in range(15, 615, 15)] + \
|
617
|
["Off (if supported by radio)"]
|
618
|
VOICE_LIST = ["Off", "English", "Chinese"]
|
619
|
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
|
620
|
WORKMODE_LIST = ["Frequency", "Channel"]
|
621
|
# mic
|
622
|
MIC_GAIN_LIST = ['%s' % x for x in range(0, 10)]
|
623
|
MIC_GAIN_LIST_H8 = ['%s' % x for x in range(0, 33)]
|
624
|
H8_LIST = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS"]
|
625
|
|
626
|
GMRS_FREQS = bandplan_na.GMRS_HIRPT
|
627
|
|
628
|
NOAA_FREQS = [162550000, 162400000, 162475000, 162425000, 162450000,
|
629
|
162500000, 162525000, 161650000, 161775000, 161750000,
|
630
|
162000000]
|
631
|
|
632
|
HAM_GMRS_NAME = ["NOAA 1", "NOAA 2", "NOAA 3", "NOAA 4", "NOAA 5", "NOAA 6",
|
633
|
"NOAA 7", "NOAA 8", "NOAA 9", "NOAA 10", "NOAA 11"]
|
634
|
|
635
|
ALL_MODEL = ["TD-H8", "TD-H8-HAM", "TD-H8-GMRS", "TD-H3",
|
636
|
"TD-H3-HAM", "TD-H3-GMRS"]
|
637
|
|
638
|
TD_H8 = b"\x50\x56\x4F\x4A\x48\x1C\x14"
|
639
|
TD_H3 = b"\x50\x56\x4F\x4A\x48\x5C\x14"
|
640
|
|
641
|
|
642
|
def in_range(freq, ranges):
|
643
|
for lo, hi in ranges:
|
644
|
if lo <= freq <= hi:
|
645
|
return True
|
646
|
return False
|
647
|
|
648
|
|
649
|
def _do_status(radio, block):
|
650
|
status = chirp_common.Status()
|
651
|
status.msg = "Cloning"
|
652
|
status.cur = block
|
653
|
status.max = radio._memsize
|
654
|
radio.status_fn(status)
|
655
|
|
656
|
|
657
|
def _upper_band_from_data(data):
|
658
|
return data[0x03:0x04]
|
659
|
|
660
|
|
661
|
def _upper_band_from_image(radio):
|
662
|
return _upper_band_from_data(radio.get_mmap())
|
663
|
|
664
|
|
665
|
def _firmware_version_from_data(data, version_start, version_stop):
|
666
|
version_tag = data[version_start:version_stop]
|
667
|
return version_tag
|
668
|
|
669
|
|
670
|
def _firmware_version_from_image(radio):
|
671
|
version = _firmware_version_from_data(radio.get_mmap(),
|
672
|
radio._fw_ver_file_start,
|
673
|
radio._fw_ver_file_stop)
|
674
|
# LOG.debug("_firmware_version_from_image: " + util.hexprint(version))
|
675
|
return version
|
676
|
|
677
|
|
678
|
def _do_ident(radio, magic, secondack=True):
|
679
|
serial = radio.pipe
|
680
|
serial.timeout = 1
|
681
|
|
682
|
LOG.info("Sending Magic: %s" % util.hexprint(magic))
|
683
|
serial.write(magic)
|
684
|
ack = serial.read(1)
|
685
|
|
686
|
if ack != b"\x06":
|
687
|
if ack:
|
688
|
# LOG.debug(repr(ack))
|
689
|
pass
|
690
|
raise errors.RadioError("Radio did not respond")
|
691
|
|
692
|
serial.write(b"\x02")
|
693
|
|
694
|
response = b""
|
695
|
for i in range(1, 9):
|
696
|
byte = serial.read(1)
|
697
|
response += byte
|
698
|
if byte == b"\xDD":
|
699
|
break
|
700
|
|
701
|
if len(response) in [8, 12]:
|
702
|
# DEBUG
|
703
|
LOG.info("Valid response, got this:")
|
704
|
LOG.info(util.hexprint(response))
|
705
|
if len(response) == 12:
|
706
|
ident = response[0] + response[3] + response[5] + response[7:]
|
707
|
else:
|
708
|
ident = response
|
709
|
else:
|
710
|
# bad response
|
711
|
msg = "Unexpected response, got this:"
|
712
|
msg += util.hexprint(response)
|
713
|
LOG.debug(msg)
|
714
|
raise errors.RadioError("Unexpected response from radio.")
|
715
|
|
716
|
if secondack:
|
717
|
serial.write(b"\x06")
|
718
|
ack = serial.read(1)
|
719
|
if ack != b"\x06":
|
720
|
raise errors.RadioError("Radio refused clone")
|
721
|
|
722
|
return ident
|
723
|
|
724
|
|
725
|
def response_mode(mode):
|
726
|
data = mode
|
727
|
return data
|
728
|
|
729
|
|
730
|
def _read_block(radio, start, size):
|
731
|
serial = radio.pipe
|
732
|
|
733
|
cmd = struct.pack(">cHb", b'R', start, size)
|
734
|
expectedresponse = b"W" + cmd[1:]
|
735
|
|
736
|
try:
|
737
|
serial.write(cmd)
|
738
|
response = serial.read(5 + size)
|
739
|
if response[:4] != expectedresponse:
|
740
|
raise errors.RadioError("Error reading block %04x." % (start))
|
741
|
block_data = response[4:-1]
|
742
|
|
743
|
except Exception:
|
744
|
raise errors.RadioError("Failed to read block at %04x" % start)
|
745
|
|
746
|
return block_data
|
747
|
|
748
|
|
749
|
def _get_radio_firmware_version(radio):
|
750
|
if radio.MODEL in ALL_MODEL:
|
751
|
block = _read_block(radio, 0x1B40, 0x20)
|
752
|
version = block[0:6]
|
753
|
return version
|
754
|
|
755
|
|
756
|
IDENT_BLACKLIST = {
|
757
|
b"\x50\x56\x4F\x4A\x48\x1C\x14": "Radio identifies as TIDRADIO TD-H8",
|
758
|
}
|
759
|
|
760
|
|
761
|
def _ident_radio(radio):
|
762
|
for magic in radio._idents:
|
763
|
error = None
|
764
|
try:
|
765
|
data = _do_ident(radio, magic)
|
766
|
return data
|
767
|
except errors.RadioError as e:
|
768
|
error = e
|
769
|
time.sleep(2)
|
770
|
|
771
|
if error:
|
772
|
raise error
|
773
|
raise errors.RadioError("Radio did not respond")
|
774
|
|
775
|
|
776
|
def _do_download(radio):
|
777
|
data = _ident_radio(radio)
|
778
|
append_model = False
|
779
|
# HAM OR GMRS
|
780
|
# Determine the walkie-talkie mode
|
781
|
# TDH8 have three mode:ham, gmrs and normal
|
782
|
|
783
|
LOG.info("Radio mode is " + str(data)[2:8])
|
784
|
LOG.info("Chirp choose mode is " + str(data)[2:8])
|
785
|
# The Ham and GMRS modes are subclasses of this model TDH8.
|
786
|
# We compare the radio identification to the value of that class to
|
787
|
# make sure the user chose the model that matches
|
788
|
# the radio we're talking to right now. If they do not match,
|
789
|
# we refuse to talk to the radio until the user selects the correct model.
|
790
|
|
791
|
if radio.ident_mode == data:
|
792
|
LOG.info("Successful match.")
|
793
|
else:
|
794
|
msg = ("Model mismatch!")
|
795
|
raise errors.RadioError(msg)
|
796
|
|
797
|
# Main block
|
798
|
LOG.info("Downloading...")
|
799
|
|
800
|
for i in range(0, radio._memsize, 0x20):
|
801
|
block = _read_block(radio, i, 0x20)
|
802
|
data += block
|
803
|
_do_status(radio, i)
|
804
|
_do_status(radio, radio._memsize)
|
805
|
LOG.info("done.")
|
806
|
|
807
|
if append_model:
|
808
|
data += radio.MODEL.ljust(8)
|
809
|
|
810
|
return memmap.MemoryMapBytes(data)
|
811
|
|
812
|
|
813
|
def _exit_write_block(radio):
|
814
|
serial = radio.pipe
|
815
|
try:
|
816
|
serial.write(b"E")
|
817
|
|
818
|
except Exception:
|
819
|
raise errors.RadioError("Radio refused to exit programming mode")
|
820
|
|
821
|
|
822
|
def _write_block(radio, addr, data):
|
823
|
serial = radio.pipe
|
824
|
cmd = struct.pack(">cHb", b'W', addr, 0x20)
|
825
|
data = radio.get_mmap()[addr + 8: addr + 40]
|
826
|
# The checksum needs to be in the last
|
827
|
check_sum = bytes([sum(data) & 0xFF])
|
828
|
data += check_sum
|
829
|
used_data = cmd + data
|
830
|
serial.write(used_data)
|
831
|
|
832
|
ack = radio.pipe.read(1)
|
833
|
if ack != b"\x06":
|
834
|
raise errors.RadioError("Radio refused to accept block 0x%04x" % addr)
|
835
|
|
836
|
|
837
|
def _do_upload(radio):
|
838
|
data = _ident_radio(radio)
|
839
|
radio_version = _get_radio_firmware_version(radio)
|
840
|
LOG.info("Radio Version is %s" % repr(radio_version))
|
841
|
|
842
|
if radio.ident_mode == data:
|
843
|
LOG.info("Successful match.")
|
844
|
else:
|
845
|
msg = ("Model mismatch!")
|
846
|
raise errors.RadioError(msg)
|
847
|
|
848
|
# Main block
|
849
|
LOG.debug("Uploading...")
|
850
|
|
851
|
for start_addr, end_addr in radio._ranges_main:
|
852
|
for addr in range(start_addr, end_addr, 0x20):
|
853
|
_write_block(radio, addr, 0x20)
|
854
|
_do_status(radio, addr)
|
855
|
_exit_write_block(radio)
|
856
|
LOG.debug("Upload all done.")
|
857
|
|
858
|
|
859
|
TDH8_CHARSET = chirp_common.CHARSET_ALPHANUMERIC + \
|
860
|
"!@#$%^&*()+-=[]:\";'<>?,./"
|
861
|
|
862
|
|
863
|
@directory.register
|
864
|
class TDH8(chirp_common.CloneModeRadio):
|
865
|
"""TIDRADIO TD-H8"""
|
866
|
VENDOR = "TIDRADIO"
|
867
|
MODEL = "TD-H8"
|
868
|
ident_mode = b'P31183\xff\xff'
|
869
|
BAUD_RATE = 38400
|
870
|
NEEDS_COMPAT_SERIAL = False
|
871
|
_memsize = 0x1eef
|
872
|
_ranges_main = [(0x0000, 0x1eef)]
|
873
|
_idents = [TD_H8]
|
874
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
875
|
_rxbands = []
|
876
|
_aux_block = True
|
877
|
_tri_power = True
|
878
|
_gmrs = False
|
879
|
_ham = False
|
880
|
_mem_params = (0x1F2F)
|
881
|
|
882
|
# offset of fw version in image file
|
883
|
_fw_ver_file_start = 0x1838
|
884
|
_fw_ver_file_stop = 0x1846
|
885
|
_valid_chars = TDH8_CHARSET
|
886
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
887
|
chirp_common.PowerLevel("Mid", watts=4.00),
|
888
|
chirp_common.PowerLevel("High", watts=8.00)]
|
889
|
|
890
|
@classmethod
|
891
|
def get_prompts(cls):
|
892
|
rp = chirp_common.RadioPrompts()
|
893
|
rp.pre_download = (dedent("""\
|
894
|
1. Turn radio off.
|
895
|
2. Connect cable to mic/spkr connector.
|
896
|
3. Make sure connector is firmly connected.
|
897
|
4. Turn radio on (volume may need to be set at 100%).
|
898
|
5. Ensure that the radio is tuned to channel with no activity.
|
899
|
6. Click OK to download image from device."""))
|
900
|
rp.pre_upload = (dedent("""\
|
901
|
1. Turn radio off.
|
902
|
2. Connect cable to mic/spkr connector.
|
903
|
3. Make sure connector is firmly connected.
|
904
|
4. Turn radio on (volume may need to be set at 100%).
|
905
|
5. Ensure that the radio is tuned to channel with no activity.
|
906
|
6. Click OK to upload image to device."""))
|
907
|
return rp
|
908
|
|
909
|
def get_features(self):
|
910
|
rf = chirp_common.RadioFeatures()
|
911
|
rf.has_settings = True
|
912
|
rf.has_bank = False
|
913
|
rf.has_cross = True
|
914
|
rf.has_ctone = True
|
915
|
rf.has_rx_dtcs = True
|
916
|
rf.has_tuning_step = False
|
917
|
rf.has_ctone = True
|
918
|
rf.can_odd_split = True
|
919
|
rf.valid_name_length = 8
|
920
|
rf.valid_characters = self._valid_chars
|
921
|
rf.valid_skips = ["", "S"]
|
922
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
923
|
rf.valid_cross_modes = [
|
924
|
"Tone->Tone",
|
925
|
"DTCS->",
|
926
|
"->DTCS",
|
927
|
"Tone->DTCS",
|
928
|
"DTCS->Tone",
|
929
|
"->Tone",
|
930
|
"DTCS->DTCS"]
|
931
|
rf.valid_power_levels = [x for x in self._tx_power if x]
|
932
|
rf.valid_duplexes = ["", "-", "+", "split", "off"]
|
933
|
rf.valid_modes = ["FM", "NFM"]
|
934
|
rf.valid_tuning_steps = STEPS
|
935
|
|
936
|
rf.valid_bands = self._txbands + self._rxbands
|
937
|
rf.valid_bands.sort()
|
938
|
rf.memory_bounds = (1, 199)
|
939
|
return rf
|
940
|
|
941
|
def process_mmap(self):
|
942
|
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
943
|
|
944
|
def sync_in(self):
|
945
|
try:
|
946
|
self._mmap = _do_download(self)
|
947
|
self.process_mmap()
|
948
|
except Exception as e:
|
949
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
950
|
|
951
|
def sync_out(self):
|
952
|
try:
|
953
|
_do_upload(self)
|
954
|
except errors.RadioError:
|
955
|
raise
|
956
|
except Exception as e:
|
957
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
958
|
|
959
|
def get_raw_memory(self, number):
|
960
|
return repr(self._memobj.memory[number])
|
961
|
|
962
|
# Encoding processing
|
963
|
def _decode_tone(self, val):
|
964
|
if val == 16665 or val == 0:
|
965
|
return '', None, None
|
966
|
elif val >= 12000:
|
967
|
return 'DTCS', val - 12000, 'R'
|
968
|
elif val >= 8000:
|
969
|
return 'DTCS', val - 8000, 'N'
|
970
|
else:
|
971
|
return 'Tone', val / 10.0, None
|
972
|
|
973
|
# Decoding processing
|
974
|
def _encode_tone(self, memval, mode, value, pol):
|
975
|
if mode == "":
|
976
|
memval[0].set_raw(0xFF)
|
977
|
memval[1].set_raw(0xFF)
|
978
|
elif mode == 'Tone':
|
979
|
memval.set_value(int(value * 10))
|
980
|
|
981
|
elif mode == 'DTCS':
|
982
|
flag = 0x80 if pol == 'N' else 0xC0
|
983
|
memval.set_value(value)
|
984
|
memval[1].set_bits(flag)
|
985
|
else:
|
986
|
raise Exception("Internal error: invalid mode `%s'" % mode)
|
987
|
|
988
|
def _get_mem(self, number):
|
989
|
return self._memobj.memory[number]
|
990
|
|
991
|
def _get_nam(self, number):
|
992
|
return self._memobj.names[number - 1]
|
993
|
|
994
|
def _get_fm(self, number):
|
995
|
return self._memobj.fmmode[number]
|
996
|
|
997
|
def _get_get_scanvfo(self, number):
|
998
|
return self._memobj.fmvfo[number]
|
999
|
|
1000
|
def get_memory(self, number):
|
1001
|
_mem = self._get_mem(number)
|
1002
|
_nam = self._get_nam(number)
|
1003
|
mem = chirp_common.Memory()
|
1004
|
mem.number = number
|
1005
|
|
1006
|
if _mem.get_raw()[0] == 0xff:
|
1007
|
mem.empty = True
|
1008
|
return mem
|
1009
|
|
1010
|
# narrow and wide
|
1011
|
mem.mode = _mem.wide and "NFM" or "FM"
|
1012
|
|
1013
|
# power
|
1014
|
try:
|
1015
|
mem.power = self._tx_power[_mem.lowpower]
|
1016
|
if mem.power is None:
|
1017
|
# Gaps are basically missing power levels
|
1018
|
raise IndexError()
|
1019
|
except IndexError:
|
1020
|
LOG.error("Radio reported invalid power level %s (in %s)" %
|
1021
|
(_mem.lowpower, self._tx_power))
|
1022
|
mem.power = self._tx_power[0]
|
1023
|
|
1024
|
# Channel name
|
1025
|
for char in _nam.name:
|
1026
|
if "\x00" in str(char) or "\xFF" in str(char):
|
1027
|
char = ""
|
1028
|
mem.name += str(char)
|
1029
|
|
1030
|
mem.name = mem.name.rstrip()
|
1031
|
if self.ident_mode != b'P31183\xff\xff' and \
|
1032
|
(mem.number >= 189 and mem.number <= 199):
|
1033
|
mem.name = HAM_GMRS_NAME[mem.number - 200]
|
1034
|
|
1035
|
# tmode
|
1036
|
lin2 = int(_mem.rxtone)
|
1037
|
rxtone = self._decode_tone(lin2)
|
1038
|
|
1039
|
lin = int(_mem.txtone)
|
1040
|
txtone = self._decode_tone(lin)
|
1041
|
|
1042
|
if txtone[0] == "Tone" and not rxtone[0]:
|
1043
|
mem.tmode = "Tone"
|
1044
|
elif txtone[0] == rxtone[0] and txtone[0] == "Tone" \
|
1045
|
and mem.rtone == mem.ctone:
|
1046
|
mem.tmode = "TSQL"
|
1047
|
elif txtone[0] == rxtone[0] and txtone[0] == "DTCS" \
|
1048
|
and mem.dtcs == mem.rx_dtcs:
|
1049
|
mem.tmode = "DTCS"
|
1050
|
elif rxtone[0] or txtone[0]:
|
1051
|
mem.tmode = "Cross"
|
1052
|
mem.cross_mode = "%s->%s" % (txtone[0], rxtone[0])
|
1053
|
|
1054
|
chirp_common.split_tone_decode(mem, txtone, rxtone)
|
1055
|
|
1056
|
mem.skip = '' if self._memobj.scanadd[mem.number - 1] else 'S'
|
1057
|
|
1058
|
mem.freq = int(_mem.rxfreq) * 10
|
1059
|
if _mem.txfreq.get_raw() == b'\xff\xff\xff\xff':
|
1060
|
mem.offset = 0
|
1061
|
mem.duplex = 'off'
|
1062
|
else:
|
1063
|
chirp_common.split_to_offset(mem,
|
1064
|
int(_mem.rxfreq) * 10,
|
1065
|
int(_mem.txfreq) * 10)
|
1066
|
|
1067
|
if self._gmrs:
|
1068
|
# mem.duplex = ""
|
1069
|
# mem.offset = 0
|
1070
|
if mem.number >= 1 and mem.number <= 30:
|
1071
|
mem.immutable.append('freq')
|
1072
|
if mem.number >= 8 and mem.number <= 14:
|
1073
|
mem.mode = 'NFM'
|
1074
|
mem.power = self._tx_power[0]
|
1075
|
mem.immutable = ['freq', 'mode', 'power',
|
1076
|
'duplex', 'offset']
|
1077
|
elif mem.number >= 31 and mem.number <= 54:
|
1078
|
# mem.immutable = ['duplex', 'offset']
|
1079
|
mem.duplex = '+'
|
1080
|
mem.offset = 5000000
|
1081
|
elif mem.number >= 189 and mem.number <= 199:
|
1082
|
ham_freqs = NOAA_FREQS[mem.number - 189]
|
1083
|
mem.freq = ham_freqs
|
1084
|
mem.immutable = ['name', 'power', 'duplex', 'freq',
|
1085
|
'rx_dtcs', 'vfo', 'tmode', 'empty',
|
1086
|
'offset', 'rtone', 'ctone', 'dtcs',
|
1087
|
'dtcs_polarity', 'cross_mode']
|
1088
|
elif self._ham:
|
1089
|
if mem.number >= 189 and mem.number <= 199:
|
1090
|
ham_freqs = NOAA_FREQS[mem.number - 189]
|
1091
|
mem.freq = ham_freqs
|
1092
|
mem.immutable = ['name', 'power', 'freq', 'rx_dtcs', 'vfo',
|
1093
|
'tmode', 'empty', 'offset', 'rtone', 'ctone',
|
1094
|
'dtcs', 'dtcs_polarity', 'cross_mode']
|
1095
|
|
1096
|
# other function
|
1097
|
# pttid
|
1098
|
mem.extra = RadioSettingGroup("Extra", "extra")
|
1099
|
|
1100
|
rs = RadioSetting("pttid", "PTT ID",
|
1101
|
RadioSettingValueList(PTTID_VALUES,
|
1102
|
PTTID_VALUES[_mem.pttid]))
|
1103
|
mem.extra.append(rs)
|
1104
|
|
1105
|
# Busylock
|
1106
|
rs = RadioSetting("bcl", "Busy Lock",
|
1107
|
RadioSettingValueList(BCLOCK_VALUES,
|
1108
|
BCLOCK_VALUES[_mem.bcl]))
|
1109
|
mem.extra.append(rs)
|
1110
|
|
1111
|
rs = RadioSetting(
|
1112
|
"freqhop", "Frequency Hop", RadioSettingValueList(
|
1113
|
FREQHOP_VALUES, FREQHOP_VALUES[_mem.freqhop]))
|
1114
|
mem.extra.append(rs)
|
1115
|
|
1116
|
if in_range(mem.freq, self._rxbands):
|
1117
|
mem.duplex = 'off'
|
1118
|
mem.immutable.append('duplex')
|
1119
|
if in_range(mem.freq, [AIRBAND]):
|
1120
|
mem.mode = 'AM'
|
1121
|
mem.immutable.append('mode')
|
1122
|
|
1123
|
return mem
|
1124
|
|
1125
|
def _set_mem(self, number):
|
1126
|
return self._memobj.memory[number]
|
1127
|
|
1128
|
def _set_nam(self, number):
|
1129
|
return self._memobj.names[number - 1]
|
1130
|
|
1131
|
def _get_scan_list(self, scan_data):
|
1132
|
# scan_val_list - Get all scans Add data 1-200 digits
|
1133
|
scan_val_list = []
|
1134
|
for x in range(25):
|
1135
|
a = self._get_scan(x)
|
1136
|
for i in range(0, 8):
|
1137
|
scan_val = (getattr(a, 'scan%i' % (i+1)))
|
1138
|
used_scan_val = str(scan_val)[3]
|
1139
|
scan_val_list.append(used_scan_val)
|
1140
|
|
1141
|
# used_scan_list - 25 structures, split the scan added
|
1142
|
# data into 25 groups of 8 bits each
|
1143
|
used_scan_list = []
|
1144
|
count_num = 1
|
1145
|
for i in range(0, len(scan_val_list), 8):
|
1146
|
used_scan_list.append(scan_val_list[i:i + 8])
|
1147
|
count_num += 1
|
1148
|
# Determine whether it is a standard number that can be divisible
|
1149
|
# Which group is the scan addition located in the modified channel
|
1150
|
if scan_data % 8 != 0:
|
1151
|
x_list = scan_data / 8
|
1152
|
y_list = scan_data % 8
|
1153
|
|
1154
|
else:
|
1155
|
x_list = (scan_data / 8) - 1
|
1156
|
y_list = 8
|
1157
|
|
1158
|
return ([x_list, y_list])
|
1159
|
|
1160
|
def set_memory(self, mem):
|
1161
|
_mem = self._get_mem(mem.number)
|
1162
|
_nam = self._get_nam(mem.number)
|
1163
|
|
1164
|
# When the channel is empty, you need to set "usedflags" to 0,
|
1165
|
# When the channel is used , you need to set "usedflags" to 1.
|
1166
|
self._memobj.usedflags[mem.number - 1] = int(not mem.empty)
|
1167
|
|
1168
|
if mem.empty:
|
1169
|
_mem.fill_raw(b'\xFF')
|
1170
|
return
|
1171
|
|
1172
|
_mem.fill_raw(b'\x00')
|
1173
|
|
1174
|
if mem.duplex == "":
|
1175
|
_mem.rxfreq = _mem.txfreq = mem.freq / 10
|
1176
|
elif mem.duplex == "split":
|
1177
|
_mem.txfreq = mem.offset / 10
|
1178
|
elif mem.duplex == "+":
|
1179
|
_mem.txfreq = (mem.freq + mem.offset) / 10
|
1180
|
elif mem.duplex == "-":
|
1181
|
_mem.txfreq = (mem.freq - mem.offset) / 10
|
1182
|
elif mem.duplex == 'off':
|
1183
|
_mem.txfreq.fill_raw(b'\xFF')
|
1184
|
else:
|
1185
|
_mem.txfreq = mem.freq / 10
|
1186
|
|
1187
|
if in_range(mem.freq, self._rxbands):
|
1188
|
_mem.txfreq.fill_raw(b'\xFF')
|
1189
|
|
1190
|
_mem.rxfreq = mem.freq / 10
|
1191
|
_namelength = self.get_features().valid_name_length
|
1192
|
|
1193
|
for i in range(_namelength):
|
1194
|
try:
|
1195
|
_nam.name[i] = mem.name[i]
|
1196
|
except IndexError:
|
1197
|
_nam.name[i] = "\xFF"
|
1198
|
|
1199
|
txtone, rxtone = chirp_common.split_tone_encode(mem)
|
1200
|
|
1201
|
self._encode_tone(_mem.txtone, *txtone)
|
1202
|
self._encode_tone(_mem.rxtone, *rxtone)
|
1203
|
|
1204
|
if mem.mode == "FM":
|
1205
|
_mem.wide = 0
|
1206
|
else:
|
1207
|
_mem.wide = 1
|
1208
|
|
1209
|
try:
|
1210
|
_mem.lowpower = self._tx_power.index(mem.power or
|
1211
|
self._tx_power[-1])
|
1212
|
except ValueError:
|
1213
|
_mem.lowpower = 0
|
1214
|
LOG.warning('Unsupported power %r', mem.power)
|
1215
|
|
1216
|
# Skip/Scanadd Setting
|
1217
|
self._memobj.scanadd[mem.number - 1] = mem.skip != 'S'
|
1218
|
|
1219
|
for setting in mem.extra:
|
1220
|
if (self.ident_mode == b'P31185\xff\xff' or
|
1221
|
self.ident_mode == b'P31184\xff\xff') and \
|
1222
|
mem.number >= 189 and mem.number <= 199:
|
1223
|
if setting.get_name() == 'pttid':
|
1224
|
setting.value = 'Off'
|
1225
|
setattr(_mem, setting.get_name(), setting.value)
|
1226
|
elif setting.get_name() == 'bcl':
|
1227
|
setting.value = 'Off'
|
1228
|
setattr(_mem, setting.get_name(), setting.value)
|
1229
|
elif setting.get_name() == 'freqhop':
|
1230
|
setting.value = 'Off'
|
1231
|
setattr(_mem, setting.get_name(), setting.value)
|
1232
|
else:
|
1233
|
setattr(_mem, setting.get_name(), setting.value)
|
1234
|
|
1235
|
def _is_orig(self):
|
1236
|
version_tag = _firmware_version_from_image(self)
|
1237
|
try:
|
1238
|
if b'BFB' in version_tag:
|
1239
|
idx = version_tag.index(b"BFB") + 3
|
1240
|
version = int(version_tag[idx:idx + 3])
|
1241
|
return version < 291
|
1242
|
return False
|
1243
|
except Exception:
|
1244
|
pass
|
1245
|
raise errors.RadioError("Unable to parse version string %s" %
|
1246
|
version_tag)
|
1247
|
|
1248
|
def _my_upper_band(self):
|
1249
|
band_tag = _upper_band_from_image(self)
|
1250
|
return band_tag
|
1251
|
|
1252
|
def _get_settings(self):
|
1253
|
_settings = self._memobj.settings
|
1254
|
_press = self._memobj.press
|
1255
|
_aoffset = self._memobj.aoffset
|
1256
|
_boffset = self._memobj.boffset
|
1257
|
_vfoa = self._memobj.vfoa
|
1258
|
_vfob = self._memobj.vfob
|
1259
|
_gcode = self._memobj.groupcode
|
1260
|
_msg = self._memobj.poweron_msg
|
1261
|
basic = RadioSettingGroup("basic", "Basic Settings")
|
1262
|
abblock = RadioSettingGroup("abblock", "A/B Channel")
|
1263
|
fmmode = RadioSettingGroup("fmmode", "FM")
|
1264
|
dtmf = RadioSettingGroup("dtmf", "DTMF")
|
1265
|
|
1266
|
# group = RadioSettings(fmmode, dtmf)
|
1267
|
group = RadioSettings(basic, abblock, fmmode, dtmf)
|
1268
|
|
1269
|
rs = RadioSetting("squelch", "Squelch Level",
|
1270
|
RadioSettingValueList(
|
1271
|
SQUELCH, SQUELCH[_settings.squelch]))
|
1272
|
basic.append(rs)
|
1273
|
|
1274
|
rs = RadioSetting("ligcon", "Light Control",
|
1275
|
RadioSettingValueList(
|
1276
|
LIGHT_LIST, LIGHT_LIST[_settings.ligcon]))
|
1277
|
basic.append(rs)
|
1278
|
|
1279
|
rs = RadioSetting("voiceprompt", "Voice Prompt",
|
1280
|
RadioSettingValueList(
|
1281
|
VOICE_PRMPT_LIST, VOICE_PRMPT_LIST[
|
1282
|
_settings.voiceprompt]))
|
1283
|
basic.append(rs)
|
1284
|
|
1285
|
rs = RadioSetting("keyautolock", "Auto Lock",
|
1286
|
RadioSettingValueList(
|
1287
|
AUTOLOCK_LIST, AUTOLOCK_LIST[
|
1288
|
_settings.keyautolock]))
|
1289
|
basic.append(rs)
|
1290
|
|
1291
|
rs = RadioSetting("mdfa", "MDF-A",
|
1292
|
RadioSettingValueList(
|
1293
|
MDFA_LIST, MDFA_LIST[_settings.mdfa]))
|
1294
|
basic.append(rs)
|
1295
|
|
1296
|
rs = RadioSetting("mdfb", "MDF-B",
|
1297
|
RadioSettingValueList(
|
1298
|
MDFB_LIST, MDFB_LIST[_settings.mdfb]))
|
1299
|
basic.append(rs)
|
1300
|
|
1301
|
rs = RadioSetting("sync", "SYNC",
|
1302
|
RadioSettingValueList(
|
1303
|
SYNC_LIST, SYNC_LIST[_settings.sync]))
|
1304
|
basic.append(rs)
|
1305
|
|
1306
|
rs = RadioSetting("save", "Battery Save",
|
1307
|
RadioSettingValueList(
|
1308
|
BTV_SAVER_LIST, BTV_SAVER_LIST[_settings.save]))
|
1309
|
basic.append(rs)
|
1310
|
|
1311
|
rs = RadioSetting("dbrx", "Double Rx",
|
1312
|
RadioSettingValueList(
|
1313
|
DBRX_LIST, DBRX_LIST[_settings.dbrx]))
|
1314
|
basic.append(rs)
|
1315
|
|
1316
|
rs = RadioSetting("astep", "A Step",
|
1317
|
RadioSettingValueList(
|
1318
|
ASTEP_LIST, ASTEP_LIST[_settings.astep]))
|
1319
|
basic.append(rs)
|
1320
|
|
1321
|
rs = RadioSetting("bstep", "B Step",
|
1322
|
RadioSettingValueList(
|
1323
|
BSTEP_LIST, BSTEP_LIST[_settings.bstep]))
|
1324
|
basic.append(rs)
|
1325
|
|
1326
|
rs = RadioSetting("scanmode", "Scan Mode",
|
1327
|
RadioSettingValueList(
|
1328
|
SCAN_MODE_LIST, SCAN_MODE_LIST[
|
1329
|
_settings.scanmode]))
|
1330
|
basic.append(rs)
|
1331
|
|
1332
|
rs = RadioSetting("pritx", "Priority TX",
|
1333
|
RadioSettingValueList(
|
1334
|
PRIO_LIST, PRIO_LIST[_settings.pritx]))
|
1335
|
basic.append(rs)
|
1336
|
|
1337
|
rs = RadioSetting("btnvoice", "Beep",
|
1338
|
RadioSettingValueBoolean(_settings.btnvoice))
|
1339
|
basic.append(rs)
|
1340
|
|
1341
|
rs = RadioSetting("rogerprompt", "Roger",
|
1342
|
RadioSettingValueBoolean(_settings.rogerprompt))
|
1343
|
basic.append(rs)
|
1344
|
|
1345
|
rs = RadioSetting("txled", "Disp Lcd(TX)",
|
1346
|
|
1347
|
RadioSettingValueBoolean(_settings.txled))
|
1348
|
basic.append(rs)
|
1349
|
|
1350
|
rs = RadioSetting("rxled", "Disp Lcd(RX)",
|
1351
|
RadioSettingValueBoolean(_settings.rxled))
|
1352
|
basic.append(rs)
|
1353
|
|
1354
|
rs = RadioSetting("onlychmode", "Only CH Mode",
|
1355
|
RadioSettingValueBoolean(_settings.onlychmode))
|
1356
|
basic.append(rs)
|
1357
|
rs = RadioSetting("ssidekey1", "SHORT_KEY_PF1",
|
1358
|
RadioSettingValueList(
|
1359
|
SHORT_KEY_LIST, SHORT_KEY_LIST[
|
1360
|
_press.ssidekey1]))
|
1361
|
basic.append(rs)
|
1362
|
rs = RadioSetting("lsidekey3", "LONG_KEY_PF1",
|
1363
|
RadioSettingValueList(
|
1364
|
LONG_KEY_LIST,
|
1365
|
LONG_KEY_LIST[_press.lsidekey3]))
|
1366
|
basic.append(rs)
|
1367
|
if self.MODEL in H8_LIST:
|
1368
|
rs = RadioSetting("stopkey1", "SHORT_KEY_TOP",
|
1369
|
RadioSettingValueList(SHORT_KEY_LIST,
|
1370
|
SHORT_KEY_LIST[0]))
|
1371
|
basic.append(rs)
|
1372
|
|
1373
|
rs = RadioSetting("ltopkey2", "LONG_KEY_TOP",
|
1374
|
RadioSettingValueList(
|
1375
|
LONG_KEY_LIST,
|
1376
|
LONG_KEY_LIST[_press.ltopkey2]))
|
1377
|
basic.append(rs)
|
1378
|
|
1379
|
rs = RadioSetting("ssidekey2", "SHORT_KEY_PF2",
|
1380
|
RadioSettingValueList(
|
1381
|
SHORT_KEY_LIST,
|
1382
|
SHORT_KEY_LIST[_press.ssidekey2]))
|
1383
|
basic.append(rs)
|
1384
|
|
1385
|
rs = RadioSetting("lsidekey4", "LONG_KEY_PF2",
|
1386
|
RadioSettingValueList(
|
1387
|
LONG_KEY_LIST,
|
1388
|
LONG_KEY_LIST[_press.lsidekey4]))
|
1389
|
basic.append(rs)
|
1390
|
|
1391
|
rs = RadioSetting("voxgain", "VOX Gain",
|
1392
|
RadioSettingValueList(
|
1393
|
VOX_GAIN, VOX_GAIN[_settings.voxgain]))
|
1394
|
basic.append(rs)
|
1395
|
|
1396
|
rs = RadioSetting("voxdelay", "VOX Delay",
|
1397
|
RadioSettingValueList(
|
1398
|
VOX_DELAY, VOX_DELAY[_settings.voxdelay]))
|
1399
|
basic.append(rs)
|
1400
|
|
1401
|
rs = RadioSetting("ponmsg", "Power-On Message",
|
1402
|
RadioSettingValueList(
|
1403
|
PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
|
1404
|
basic.append(rs)
|
1405
|
|
1406
|
# mic gain
|
1407
|
if self.MODEL not in H8_LIST:
|
1408
|
_mic = self._memobj.mic
|
1409
|
rs = RadioSetting("micgain", "MIC GAIN",
|
1410
|
RadioSettingValueList(
|
1411
|
MIC_GAIN_LIST, current_index=_mic.micgain))
|
1412
|
basic.append(rs)
|
1413
|
|
1414
|
if self.MODEL not in H8_LIST:
|
1415
|
rs = RadioSetting("kill", "Kill",
|
1416
|
RadioSettingValueBoolean(_settings.kill))
|
1417
|
basic.append(rs)
|
1418
|
rs = RadioSetting("stun", "Stun",
|
1419
|
RadioSettingValueBoolean(_settings.stun))
|
1420
|
basic.append(rs)
|
1421
|
|
1422
|
def _filter(name):
|
1423
|
filtered = ""
|
1424
|
for char in str(name):
|
1425
|
if char in chirp_common.CHARSET_ASCII:
|
1426
|
filtered += char
|
1427
|
else:
|
1428
|
filtered += " "
|
1429
|
return filtered
|
1430
|
|
1431
|
rs = RadioSetting("poweron_msg.msg1", "Power-On Message 1",
|
1432
|
RadioSettingValueString(0, 16, _filter(_msg.msg1)))
|
1433
|
basic.append(rs)
|
1434
|
rs = RadioSetting("poweron_msg.msg2", "Power-On Message 2",
|
1435
|
RadioSettingValueString(0, 16, _filter(_msg.msg2)))
|
1436
|
basic.append(rs)
|
1437
|
rs = RadioSetting("poweron_msg.msg3", "Power-On Message 3",
|
1438
|
RadioSettingValueString(0, 16, _filter(_msg.msg3)))
|
1439
|
basic.append(rs)
|
1440
|
|
1441
|
# A channel
|
1442
|
a_freq = int(_vfoa.rxfreqa)
|
1443
|
freqa = "%i.%05i" % (a_freq / 100000, a_freq % 100000)
|
1444
|
if freqa == "0.00000":
|
1445
|
val1a = RadioSettingValueString(0, 7, '0.00000')
|
1446
|
else:
|
1447
|
val1a = RadioSettingValueFloat(
|
1448
|
136, 520, float(freqa), 0.00001, 5)
|
1449
|
rs = RadioSetting("rxfreqa", "A Channel - Frequency", val1a)
|
1450
|
abblock.append(rs)
|
1451
|
|
1452
|
# Offset
|
1453
|
# If the offset is 12.345
|
1454
|
# Then the data obtained is [0x45, 0x23, 0x01, 0x00]
|
1455
|
a_set_val = _aoffset.ofseta
|
1456
|
a_set_list = len(_aoffset.ofseta) - 1
|
1457
|
real_val = ''
|
1458
|
for i in range(a_set_list, -1, -1):
|
1459
|
real_val += str(a_set_val[i])[2:]
|
1460
|
if real_val == "FFFFFFFF":
|
1461
|
rs = RadioSetting("ofseta", "A Offset Frequency",
|
1462
|
RadioSettingValueString(0, 7, ""))
|
1463
|
|
1464
|
else:
|
1465
|
real_val = int(real_val)
|
1466
|
real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
|
1467
|
rs = RadioSetting("ofseta", "A Offset Frequency",
|
1468
|
RadioSettingValueFloat(
|
1469
|
0.00000, 59.99750, real_val, 0.00001, 5))
|
1470
|
abblock.append(rs)
|
1471
|
|
1472
|
rs = RadioSetting("offset", "A Offset",
|
1473
|
RadioSettingValueList(
|
1474
|
A_OFFSET, A_OFFSET[_vfoa.offset]))
|
1475
|
abblock.append(rs)
|
1476
|
|
1477
|
rs = RadioSetting("lowpower", "A TX Power",
|
1478
|
RadioSettingValueList(
|
1479
|
A_TX_POWER, A_TX_POWER[_vfoa.lowpower]))
|
1480
|
abblock.append(rs)
|
1481
|
|
1482
|
rs = RadioSetting("wide", "A Band",
|
1483
|
RadioSettingValueList(
|
1484
|
A_BAND, A_BAND[_vfoa.wide]))
|
1485
|
abblock.append(rs)
|
1486
|
|
1487
|
rs = RadioSetting("bcl", "A Busy Lock",
|
1488
|
RadioSettingValueList(
|
1489
|
A_BUSYLOCK, A_BUSYLOCK[_vfoa.bcl]))
|
1490
|
abblock.append(rs)
|
1491
|
|
1492
|
rs = RadioSetting("specialqta", "A Special QT/DQT",
|
1493
|
RadioSettingValueList(
|
1494
|
A_SPEC_QTDQT, A_SPEC_QTDQT[_vfoa.specialqta]))
|
1495
|
abblock.append(rs)
|
1496
|
|
1497
|
rs = RadioSetting("aworkmode", "A Work Mode",
|
1498
|
RadioSettingValueList(
|
1499
|
A_WORKMODE, A_WORKMODE[_settings.aworkmode]))
|
1500
|
abblock.append(rs)
|
1501
|
|
1502
|
# B channel
|
1503
|
b_freq = int(str(int(_vfob.rxfreqb)).ljust(8, '0'))
|
1504
|
freqb = "%i.%05i" % (b_freq / 100000, b_freq % 100000)
|
1505
|
if freqb == "0.00000":
|
1506
|
val1a = RadioSettingValueString(0, 7, '0.00000')
|
1507
|
else:
|
1508
|
val1a = RadioSettingValueFloat(
|
1509
|
136, 520, float(freqb), 0.00001, 5)
|
1510
|
rs = RadioSetting("rxfreqb", "B Channel - Frequency", val1a)
|
1511
|
abblock.append(rs)
|
1512
|
|
1513
|
# Offset frequency
|
1514
|
# If the offset is 12.345
|
1515
|
# Then the data obtained is [0x45, 0x23, 0x01, 0x00]
|
1516
|
# Need to use the following anonymous function to process data
|
1517
|
b_set_val = _boffset.ofsetb
|
1518
|
b_set_list = len(_boffset.ofsetb) - 1
|
1519
|
real_val = ''
|
1520
|
for i in range(b_set_list, -1, -1):
|
1521
|
real_val += str(b_set_val[i])[2:]
|
1522
|
if real_val == "FFFFFFFF":
|
1523
|
rs = RadioSetting("ofsetb", "B Offset Frequency",
|
1524
|
RadioSettingValueString(0, 7, " "))
|
1525
|
else:
|
1526
|
real_val = int(real_val)
|
1527
|
real_val = "%i.%05i" % (real_val / 100000, real_val % 100000)
|
1528
|
rs = RadioSetting("ofsetb", "B Offset Frequency",
|
1529
|
RadioSettingValueFloat(
|
1530
|
0.00000, 59.99750, real_val, 0.00001, 5))
|
1531
|
abblock.append(rs)
|
1532
|
|
1533
|
rs = RadioSetting("offsetb", "B Offset",
|
1534
|
RadioSettingValueList(
|
1535
|
B_OFFSET, B_OFFSET[_vfob.offsetb]))
|
1536
|
abblock.append(rs)
|
1537
|
|
1538
|
rs = RadioSetting("lowpowerb", "B TX Power",
|
1539
|
RadioSettingValueList(
|
1540
|
B_TX_POWER, B_TX_POWER[_vfob.lowpowerb]))
|
1541
|
abblock.append(rs)
|
1542
|
|
1543
|
rs = RadioSetting("wideb", "B Band",
|
1544
|
RadioSettingValueList(
|
1545
|
B_BAND, B_BAND[_vfob.wideb]))
|
1546
|
abblock.append(rs)
|
1547
|
|
1548
|
rs = RadioSetting("bclb", "B Busy Lock",
|
1549
|
RadioSettingValueList(
|
1550
|
B_BUSYLOCK, B_BUSYLOCK[_vfob.bclb]))
|
1551
|
abblock.append(rs)
|
1552
|
|
1553
|
rs = RadioSetting("specialqtb", "B Special QT/DQT",
|
1554
|
RadioSettingValueList(
|
1555
|
B_SPEC_QTDQT, B_SPEC_QTDQT[_vfob.specialqtb]))
|
1556
|
abblock.append(rs)
|
1557
|
|
1558
|
rs = RadioSetting("bworkmode", "B Work Mode",
|
1559
|
RadioSettingValueList(
|
1560
|
B_WORKMODE, B_WORKMODE[_settings.bworkmode]))
|
1561
|
abblock.append(rs)
|
1562
|
|
1563
|
rs = RadioSetting("fmworkmode", "Work Mode",
|
1564
|
RadioSettingValueList(
|
1565
|
FM_WORKMODE, FM_WORKMODE[_settings.fmworkmode]))
|
1566
|
fmmode.append(rs)
|
1567
|
|
1568
|
rs = RadioSetting("fmroad", "Channel",
|
1569
|
RadioSettingValueList(
|
1570
|
FM_CHANNEL, FM_CHANNEL[_settings.fmroad]))
|
1571
|
fmmode.append(rs)
|
1572
|
|
1573
|
rs = RadioSetting("fmrec", "Forbid Receive",
|
1574
|
RadioSettingValueBoolean(_settings.fmrec))
|
1575
|
fmmode.append(rs)
|
1576
|
|
1577
|
# FM
|
1578
|
numeric = '0123456789.'
|
1579
|
for i in range(25):
|
1580
|
if self._memobj.fmusedflags[i]:
|
1581
|
_fm = self._get_fm(i).fmblock
|
1582
|
try:
|
1583
|
if not (760 < int(_fm) < 1080):
|
1584
|
raise ValueError()
|
1585
|
val = '%.1f' % (int(_fm) / 10)
|
1586
|
except ValueError:
|
1587
|
LOG.warning('FM channel index %i is invalid', i)
|
1588
|
val = ''
|
1589
|
else:
|
1590
|
val = ''
|
1591
|
rs = RadioSetting('block%02i' % i, "Channel %i" % (i + 1),
|
1592
|
RadioSettingValueString(0, 5,
|
1593
|
val,
|
1594
|
False, charset=numeric))
|
1595
|
fmmode.append(rs)
|
1596
|
|
1597
|
try:
|
1598
|
_fmv = int(self._memobj.fmvfo) / 10
|
1599
|
except ValueError:
|
1600
|
LOG.warning('FM VFO is invalid')
|
1601
|
_fmv = 0
|
1602
|
|
1603
|
rs = RadioSetting(
|
1604
|
"fmvfo", "VFO", RadioSettingValueFloat(
|
1605
|
76.0, 108.0, _fmv, 0.1, 1))
|
1606
|
fmmode.append(rs)
|
1607
|
|
1608
|
# DTMF
|
1609
|
gcode_val = str(_gcode.gcode)[2:]
|
1610
|
if gcode_val == "FF":
|
1611
|
gcode_val = "Off"
|
1612
|
elif gcode_val == "0F":
|
1613
|
gcode_val = "#"
|
1614
|
elif gcode_val == "0E":
|
1615
|
gcode_val = "*"
|
1616
|
elif gcode_val == '00':
|
1617
|
gcode_val = ""
|
1618
|
else:
|
1619
|
gcode_val = gcode_val[1]
|
1620
|
rs = RadioSetting("gcode", "Group Code",
|
1621
|
RadioSettingValueList(GROUPCODE,
|
1622
|
gcode_val))
|
1623
|
dtmf.append(rs)
|
1624
|
|
1625
|
icode_list = self._memobj.icode.idcode
|
1626
|
used_icode = ''
|
1627
|
for i in icode_list:
|
1628
|
if i == 0xFF:
|
1629
|
continue
|
1630
|
used_icode += str(i)[3]
|
1631
|
dtmfcharsani = "0123456789ABCD "
|
1632
|
i_val = RadioSettingValueString(0, 3, used_icode)
|
1633
|
rs = RadioSetting("icode", "ID Code", i_val)
|
1634
|
i_val.set_charset(dtmfcharsani)
|
1635
|
dtmf.append(rs)
|
1636
|
|
1637
|
gcode_list_1 = self._memobj.group1.group1
|
1638
|
used_group1 = ''
|
1639
|
for i in gcode_list_1:
|
1640
|
if i == 0xFF:
|
1641
|
continue
|
1642
|
used_group1 += str(i)[3]
|
1643
|
group1_val = RadioSettingValueString(0, 7, used_group1)
|
1644
|
rs = RadioSetting("group1", "1", group1_val)
|
1645
|
group1_val.set_charset(dtmfcharsani)
|
1646
|
dtmf.append(rs)
|
1647
|
|
1648
|
gcode_list_2 = self._memobj.group2.group2
|
1649
|
used_group2 = ''
|
1650
|
for i in gcode_list_2:
|
1651
|
if i == 0xFF:
|
1652
|
continue
|
1653
|
used_group2 += str(i)[3]
|
1654
|
group2_val = RadioSettingValueString(0, 7, used_group2)
|
1655
|
rs = RadioSetting("group2", "2", group2_val)
|
1656
|
group2_val.set_charset(dtmfcharsani)
|
1657
|
dtmf.append(rs)
|
1658
|
|
1659
|
gcode_list_3 = self._memobj.group3.group3
|
1660
|
used_group3 = ''
|
1661
|
for i in gcode_list_3:
|
1662
|
if i == 0xFF:
|
1663
|
continue
|
1664
|
used_group3 += str(i)[3]
|
1665
|
group3_val = RadioSettingValueString(0, 7, used_group3)
|
1666
|
rs = RadioSetting("group3", "3", group3_val)
|
1667
|
group3_val.set_charset(dtmfcharsani)
|
1668
|
dtmf.append(rs)
|
1669
|
|
1670
|
gcode_list_4 = self._memobj.group4.group4
|
1671
|
used_group4 = ''
|
1672
|
for i in gcode_list_4:
|
1673
|
if i == 0xFF:
|
1674
|
continue
|
1675
|
used_group4 += str(i)[3]
|
1676
|
group4_val = RadioSettingValueString(0, 7, used_group4)
|
1677
|
rs = RadioSetting("group4", "4", group4_val)
|
1678
|
group4_val.set_charset(dtmfcharsani)
|
1679
|
dtmf.append(rs)
|
1680
|
|
1681
|
gcode_list_5 = self._memobj.group5.group5
|
1682
|
used_group5 = ''
|
1683
|
for i in gcode_list_5:
|
1684
|
if i == 0xFF:
|
1685
|
continue
|
1686
|
used_group5 += str(i)[3]
|
1687
|
group5_val = RadioSettingValueString(0, 7, used_group5)
|
1688
|
rs = RadioSetting("group5", "5", group5_val)
|
1689
|
group5_val.set_charset(dtmfcharsani)
|
1690
|
dtmf.append(rs)
|
1691
|
|
1692
|
gcode_list_6 = self._memobj.group6.group6
|
1693
|
used_group6 = ''
|
1694
|
for i in gcode_list_6:
|
1695
|
if i == 0xFF:
|
1696
|
continue
|
1697
|
used_group6 += str(i)[3]
|
1698
|
group6_val = RadioSettingValueString(0, 7, used_group6)
|
1699
|
rs = RadioSetting("group6", "6", group6_val)
|
1700
|
group6_val.set_charset(dtmfcharsani)
|
1701
|
dtmf.append(rs)
|
1702
|
|
1703
|
gcode_list_7 = self._memobj.group7.group7
|
1704
|
used_group7 = ''
|
1705
|
for i in gcode_list_7:
|
1706
|
if i == 0xFF:
|
1707
|
continue
|
1708
|
used_group7 += str(i)[3]
|
1709
|
group7_val = RadioSettingValueString(0, 7, used_group7)
|
1710
|
rs = RadioSetting("group7", "7", group7_val)
|
1711
|
group7_val.set_charset(dtmfcharsani)
|
1712
|
dtmf.append(rs)
|
1713
|
|
1714
|
gcode_list_8 = self._memobj.group8.group8
|
1715
|
used_group8 = ''
|
1716
|
for i in gcode_list_8:
|
1717
|
if i == 0xFF:
|
1718
|
continue
|
1719
|
used_group8 += str(i)[3]
|
1720
|
group8_val = RadioSettingValueString(0, 7, used_group8)
|
1721
|
rs = RadioSetting("group8", "8", group7_val)
|
1722
|
group8_val.set_charset(dtmfcharsani)
|
1723
|
dtmf.append(rs)
|
1724
|
|
1725
|
scode_list = self._memobj.startcode.scode
|
1726
|
used_scode = ''
|
1727
|
for i in scode_list:
|
1728
|
if i == 0xFF:
|
1729
|
continue
|
1730
|
used_scode += str(i)[3]
|
1731
|
scode_val = RadioSettingValueString(0, 7, used_scode)
|
1732
|
rs = RadioSetting("scode", "PTT ID Starting(BOT)", scode_val)
|
1733
|
scode_val.set_charset(dtmfcharsani)
|
1734
|
dtmf.append(rs)
|
1735
|
|
1736
|
ecode_list = self._memobj.endcode.ecode
|
1737
|
used_ecode = ''
|
1738
|
for i in ecode_list:
|
1739
|
if i == 0xFF:
|
1740
|
continue
|
1741
|
used_ecode += str(i)[3]
|
1742
|
ecode_val = RadioSettingValueString(0, 7, used_ecode)
|
1743
|
rs = RadioSetting("ecode", "PTT ID Ending(BOT)", ecode_val)
|
1744
|
dtmf.append(rs)
|
1745
|
# H3
|
1746
|
if self.MODEL not in H8_LIST:
|
1747
|
# stuncode
|
1748
|
ecode_list = self._memobj.skcode.stuncode
|
1749
|
used_ecode = ''
|
1750
|
for i in ecode_list:
|
1751
|
if i == 0xFF:
|
1752
|
continue
|
1753
|
used_ecode += str(i)[3]
|
1754
|
ecode_val = RadioSettingValueString(0, 16, used_ecode)
|
1755
|
rs = RadioSetting("stuncode", "Stun Code", ecode_val)
|
1756
|
dtmf.append(rs)
|
1757
|
# killcode
|
1758
|
ecode_list = self._memobj.skcode.killcode
|
1759
|
used_ecode = ''
|
1760
|
for i in ecode_list:
|
1761
|
if i == 0xFF:
|
1762
|
continue
|
1763
|
used_ecode += str(i)[3]
|
1764
|
ecode_val = RadioSettingValueString(0, 16, used_ecode)
|
1765
|
rs = RadioSetting("killcode", "Kill Code", ecode_val)
|
1766
|
dtmf.append(rs)
|
1767
|
|
1768
|
return group
|
1769
|
|
1770
|
def get_settings(self):
|
1771
|
try:
|
1772
|
return self._get_settings()
|
1773
|
except Exception:
|
1774
|
raise InvalidValueError("Setting Failed!")
|
1775
|
|
1776
|
def set_settings(self, settings):
|
1777
|
|
1778
|
def fm_validate(value):
|
1779
|
if 760 > value or value > 1080:
|
1780
|
msg = ("FM Channel must be between 76.0-108.0")
|
1781
|
raise InvalidValueError(msg)
|
1782
|
|
1783
|
_settings = self._memobj.settings
|
1784
|
_press = self._memobj.press
|
1785
|
_aoffset = self._memobj.aoffset
|
1786
|
_boffset = self._memobj.boffset
|
1787
|
_vfoa = self._memobj.vfoa
|
1788
|
_vfob = self._memobj.vfob
|
1789
|
_fmmode = self._memobj.fmmode
|
1790
|
|
1791
|
for element in settings:
|
1792
|
if not isinstance(element, RadioSetting):
|
1793
|
if element.get_name() == "fm_preset":
|
1794
|
self._set_fm_preset(element)
|
1795
|
else:
|
1796
|
self.set_settings(element)
|
1797
|
continue
|
1798
|
else:
|
1799
|
try:
|
1800
|
name = element.get_name()
|
1801
|
if "." in name:
|
1802
|
bits = name.split(".")
|
1803
|
obj = self._memobj
|
1804
|
for bit in bits[:-1]:
|
1805
|
if "/" in bit:
|
1806
|
bit, index = bit.split("/", 1)
|
1807
|
index = int(index)
|
1808
|
obj = getattr(obj, bit)[index]
|
1809
|
else:
|
1810
|
obj = getattr(obj, bit)
|
1811
|
setting = bits[-1]
|
1812
|
elif name in PRESS_NAME:
|
1813
|
obj = _press
|
1814
|
setting = element.get_name()
|
1815
|
|
1816
|
elif name in VFOA_NAME:
|
1817
|
obj = _vfoa
|
1818
|
setting = element.get_name()
|
1819
|
elif name == "ofseta":
|
1820
|
obj = _aoffset
|
1821
|
setting = element.get_name()
|
1822
|
elif name in VFOB_NAME:
|
1823
|
obj = _vfob
|
1824
|
setting = element.get_name()
|
1825
|
elif name == "ofsetb":
|
1826
|
obj = _boffset
|
1827
|
setting = element.get_name()
|
1828
|
elif "block" in name:
|
1829
|
obj = _fmmode
|
1830
|
setting = element.get_name()
|
1831
|
elif "fmvfo" in name:
|
1832
|
obj = self._memobj.fmvfo
|
1833
|
setting = element.get_name()
|
1834
|
elif "gcode" in name:
|
1835
|
obj = self._memobj.groupcode.gcode
|
1836
|
setting = element.get_name()
|
1837
|
elif "idcode" in name:
|
1838
|
obj = self._memobj.icode.idcode
|
1839
|
setting = element.get_name()
|
1840
|
elif "scode" in name:
|
1841
|
obj = self._memobj.startcode.scode
|
1842
|
setting = element.get_name()
|
1843
|
elif "ecode" in name:
|
1844
|
obj = self._memobj.endcode.ecode
|
1845
|
setting = element.get_name()
|
1846
|
elif "group1" in name:
|
1847
|
obj = self._memobj.group1.group1
|
1848
|
setting = element.get_name()
|
1849
|
elif "group2" in name:
|
1850
|
obj = self._memobj.group2.group2
|
1851
|
setting = element.get_name()
|
1852
|
elif "group3" in name:
|
1853
|
obj = self._memobj.group3.group3
|
1854
|
setting = element.get_name()
|
1855
|
elif "group4" in name:
|
1856
|
obj = self._memobj.group4.group4
|
1857
|
setting = element.get_name()
|
1858
|
elif "group5" in name:
|
1859
|
obj = self._memobj.group5.group5
|
1860
|
setting = element.get_name()
|
1861
|
elif "group6" in name:
|
1862
|
obj = self._memobj.group6.group6
|
1863
|
setting = element.get_name()
|
1864
|
elif "group7" in name:
|
1865
|
obj = self._memobj.group7.group7
|
1866
|
setting = element.get_name()
|
1867
|
elif "group8" in name:
|
1868
|
obj = self._memobj.group8.group8
|
1869
|
setting = element.get_name()
|
1870
|
elif "micgain" in name:
|
1871
|
obj = self._memobj.mic.micgain
|
1872
|
setting = element.get_name()
|
1873
|
elif "killcode" in name:
|
1874
|
obj = self._memobj.skcode.killcode
|
1875
|
setting = element.get_name()
|
1876
|
elif "stuncode" in name:
|
1877
|
obj = self._memobj.skcode.stuncode
|
1878
|
setting = element.get_name()
|
1879
|
else:
|
1880
|
obj = _settings
|
1881
|
setting = element.get_name()
|
1882
|
if element.has_apply_callback():
|
1883
|
LOG.debug("Using apply callback")
|
1884
|
element.run_apply_callback()
|
1885
|
|
1886
|
# Channel A
|
1887
|
elif setting == "rxfreqa" and element.value.get_mutable():
|
1888
|
val = int(str(element.value).replace(
|
1889
|
'.', '').ljust(8, '0'))
|
1890
|
if (val >= 13600000 and val <= 17400000) or \
|
1891
|
(val >= 40000000 and val <= 52000000):
|
1892
|
setattr(obj, setting, val)
|
1893
|
else:
|
1894
|
msg = (
|
1895
|
"Frequency must be between "
|
1896
|
"136.00000-174.00000 or 400.00000-520.00000")
|
1897
|
raise InvalidValueError(msg)
|
1898
|
|
1899
|
elif setting == "ofseta" and element.value.get_mutable():
|
1900
|
if '.' in str(element.value):
|
1901
|
val = str(element.value).replace(' ', '')
|
1902
|
if len(
|
1903
|
val[val.index(".") + 1:]
|
1904
|
) >= 1 and int(val[val.index(".") + 1:]
|
1905
|
) != 0:
|
1906
|
val = '00' + val.replace('.', '')
|
1907
|
else:
|
1908
|
val = '0' + val.replace('.', '')
|
1909
|
val = val.ljust(8, '0')
|
1910
|
lenth_val = 0
|
1911
|
list_val = []
|
1912
|
else:
|
1913
|
val = '0' + str(element.value).replace(' ', '')
|
1914
|
val = val.ljust(8, '0')
|
1915
|
lenth_val = 0
|
1916
|
list_val = []
|
1917
|
if (int(val) >= 0 and int(val) <= 5999750):
|
1918
|
if int(val) == 0:
|
1919
|
_aoffset.ofseta = [0xFF, 0xFF, 0xFF, 0xFF]
|
1920
|
else:
|
1921
|
while lenth_val < (len(val)):
|
1922
|
list_val.insert(
|
1923
|
0, val[lenth_val:lenth_val + 2])
|
1924
|
lenth_val += 2
|
1925
|
for i in range(len(list_val)):
|
1926
|
list_val[i] = int(list_val[i], 16)
|
1927
|
_aoffset.ofseta = list_val
|
1928
|
else:
|
1929
|
msg = ("Offset must be between 0.00000-59.99750")
|
1930
|
raise InvalidValueError(msg)
|
1931
|
|
1932
|
# B channel
|
1933
|
elif setting == "rxfreqb" and element.value.get_mutable():
|
1934
|
val = 0
|
1935
|
val = int(str(element.value).replace(
|
1936
|
'.', '').ljust(8, '0'))
|
1937
|
if (val >= 13600000 and val <= 17400000) or \
|
1938
|
(val >= 40000000 and val <= 52000000):
|
1939
|
setattr(obj, setting, val)
|
1940
|
else:
|
1941
|
msg = (
|
1942
|
"Frequency must be between "
|
1943
|
"136.00000-174.00000 or 400.00000-520.00000")
|
1944
|
raise InvalidValueError(msg)
|
1945
|
# setattr(obj, setting, val)
|
1946
|
|
1947
|
elif setting == "ofsetb" and element.value.get_mutable():
|
1948
|
if '.' in str(element.value):
|
1949
|
val = str(element.value).replace(' ', '')
|
1950
|
if len(val[val.index(".") + 1:]
|
1951
|
) >= 1 and int(val[val.index(".") + 1:]
|
1952
|
) != 0:
|
1953
|
val = '00' + \
|
1954
|
str(element.value).replace('.', '')
|
1955
|
else:
|
1956
|
val = '0' + str(element.value).replace('.', '')
|
1957
|
val = val.ljust(8, '0')
|
1958
|
lenth_val = 0
|
1959
|
list_val = []
|
1960
|
else:
|
1961
|
val = '0' + str(element.value).replace(' ', '')
|
1962
|
val = val.ljust(8, '0')
|
1963
|
lenth_val = 0
|
1964
|
list_val = []
|
1965
|
if (int(val) >= 0 and int(val) <= 5999750):
|
1966
|
if int(val) == 0:
|
1967
|
_boffset.ofsetb = [0xFF, 0xFF, 0xFF, 0xFF]
|
1968
|
else:
|
1969
|
while lenth_val < (len(val)):
|
1970
|
list_val.insert(
|
1971
|
0, val[lenth_val:lenth_val + 2])
|
1972
|
lenth_val += 2
|
1973
|
for i in range(len(list_val)):
|
1974
|
list_val[i] = int(list_val[i], 16)
|
1975
|
_boffset.ofsetb = list_val
|
1976
|
else:
|
1977
|
msg = ("Offset must be between 0.00000-59.99750")
|
1978
|
raise InvalidValueError(msg)
|
1979
|
|
1980
|
# FM
|
1981
|
elif "block" in name:
|
1982
|
num = int(name[-2:], 10)
|
1983
|
val = str(element.value)
|
1984
|
if val.strip():
|
1985
|
try:
|
1986
|
val = int(float(val) * 10)
|
1987
|
except ValueError:
|
1988
|
raise InvalidValueError(
|
1989
|
'Value must be between 76.0-108.0')
|
1990
|
fm_validate(val)
|
1991
|
else:
|
1992
|
val = 0
|
1993
|
self._memobj.fmmode[num].fmblock = val
|
1994
|
self._memobj.fmusedflags[num] = bool(val)
|
1995
|
|
1996
|
elif setting == 'fmvfo' and element.value.get_mutable():
|
1997
|
self._memobj.fmvfo = int(element.value * 10)
|
1998
|
|
1999
|
elif setting == 'gcode' and element.value.get_mutable():
|
2000
|
val = str(element.value)
|
2001
|
if val == 'Off':
|
2002
|
gcode_used = 0xFF
|
2003
|
elif val == 'A':
|
2004
|
gcode_used = 0x0A
|
2005
|
elif val == 'B':
|
2006
|
gcode_used = 0x0B
|
2007
|
elif val == 'C':
|
2008
|
gcode_used = 0x0C
|
2009
|
elif val == 'D':
|
2010
|
gcode_used = 0x0D
|
2011
|
elif val == '#':
|
2012
|
gcode_used = 0x0F
|
2013
|
elif val == '*':
|
2014
|
gcode_used = 0x0E
|
2015
|
elif val == '':
|
2016
|
gcode_used = 0x00
|
2017
|
self._memobj.groupcode.gcode = gcode_used
|
2018
|
|
2019
|
elif setting == 'icode' and element.value.get_mutable():
|
2020
|
val = str(element.value)
|
2021
|
list_val = []
|
2022
|
lenth_val = 0
|
2023
|
while lenth_val < (len(val)):
|
2024
|
if val[lenth_val] != ' ':
|
2025
|
list_val.append(int(val[lenth_val], 16))
|
2026
|
lenth_val += 1
|
2027
|
else:
|
2028
|
list_val.append(0xFF)
|
2029
|
lenth_val += 1
|
2030
|
self._memobj.icode.idcode = list_val
|
2031
|
|
2032
|
elif setting == 'scode' and element.value.get_mutable():
|
2033
|
val = str(element.value)
|
2034
|
list_val = []
|
2035
|
lenth_val = 0
|
2036
|
while lenth_val < (len(val)):
|
2037
|
if val[lenth_val] != ' ':
|
2038
|
list_val.append(int(val[lenth_val], 16))
|
2039
|
lenth_val += 1
|
2040
|
else:
|
2041
|
list_val.append(0xFF)
|
2042
|
lenth_val += 1
|
2043
|
self._memobj.startcode.scode = list_val
|
2044
|
|
2045
|
elif setting == 'ecode' and element.value.get_mutable():
|
2046
|
val = str(element.value)
|
2047
|
list_val = []
|
2048
|
lenth_val = 0
|
2049
|
while lenth_val < (len(val)):
|
2050
|
if val[lenth_val] != ' ':
|
2051
|
list_val.append(int(val[lenth_val], 16))
|
2052
|
lenth_val += 1
|
2053
|
else:
|
2054
|
list_val.append(0xFF)
|
2055
|
lenth_val += 1
|
2056
|
self._memobj.endcode.ecode = list_val
|
2057
|
|
2058
|
elif setting == 'group1' and element.value.get_mutable():
|
2059
|
val = str(element.value)
|
2060
|
list_val = []
|
2061
|
lenth_val = 0
|
2062
|
while lenth_val < (len(val)):
|
2063
|
if val[lenth_val] != ' ':
|
2064
|
list_val.append(int(val[lenth_val], 16))
|
2065
|
lenth_val += 1
|
2066
|
else:
|
2067
|
list_val.append(0xFF)
|
2068
|
lenth_val += 1
|
2069
|
self._memobj.group1.group1 = list_val
|
2070
|
|
2071
|
elif setting == 'group2' and element.value.get_mutable():
|
2072
|
val = str(element.value)
|
2073
|
list_val = []
|
2074
|
lenth_val = 0
|
2075
|
while lenth_val < (len(val)):
|
2076
|
if val[lenth_val] != ' ':
|
2077
|
list_val.append(int(val[lenth_val], 16))
|
2078
|
lenth_val += 1
|
2079
|
else:
|
2080
|
list_val.append(0xFF)
|
2081
|
lenth_val += 1
|
2082
|
self._memobj.group2.group2 = list_val
|
2083
|
|
2084
|
elif setting == 'group3' and element.value.get_mutable():
|
2085
|
val = str(element.value)
|
2086
|
list_val = []
|
2087
|
lenth_val = 0
|
2088
|
while lenth_val < (len(val)):
|
2089
|
if val[lenth_val] != ' ':
|
2090
|
list_val.append(int(val[lenth_val], 16))
|
2091
|
lenth_val += 1
|
2092
|
else:
|
2093
|
list_val.append(0xFF)
|
2094
|
lenth_val += 1
|
2095
|
self._memobj.group3.group3 = list_val
|
2096
|
|
2097
|
elif setting == 'group4' and element.value.get_mutable():
|
2098
|
val = str(element.value)
|
2099
|
list_val = []
|
2100
|
lenth_val = 0
|
2101
|
while lenth_val < (len(val)):
|
2102
|
if val[lenth_val] != ' ':
|
2103
|
list_val.append(int(val[lenth_val], 16))
|
2104
|
lenth_val += 1
|
2105
|
else:
|
2106
|
list_val.append(0xFF)
|
2107
|
lenth_val += 1
|
2108
|
self._memobj.group4.group4 = list_val
|
2109
|
|
2110
|
elif setting == 'group5' and element.value.get_mutable():
|
2111
|
val = str(element.value)
|
2112
|
list_val = []
|
2113
|
lenth_val = 0
|
2114
|
while lenth_val < (len(val)):
|
2115
|
if val[lenth_val] != ' ':
|
2116
|
list_val.append(int(val[lenth_val], 16))
|
2117
|
lenth_val += 1
|
2118
|
else:
|
2119
|
list_val.append(0xFF)
|
2120
|
lenth_val += 1
|
2121
|
self._memobj.group5.group5 = list_val
|
2122
|
|
2123
|
elif setting == 'group6' and element.value.get_mutable():
|
2124
|
val = str(element.value)
|
2125
|
list_val = []
|
2126
|
lenth_val = 0
|
2127
|
while lenth_val < (len(val)):
|
2128
|
if val[lenth_val] != ' ':
|
2129
|
list_val.append(int(val[lenth_val], 16))
|
2130
|
lenth_val += 1
|
2131
|
else:
|
2132
|
list_val.append(0xFF)
|
2133
|
lenth_val += 1
|
2134
|
self._memobj.group6.group6 = list_val
|
2135
|
|
2136
|
elif setting == 'group7' and element.value.get_mutable():
|
2137
|
val = str(element.value)
|
2138
|
list_val = []
|
2139
|
lenth_val = 0
|
2140
|
while lenth_val < (len(val)):
|
2141
|
if val[lenth_val] != ' ':
|
2142
|
list_val.append(int(val[lenth_val], 16))
|
2143
|
lenth_val += 1
|
2144
|
else:
|
2145
|
list_val.append(0xFF)
|
2146
|
lenth_val += 1
|
2147
|
self._memobj.group7.group7 = list_val
|
2148
|
|
2149
|
elif setting == 'group8' and element.value.get_mutable():
|
2150
|
val = str(element.value)
|
2151
|
list_val = []
|
2152
|
lenth_val = 0
|
2153
|
while lenth_val < (len(val)):
|
2154
|
if val[lenth_val] != ' ':
|
2155
|
list_val.append(int(val[lenth_val], 16))
|
2156
|
lenth_val += 1
|
2157
|
else:
|
2158
|
list_val.append(0xFF)
|
2159
|
lenth_val += 1
|
2160
|
self._memobj.group8.group8 = list_val
|
2161
|
elif setting == 'micgain':
|
2162
|
self._memobj.mic.micgain = (
|
2163
|
str(element.value))
|
2164
|
elif setting == 'stuncode' and element.value.get_mutable():
|
2165
|
val = str(element.value)
|
2166
|
list_val = []
|
2167
|
lenth_val = 0
|
2168
|
while lenth_val < (len(val)):
|
2169
|
if val[lenth_val] != ' ':
|
2170
|
list_val.append(int(val[lenth_val], 16))
|
2171
|
lenth_val += 1
|
2172
|
else:
|
2173
|
list_val.append(0xFF)
|
2174
|
lenth_val += 1
|
2175
|
self._memobj.skcode.stuncode = list_val
|
2176
|
elif setting == 'killcode' and element.value.get_mutable():
|
2177
|
val = str(element.value)
|
2178
|
list_val = []
|
2179
|
lenth_val = 0
|
2180
|
while lenth_val < (len(val)):
|
2181
|
if val[lenth_val] != ' ':
|
2182
|
list_val.append(int(val[lenth_val], 16))
|
2183
|
lenth_val += 1
|
2184
|
else:
|
2185
|
list_val.append(0xFF)
|
2186
|
lenth_val += 1
|
2187
|
self._memobj.skcode.killcode = list_val
|
2188
|
elif element.value.get_mutable():
|
2189
|
setattr(obj, setting, element.value)
|
2190
|
except Exception:
|
2191
|
LOG.debug(element.get_name())
|
2192
|
raise
|
2193
|
|
2194
|
def _set_fm_preset(self, settings):
|
2195
|
for element in settings:
|
2196
|
try:
|
2197
|
val = element.value
|
2198
|
if self._memobj.fm_presets <= 108.0 * 10 - 650:
|
2199
|
value = int(val.get_value() * 10 - 650)
|
2200
|
else:
|
2201
|
value = int(val.get_value() * 10)
|
2202
|
LOG.debug("Setting fm_presets = %s" % (value))
|
2203
|
self._memobj.fm_presets = value
|
2204
|
except Exception:
|
2205
|
LOG.debug(element.get_name())
|
2206
|
raise
|
2207
|
|
2208
|
|
2209
|
@directory.register
|
2210
|
class TDH8_HAM(TDH8):
|
2211
|
VENDOR = "TIDRADIO"
|
2212
|
MODEL = "TD-H8-HAM"
|
2213
|
ident_mode = b'P31185\xff\xff'
|
2214
|
_ham = True
|
2215
|
_txbands = [(144000000, 149000000), (420000000, 451000000)]
|
2216
|
|
2217
|
|
2218
|
@directory.register
|
2219
|
class TDH8_GMRS(TDH8):
|
2220
|
VENDOR = "TIDRADIO"
|
2221
|
MODEL = "TD-H8-GMRS"
|
2222
|
ident_mode = b'P31184\xff\xff'
|
2223
|
_gmrs = True
|
2224
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
2225
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
2226
|
chirp_common.PowerLevel("High", watts=8.00)]
|
2227
|
|
2228
|
def validate_memory(self, mem):
|
2229
|
msgs = super().validate_memory(mem)
|
2230
|
if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
|
2231
|
msgs.append(chirp_common.ValidationError(
|
2232
|
"The frequency in channels 31-54 must be between"
|
2233
|
"462.55000-462.72500 in 0.025 increments."))
|
2234
|
return msgs
|
2235
|
|
2236
|
|
2237
|
@directory.register
|
2238
|
class UV68(TDH8):
|
2239
|
VENDOR = "TID"
|
2240
|
MODEL = "TD-UV68"
|
2241
|
|
2242
|
|
2243
|
@directory.register
|
2244
|
class TDH3(TDH8):
|
2245
|
VENDOR = "TIDRADIO"
|
2246
|
MODEL = "TD-H3"
|
2247
|
_memsize = 0x1fef
|
2248
|
_ranges_main = [(0x0000, 0x1fef)]
|
2249
|
_idents = [TD_H3]
|
2250
|
_txbands = [(136000000, 600000000)]
|
2251
|
_rxbands = [(50000000, 107999000), (108000000, 136000000)]
|
2252
|
_aux_block = True
|
2253
|
_tri_power = True
|
2254
|
_gmrs = False
|
2255
|
_ham = False
|
2256
|
_mem_params = (0x1F2F)
|
2257
|
_tx_power = [chirp_common.PowerLevel("Low", watts=1.00),
|
2258
|
chirp_common.PowerLevel("High", watts=4.00)]
|
2259
|
|
2260
|
def process_mmap(self):
|
2261
|
self._memobj = bitwise.parse(MEM_FORMAT_H3, self._mmap)
|
2262
|
|
2263
|
|
2264
|
@directory.register
|
2265
|
class TDH3_HAM(TDH3):
|
2266
|
VENDOR = "TIDRADIO"
|
2267
|
MODEL = "TD-H3-HAM"
|
2268
|
ident_mode = b'P31185\xff\xff'
|
2269
|
_ham = True
|
2270
|
_txbands = [(144000000, 149000000), (420000000, 451000000)]
|
2271
|
|
2272
|
|
2273
|
@directory.register
|
2274
|
class TDH3_GMRS(TDH3):
|
2275
|
VENDOR = "TIDRADIO"
|
2276
|
MODEL = "TD-H3-GMRS"
|
2277
|
ident_mode = b'P31184\xff\xff'
|
2278
|
_gmrs = True
|
2279
|
_txbands = [(136000000, 175000000), (400000000, 521000000)]
|
2280
|
|
2281
|
def validate_memory(self, mem):
|
2282
|
msgs = super().validate_memory(mem)
|
2283
|
if 31 <= mem.number <= 54 and mem.freq not in GMRS_FREQS:
|
2284
|
msgs.append(chirp_common.ValidationError(
|
2285
|
"The frequency in channels 31-54 must be between"
|
2286
|
"462.55000-462.72500 in 0.025 increments."))
|
2287
|
return msgs
|