Project

General

Profile

New Model #11137 ยป ftm6000.py

Rick DeWitt, 04/26/2024 07:44 AM

 
1
# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com) V1.0
2
# Issues 9719 for FTM-6000 and 11137 for FTM-200
3
# Note: 'TESTME' flags are intended for use with future sub-models
4
# FTM-100, FTM-300 and FTM-500 can probably be supported
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18

    
19
"""FTM-6000 Yaesu Radio Driver"""
20

    
21
from chirp.drivers import yaesu_clone
22
from chirp import chirp_common, util, memmap, errors, directory, bitwise
23
from chirp.settings import RadioSetting, RadioSettingGroup, \
24
    RadioSettingValueInteger, RadioSettingValueList, \
25
    RadioSettingValueBoolean, RadioSettingValueString, \
26
    RadioSettingValueFloat, RadioSettings
27
import time
28
import struct
29
import logging
30
import math
31
import sys
32

    
33
LOG = logging.getLogger(__name__)
34
CMD_ACK = b'\x06'
35

    
36

    
37
class Ftm6000RAlias(chirp_common.Alias):
38
    Vendor = "Yaesu"
39
    MODEL = "FTM-6000R"
40

    
41

    
42
@directory.register
43
class FTM6000Radio(yaesu_clone.YaesuCloneModeRadio):
44
    """Yaesu FTM-6000"""
45
    FTM200 = False
46
    TESTME = False
47
    NEEDS_COMPAT_SERIAL = False
48
    BAUD_RATE = 38400
49
    COM_BITS = 8
50
    COM_PRTY = 'N'
51
    COM_STOP = 1
52
    MODEL = "FTM-6000"
53
    NAME_LEN = 6        # Valid Name Length
54
    ALIASES = [Ftm6000RAlias]
55
    MODES = ["FM", "AM", "NFM"]
56
    TMODES = ["", "Tone", "TSQL", "TSQL-R", "DTCS", "Cross"]
57
    CROSS_MODES = ["->DTCS", "Tone->DTCS", "DTCS->Tone"]
58
    DUPLEX = ["", "off", "-", "+", "split"]
59
    T_STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
60
    VALID_BANDS = [(108000000, 137000000),
61
                   (137000000, 174000000),
62
                   (174000000, 400000000),
63
                   (400000000, 480000000),
64
                   (480000000, 999999999)]
65
    POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=50),
66
                    chirp_common.PowerLevel("Mid", watts=20),
67
                    chirp_common.PowerLevel("Low", watts=5)]
68
    SKIPS = ["", "S"]
69
    # No lowercase,{} or ~
70
    CHARSET = [chr(x) for x in list(range(ord(" "), ord("_") + 1)) +
71
               [ord("|")]]
72
    DTMF_CHARS = list("0123456789ABCD*#")
73
    MEM_FORMAT = """
74
        struct memdat {         // 16 bytes per chan
75
          u8 used:1,            // 00
76
             skip:2,
77
             unk1:2,
78
             band:3;
79
          u8 clks:1,            // 01
80
             xx:1,
81
             mode:2,
82
             duplex:4;
83
          u24 freq;            // 02-04
84
          u8 tmode:4,           // 05
85
             step:4;
86
          u24 frqtx;            // 06-08
87
          u8 power:2,           // 09
88
             rtone:6;
89
          u8 clktyp:1,          // 0A,
90
             dtcs:7;
91
          u8 prfreq;            // 0B
92
          u16 offset;           // 0C-0D 0-99.95 MHz in 0.5 steps 06.0 is 00 0c
93
          u8 unk3;              // 0E
94
          u8 unk4;              // 0F
95
        };
96

    
97
        struct tag {
98
          u8 chname[16];
99
        };
100

    
101
        struct pmgx {
102
          u8 za;                // 0x00
103
          u8 fb;                // 0xff
104
          u16 chnp;             // 0-based pmg channel number
105
        };
106

    
107
        struct {                // In 1st block
108
            u8 id[6];
109
        } model;
110
        #seekto 0x080;
111
        struct memdat vfo_air;      // 080
112
        struct memdat vfo_144;      // 090
113
        struct memdat vfo_vhf;      // 0a0
114
        struct memdat vfo_430;      // 0b0
115
        struct memdat vfo_uhf;      // 0c0
116
        struct memdat unkc1[5];     // 0d0 - 0110  repeats vfo entries
117
        u8 120[16];                 // 0x120  all ff
118
        struct memdat home;         // 0x130
119
        u8 ff2[64];                 // 0x140 - 0x17f; ff's'
120
        struct memdat unkc2;        // 0x180 possible last chan accessed?
121
        #seekto 0x200;
122
        struct {
123
            u8 20axd[14];           // 0x200 - 0x20d
124
            u8 xm0au:5,             // 0x20e
125
               bell:3;
126
            u8 xm0bu:6,             // 020f
127
               lcd:2;
128
            u8 210xd[14];           // 0210 - 021d
129
            u8 21ea:5,
130
               bellb:3;
131
            u8 wrxdid;              // 21f
132
            u8 220x1[2];            // 220 -221
133
            u8 fkp1;                // 0x222: FTM-200 keypad quick access 1
134
            u8 fhm1;                // 0x223: FTM-200 home qa 1
135
            u8 fkp2;
136
            u8 fhm2;
137
            u8 fkp3;
138
            u8 fhm3;
139
            u8 fkp4;
140
            u8 fhm4;                // 0x229
141
            u8 22axf[6];
142
            u8 230x7[8];            // 230-237
143
            u24 wrxvfrq;
144
            u8 23b;
145
            u24 wrxufrq;
146
            u8 23f;
147
            u8 240a:6,
148
               wrxbsel:1,
149
               240b:1;
150
            u8 241x7[7];            // 24-247
151
            u8 csign[10];           // 248 - 251
152
            u8 pgrcdr1;             // 0252
153
            u8 pgrcdr2;
154
            u8 pgrcdt1;
155
            u8 pgrcdt2;             // 0255
156
            u8 256a:7,
157
               compass:1;
158
            u8 257a:7,
159
               usbcamsz:1;
160
            u8 258a:6,
161
               usbcamql:2;
162
            u8 259;
163
            u8 25a;
164
            u8 25ba:4,
165
               wrxpop:4;
166
            u8 25c;
167
            u8 micgain;             // 25d
168
            u8 25e;
169
            u8 micp1;               // 25f
170
            u8 micp2;               // 260
171
            u8 micp3;
172
            u8 micp4;
173
            u8 263xf[13];           // 0x263 - 026f
174
            u8 270xf[16];           // 0x270 - 27f
175
        } micset;
176
        struct {
177
            u8 codes[16];            // 0x280-30f
178
        } dtmfcode[9];
179
        #seekto 0x400;
180
        struct {
181
            u8 400a:7,               // 0400
182
               aprbupo:1;
183
            u8 401a:7,
184
              aprbudi:1;
185
            u8 402a:6,
186
               aprbusp:2;
187
            u8 403a:7,              // 0403
188
               aprbual:1;
189
            u8 404a:5,
190
               aprbubo:3;
191
            u8 405a:7,
192
               aprbutp:1;
193
            u8 406a:7,
194
               aprburn:1;
195
            u8 407a:5,
196
               aprbuwd:3;
197
            u8 aprcsgn[6];          // 0408-040d
198
            u8 aprcsfx;
199
            u8 400f;
200
            u8 410a:3,
201
              aprltns:1,
202
              410b:4;
203
            u8 aprlad;              // 0411
204
            u8 aprlam;
205
            u16 aprlas;             // 0413-0414
206
            u8 415a:3,
207
               aprlgew:1,
208
               415b:4;
209
            u8 aprlgd;
210
            u8 aprlgm;
211
            u16 aprlgs;
212
            u8 aprrcsn[6];          // 041a-41f
213
            u8 aprrcfx;             // 0420
214
            u8 421;
215
            u8 aprsym1[2];          // 0422, 423
216
            u8 aprsym2[2];
217
            u8 aprsym3[2];
218
            u8 aprsym4a;
219
            u8 aprsym4b;            // 0429
220
            u16 aprbfrl;            // 042a & 042b
221
            u8 42ca:4,
222
               aprsym:4;
223
            u8 42da:4,              // 042d
224
               aprdig:4;
225
            u8 42ea:4,
226
               aprstxi:4;
227
            u8 aprsrate;            // 042f
228
            u8 aprslow;             // 0430
229
            u8 431a:5,
230
               aprbamb:3;
231
            u8 432a:4,
232
               aprcmt:4;
233
            u8 aprspro:1,           // 0433
234
               aprbaut:1,
235
               433b:1,
236
               aprsdcy:1,
237
               aprspdc:1,
238
               aprsalt:1,
239
               433c:1,
240
               aprrply:1;
241
            u8 434a:1,              // 0434
242
               dspda:1,             // FTM-200 APRS
243
               pktspd:1,            // Both
244
               434b:3,
245
               aprmut:1,
246
               aprson:1;
247
            u8 435a:2,
248
               datsql:1,
249
               435b:5;
250
            u8 436a:5,
251
               bcnstat:3;
252
            u8 437a:4,            // 0x437
253
               bcntxra:4;
254
            u8 438a:1,
255
               aprbfot:1,
256
               aprbfst:1,
257
               aprbfit:1,
258
               aprbfob:1,
259
               aprbfwx:1,
260
               aprbfpo:1,
261
               aprbfme:1;
262
            u8 439a:7,
263
               aprbrtb:1;
264
            u8 43aa:7,
265
               aprbrtm:1;
266
            u8 43ba:7,
267
               aprbrrb:1;
268
            u8 43ca:7,
269
               aprbrrm:1;
270
            u8 43da:7,
271
               aprbrmp:1;
272
            u8 43ea:7,
273
               aprbrcr:1;
274
            u8 aprbrrr;             // 043f
275
            u8 440a:7,
276
               aprbrmv:1;
277
            u8 441a:7,
278
               aprmpos:1;
279
            u8 442;
280
            u8 443a:4,
281
               dbsela:4;
282
            u8 444a:4,
283
               dbseld:4;
284
            u8 aprpopb;              // 0445
285
            u8 aprpopm;
286
            u8 447a:4,
287
               aprtxd:4;
288
            u8 448a:4,
289
               comout:4;
290
            u8 449a:4,
291
               comspd:4;
292
            u8 44aa:5,
293
               bcnsmart:3;
294
            u8 typ1val0;            // 044b -> 045f
295
            u8 typ1val1;            // Smart Beaconing type settings
296
            u8 typ1val2;
297
            u8 typ1val3;
298
            u8 typ1val4;
299
            u8 typ1val5;
300
            u8 typ1val6;
301
            u8 typ2val0;
302
            u8 typ2val1;
303
            u8 typ2val2;
304
            u8 typ2val3;
305
            u8 typ2val4;
306
            u8 typ2val5;
307
            u8 typ2val6;
308
            u8 typ3val0;
309
            u8 typ3val1;
310
            u8 typ3val2;
311
            u8 typ3val3;
312
            u8 typ3val4;
313
            u8 typ3val5;
314
            u8 typ3val6;        // 045f
315
            u8 460a:4,
316
               aprstxn:4;
317
            u8 461a:1,
318
               aprmpkt:1,
319
               aprbfan:1,
320
               461b:5;
321
            u8 462a:4,
322
               comwpf:4;
323
            u8 463a:4,
324
                comflt:4;
325
            u8 464;
326
            u8 465;
327
            u8 466;
328
            u8 467;
329
            u8 468;
330
            u8 469;
331
            u8 46a;
332
            u8 46b;
333
            u8 46c;
334
            u8 46da:5,
335
               aprvlrt:3;
336
            u8 aprtsql;
337
            u8 aprvdcs;             // 046f
338
            u8 470a:6,
339
               aprsfs:2;
340
            u8 471a:4,
341
               aprsff:4;
342
            u8 472xf[14];           // -> 047f
343
            u8 aprrtxt[64];         // 0480 - 04BF
344
        } wierd;
345
        #seekto 0x500;
346
        struct {
347
            u8 grp1[9];
348
            u8 509;
349
            u8 grp2[9];
350
            u8 514;
351
            u8 grp3[9];
352
            u8 51d;
353
            u8 grp4[9];
354
            u8 527;
355
            u8 grp5[9];
356
            u8 532;
357
            u8 grp6[9];
358
            u8 53b;
359
            u8 blt1[9];
360
            u8 545;
361
            u8 blt2[9];
362
            u8 554;
363
            u8 blt3[9];
364
            u8 559;
365
        } aprsmsg;
366
        #seekto 0x580;
367
        struct {          // FMT-6000 Menu item Fntcn mode set table
368
            u8 shbyt;     // FTM-200 Digipath, RingerCS data
369
        } share1[256];    // -> 0x67f
370
        struct {            // 0x680
371
            u8 txt1[16];
372
            u8 txt2[16];
373
            u8 txt3[16];
374
            u8 txt4[16];
375
            u8 txt5[16];
376
            u8 txt6[16];
377
            u8 txt7[16];
378
            u8 txt8[16];
379
        } aprstxt;
380
        struct memdat main[999];    // 0x700 16 bytes each, up to 4570
381
        struct memdat pms[100];     // up to 0x4bb0
382
        struct memdat unkc3[5];     // unknown, up to 0x4c00
383
        #seekto 0x4c10;
384
        struct pmgx pmg[6];         // 6th element is change record
385
        #seekto 0x6980;
386
        struct {                    // WIRES-X Messages
387
            u8 msg01[128];
388
            u8 msg02[128];
389
            u8 msg03[128];
390
            u8 msg04[128];
391
            u8 msg05[128];
392
            u8 msg06[128];
393
            u8 msg07[128];
394
            u8 msg08[128];
395
            u8 msg09[128];
396
            u8 msg10[128];
397
        } wrxmsg;
398
        struct {                    // 0x6e80
399
            u8 c1[16];
400
            u8 c2[16];
401
            u8 c3[16];
402
            u8 c4[16];
403
            u8 c5[16];
404
        } wrxcat;
405
        #seekto 0x7d00;
406
        struct {                    // --> 07e3f
407
            u8 msg[60];
408
            u8 mode;
409
            u8 nax[3];
410
        } bcntxt[5];
411
        #seekto 0xff00;
412
        struct tag names[999];      // 16 bytes each, up to 0x13d6f (83055.)
413
        struct tag pms_name[100];   // 13d70 - 143af
414
        #seekto 0x17f00;
415
        struct {                    // Settings
416
            u8 f00xf[16];           // 17f00-0f
417
            u8 f10xf[16];           // 17f10-1f
418
            u8 f20;                 // 17f20
419
            u8 f21a:7,
420
               unit:1;
421
            u8 f22;
422
            u8 f23;
423
            u8 f24a:2,              // 17f24
424
                apo:5,
425
                apo1:1;
426
            u8 bclo:1,              // 17f25
427
                f25a:4,
428
                arts_int:1,
429
                arts_mode:2;
430
            u8 f26:4,              // 17f26
431
                airb:1,
432
                vhfb:1,
433
                uhfb:1,
434
                otrb:1;
435
            u8 f27;
436
            u8 tzone;
437
            u8 f29a:6,
438
               fvsvol:2;
439
            u8 f2au:4,              // 17f2a
440
               tot:4;
441
            u8 f2b;
442
            u8 f2c;
443
            u8 f2d:6,
444
               dspmode:2;
445
            u8 f2ea:6,
446
               fvsanc:2;
447
            u8 f2fa:6,              // 17f2f
448
               gpsdtm:1,
449
               f2fb:1;
450
            u8 lastmnu;             // 17f30
451
            u8 f31;                 // 17f31
452
            u8 f1key;               // 17f32
453
            u8 f33;
454
            u8 f34;
455
            u8 f35a:4,
456
               gpslog:4;
457
            u8 f36a:6,
458
               voxsen:2;            // FTM-200
459
            u8 f37a:5,
460
               voxdly:3;
461
            u8 f38a:6,              // 17f38
462
               audrec:2;
463
            u8 lastfunc;            // 17f39
464
            u8 f3a:6,               // 17f3a
465
               lcd_clr:2;
466
            u8 f3bxf[5];            // 17f3b-3f
467
            u8 f40x2[3];            // 7f40-42
468
            u8 a_chan;              // FTM-200 17f43
469
            u8 f44;
470
            u8 f45u:5,              // 17f45
471
               scnrsm:3;
472
            u8 f46;
473
            u8 f47;
474
            u8 f48u:4,              // 17f48
475
                sql:4;
476
            u8 f49:4,
477
                scnrsm2a:4;         // FTM-200 A
478
            u8 f4a;                 // 17f4a
479
            u8 f4ba:4,
480
               scndria:4;
481
            u8 f4c;
482
            u8 f4d;
483
            u8 f4e;
484
            u8 f4f;
485
            u8 f50x2[3];            // 17f50-52
486
            u8 b_chan;              // FTM-200 17f53
487
            u8 f54;
488
            u8 f55;
489
            u8 f56;
490
            u8 f57;
491
            u8 f58;
492
            u8 f59:4,
493
               scnrsm2b:4;         // FTM-200 B
494
            u8 f5a;                // 17f5a
495
            u8 f5ba:4,
496
               scndrib:4;
497
            u8 f5c;
498
            u8 f5d;
499
            u8 f5e;
500
            u8 f5f;
501
            u8 f60;                 // 17f60
502
            u8 f61;
503
            u8 scndrm1:1,           // 17f62
504
               wxalrt:1,
505
               f62b:5,
506
               dwrvt:1;
507
            u8 f63a:7,              // 17f63
508
               sqlexp1:1;
509
            u8 f64a:5,              // 17f64
510
               rpt_ars:1,
511
               f64b:2;
512
            u8 f65a:2,
513
               bscope:1,
514
               f65b:5;
515
            u8 f66;
516
            u8 f67;
517
            u8 f68a:3,
518
               wrxstb:1,
519
               f68b:1,
520
               locinfo:1,
521
               wrxdvw:1,
522
               f68c:1;
523
            u8 f69;
524
            u8 f6aa:4,
525
               audmic:1,
526
               f6ab:2,
527
               btsave:1;
528
            u8 wrxloc:1,            // 17f6b
529
               fvslan:1,
530
               f6bb:2,
531
               beep1:1,            // Stupid split beep: this is Off/Low
532
               fvsrxm:1,
533
               f6bc:2;
534
            u8 gpsdev:1,            // 17f6c
535
               memlist:1,
536
               wrxrng:1,
537
               wrxrwf:1,
538
               wrxsrch:1,
539
               f6cb:2,
540
               scndrm2:1;
541
            u8 bskpax:3,           // 17f6d
542
               bskpa0:1,
543
               bskpa4:1,
544
               bskpa3:1,
545
               bskpa2:1,
546
               bskpa1:1;
547
            u8 f6e;
548
            u8 f6f;
549
            u8 f70;                // 17f70
550
            u8 f71;
551
            u8 f72a:7,
552
               dwrvtb:1;
553
            u8 f73a:7,
554
               sqlexp2:1;
555
            u8 f74a:5,
556
               rpt_arsb:1,
557
               f74b:2;
558
            u8 f75a:2,
559
                bscopeb:1,
560
                f75b:5;
561
            u8 f76;
562
            u8 f77;
563
            u8 f78a:5,              // 17f78
564
               dtmf_auto:1,
565
               bton:1,
566
               btaud:1;
567
            u8 f79a:3,              // 17f79
568
               timefmt:1,
569
               datefmt:4;
570
            u8 f7aa:3,
571
               beep2:1,             // Split beep: High
572
               f7ab:3,
573
               fvsrec:1;
574
            u8 f7ba:6,
575
               wrxams:2;
576
            u8 f7c;
577
            u8 bskpbx:3,           // 17f7d
578
               bskpb0:1,
579
               bskpb4:1,
580
               bskpb3:1,
581
               bskpb2:1,
582
               bskpb1:1;
583
            u8 f7e;
584
            u8 f7f;
585
            u8 f80[16];             // 17f80
586
            u8 f90[16];             // 17f90
587
            u8 fA0[16];             // 17fA0
588
            u8 fB0[16];             // 17fB0
589
            u8 FC0[16];             // 17fC0
590
            u8 FD0[16];             // 17fD0
591
            u8 FE0[16];             // 17fE0
592
            u8 FF0[16];             // 17fF0
593
        } setts;
594
    """
595

    
596
    @classmethod
597
    def get_prompts(cls):
598
        rp = chirp_common.RadioPrompts()
599
        rp.info = (
600
            "The duplex setting 'split' allows for non-standard offsets "
601
            "by configuring both a\n"
602
            "receive and transmit frequency. Set the receive frequency, "
603
            "then the duplex mode,\n"
604
            "then the desired transmit frequency as the offset.\n"
605
            "The offset value of a split channel is the transmit frequency, "
606
            "and can be modified.\n"
607
            "PMS (Programmable Memory Channel Scan) pairs are displayed "
608
            "as memory channels 1000-1099.\n"
609
            "The Clock Shift Function can be set in the Properties "
610
            "> Extra tab.\n"
611
            "The radio special tone modes (REV, PR, PAGER) can also be set "
612
            "in the Properties > Extra tab,\n"
613
            "but will be displayed as Tone in CHIRP.")
614
        if cls.FTM200:
615
            rp.pre_download = (
616
                "(OK then TX)\n"
617
                "With the USB cable connected to the radio rear data port, "
618
                "and the radio powered off-\n"
619
                "1. Long-Press F-Menu.\n"
620
                "2. Rotate the dial knob to menu 116: 'This -> Other'.\n"
621
                "3. Press the dial knob.\n"
622
                "4. Rotate to select OK, but don't press yet.\n"
623
                "5. Press the OK button below.\n"
624
                "6. Press the dial knob to begin sending data.\n"
625
                "7. Wait for Complete.\n")
626
            rp.pre_upload = (
627
                "(Rx then OK)\n"
628
                "With the USB cable connected to the radio rear data port, "
629
                "and the radio powered off-\n"
630
                "1. Long-Press F-Menu.\n"
631
                "2. Rotate the dial knob to menu 117: 'Other -> This'.\n"
632
                "3. Press the dial knob.\n"
633
                "4. Rotate to select OK, and press the knob.\n"
634
                "5. Press the OK button below.\n"
635
                "6. Wait for Complete.\n")
636
        else:
637
            rp.pre_download = (
638
                "(OK then TX)\n"
639
                "With the USB cable connected to the radio rear data port, "
640
                "and the radio powered off-\n"
641
                "1. Press both the power ON and F1 keys.\n"
642
                "2. Press the dial knob.\n"
643
                "3. Rotate the dial to show CLN TX.\n"
644
                "4. Press the OK button below/\n"
645
                "5. Press the dial knob to begin sending data.\n"
646
                "6. Wait for Complete.\n"
647
                "7. Long-press the power On button to leave the clone mode.")
648
            rp.pre_upload = (
649
                "(Rx then OK)\n"
650
                "With the USB cable connected to the radio rear data port, "
651
                "and the radio powered off-\n"
652
                "1. Press both the power ON and F1 keys.\n"
653
                "2. Press the dial knob, CLN RX is displayed.\n"
654
                "3. Press the dial knob again to put the radio in the "
655
                "receiving state.\n"
656
                "4. Press the OK button below.\n"
657
                "5. Wait for Complete.\n"
658
                "6. Long-press the power On button to leave the clone mode.")
659
        return rp
660

    
661
    def _read(self, blck, blsz):
662
        # be very patient at first block
663
        if blck == 0:
664
            attempts = 60
665
        else:
666
            attempts = 5
667
        for _i in range(0, attempts):
668
            data = self.pipe.read(blsz)
669
            if data:
670
                break
671
            time.sleep(0.5)
672
        if len(data) == blsz:
673
            LOG.debug("Received block %s, sub %s" % (hex(data[0]),
674
                      hex(data[1])))
675
            checksum = data[blsz - 1]
676
            cs = 0
677
            for i in data[:-1]:
678
                cs = (cs + i) % 256
679
            if cs != checksum:
680
                raise Exception("Checksum Failed [%02X<>%02X] block %02X" %
681
                                (checksum, cs, blck))
682
            # Remove the 2-byte block header and checksum
683
            data = data[2:blsz - 1]
684
        else:
685
            raise Exception("Unable to read block %i expected %i got %i"
686
                            % (blck, blsz, len(data)))
687
        return data
688

    
689
    def _clone_in(self):
690
        # Be very patient with the radio
691
        self.pipe.timeout = 2
692
        self.pipe.baudrate = self.BAUD_RATE
693
        self.pipe.bytesize = self.COM_BITS
694
        self.pipe.parity = self.COM_PRTY
695
        self.pipe.stopbits = self.COM_STOP
696
        self.pipe.rtscts = False
697
        data = b""
698
        status = chirp_common.Status()
699
        status.msg = "Cloning from radio"
700
        nblocks = 768
701
        status.max = nblocks - 1
702
        blksz = 131
703
        for block in range(0, nblocks):     # SPS - reads to nblocks-1
704
            LOG.debug("Reading packet %d" % block)
705
            data += self._read(block, blksz)
706
            self.pipe.write(CMD_ACK)
707
            status.cur = block
708
            self.status_fn(status)
709
        return memmap.MemoryMapBytes(data)
710

    
711
    def _write(self, nblk, nsub, pntr, bksz, zblk):
712
        LOG.debug("Block %02X %02X" % (nblk, nsub))
713
        if self.TESTME:
714
            LOG.warning("Block %02X %02X" % (nblk, nsub))
715
        data = struct.pack('B', nblk)
716
        data += struct.pack('B', nsub)
717
        if zblk:
718
            blkdat = bytearray(bksz)    # All Zeros
719
        else:
720
            blkdat = self.get_mmap()[pntr:pntr + bksz]
721
        data += blkdat
722
        cs = 0
723
        for i in data:
724
            cs = (cs + i) % 256
725
        LOG.debug("Checksum %s" % hex(cs))
726
        if self.TESTME:
727
            LOG.warning("Checksum %s" % hex(cs))
728
        data += struct.pack('B', cs)
729
        LOG.debug("Writing %d bytes:\n%s" % (len(data),
730
                                             util.hexprint(data)))
731
        if self.TESTME:      # Dont send data yet
732
            LOG.warning("Writing %d bytes:\n%s" % (len(data),
733
                                                   util.hexprint(data)))
734
            buf = CMD_ACK
735
        else:
736
            self.pipe.write(data)
737
            buf = self.pipe.read(1)
738
        if not buf or buf[:1] != CMD_ACK:
739
            time.sleep(0.5)
740
            buf = self.pipe.read(1)
741
        if not buf or buf[:1] != CMD_ACK:
742
            return False
743
        return True
744

    
745
    def _clone_out(self):
746
        self.pipe.baudrate = self.BAUD_RATE
747
        self.pipe.bytesize = self.COM_BITS
748
        self.pipe.parity = self.COM_PRTY
749
        self.pipe.stopbits = self.COM_STOP
750
        self.pipe.rtscts = False
751

    
752
        status = chirp_common.Status()
753
        status.msg = "Cloning to radio"
754
        blksz = 128     # actual data; excluding block, sub, checksum
755
        pkt = 0
756
        pos = 0
757
        block = 0
758
        wrtzero = False        # Special last block flag
759
        nblocks = 767          # last block number
760
        status.max = nblocks - 2
761
        # This sucker took FOREVER to figure out the repeating block
762
        # + sub-block + special EoD numbering!
763
        while pkt <= nblocks:
764
            LOG.debug("Writing packet #: %d." % pkt)
765
            if self.TESTME:
766
                LOG.warning("Writing packet #: %d , pos %x." % (pkt, pos))
767
            time.sleep(0.01)
768
            sub2 = True
769
            sublk = 0
770
            if pkt == 0:
771
                block = pkt
772
                sub2 = False
773
            elif pkt == 1:
774
                block = pkt
775
            elif pkt == 7:          # Block 04 00 is skipped !!??
776
                block = 0x04
777
                sublk = 0x80
778
                sub2 = False        # block increments to 05 00
779
            elif pkt == 510:        # Reset to block 0
780
                block = 0
781
            elif pkt == 766:
782
                block = 0xff
783
                sublk = 0xfd
784
                sub2 = False
785
            elif pkt == 767:
786
                block = 0xff
787
                sublk = 0xfe
788
                sub2 = False
789
                # pkt 767, block ff fe must be all zeros!!???
790
                wrtzero = True
791
            rslt = self._write(block, sublk, pos, blksz, wrtzero)
792
            if rslt and sub2:       # write same block, new sublk
793
                sublk = 0x80
794
                pkt += 1
795
                pos += blksz
796
                LOG.debug("Writing packet #: %.d" % pkt)
797
                if self.TESTME:
798
                    LOG.warning("Writing packet #: %d , pos %x." % (pkt, pos))
799
                rslt = self._write(block, sublk, pos, blksz, wrtzero)
800
            if not rslt:
801
                raise Exception("Radio did not ack block %i %i" %
802
                                (block, sublk))
803
            pos += blksz
804
            block += 1
805
            pkt += 1
806
            status.cur = pkt
807
            self.status_fn(status)
808
        return
809

    
810
    def sync_in(self):
811
        try:
812
            self._mmap = self._clone_in()
813
        except errors.RadioError:
814
            raise
815
        except Exception as e:
816
            raise errors.RadioError("Failed to communicate with radio: %s"
817
                                    % e)
818
        self.process_mmap()
819

    
820
    def sync_out(self):
821
        try:
822
            self._clone_out()
823
        except errors.RadioError:
824
            raise
825
        except Exception as e:
826
            raise errors.RadioError("Failed to communicate with radio: %s"
827
                                    % e)
828

    
829
    def process_mmap(self):
830
        self._memobj = bitwise.parse(self.MEM_FORMAT, self._mmap)
831

    
832
    def get_features(self):
833
        rf = chirp_common.RadioFeatures()
834
        rf.has_dtcs_polarity = False
835
        rf.has_bank = False
836
        rf.has_dtcs = True
837
        rf.has_cross = True
838
        rf.has_tuning_step = True
839
        rf.has_ctone = False                    # Common Tones
840
        rf.has_rx_dtcs = False                  # Common codes
841
        rf.has_settings = True
842
        rf.can_odd_split = True
843
        rf.valid_modes = [x for x in self.MODES if x in chirp_common.MODES]
844
        rf.valid_tmodes = self.TMODES
845
        rf.valid_cross_modes = self.CROSS_MODES
846
        rf.valid_duplexes = self.DUPLEX
847
        rf.valid_tuning_steps = self.T_STEPS
848
        rf.valid_bands = self.VALID_BANDS
849
        rf.valid_power_levels = self.POWER_LEVELS
850
        rf.valid_characters = "".join(self.CHARSET)
851
        rf.valid_name_length = self.NAME_LEN
852
        rf.memory_bounds = (1, 1099)
853
        rf.valid_skips = self.SKIPS
854
        return rf
855

    
856
    def get_raw_memory(self, number):
857
        return repr(self._memobj.main[number - 1])
858

    
859
    def validate_memory(self, mem):
860
        msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem)
861
        return msgs
862

    
863
    def filter_name(self, namsx):
864
        """Name must be <= NAME_LEN and contain only CHARSET chars"""
865
        tx = namsx.strip()
866
        sx = ""
867
        if self.FTM200 is False:
868
            tx = tx.upper()
869
        for i in range(0, len(tx)):
870
            if i >= self.NAME_LEN:
871
                break
872
            if tx[i] in self.CHARSET:
873
                sx += tx[i]
874
            else:
875
                sx = "?"
876
        return sx
877

    
878
    def _freqdcode(self, frqb):
879
        """Decode .0025 Mhz upper 2 MSB of freq"""
880
        # frqb is u24: 2bits suffix, 22 bits bcd
881
        sx = "%06X" % frqb
882
        v0 = int(sx[0], 16) * 625
883
        v1 = int(sx[1])     # 100 Mhz
884
        v2 = int(sx[2:4])   # 10 Mhz
885
        v3 = int(sx[4:])    # 100 Khz
886
        vx = (v1 * 10000) + (v2 * 100) + v3
887
        vx = vx * 10000 + v0
888
        return vx
889

    
890
    def _freqncode(self, frq):
891
        """Encode .0625 Mhz in upper MSB of u24 bcd freq"""
892
        v0 = frq % 10000    # .025 value
893
        bx = v0 // 625
894
        bx = bx * 0x100000
895
        vx = int((frq - v0) / 10000)
896
        sx = "%06d" % vx    # Ex: 012345 for frq = 123.45 MHz
897
        v1 = int("0x" + sx[0:2], 16)
898
        v2 = int("0x" + sx[2:4], 16)
899
        v3 = int("0x" + sx[4:], 16)
900
        frqb = bx + v1 * 0x10000 + v2 * 0x100 + v3
901
        return frqb
902

    
903
    def _b2s(self, bary, term=0xff):
904
        """Convert byte array into string """
905
        strx = ""
906
        for i in bary:
907
            if i == term:
908
                break
909
            strx += chr(i)
910
        return strx
911

    
912
    def _s2b(self, setting, obj, atrb, mxk, pad=0xff, upc=True):
913
        """Callback: Convert string to byte array, pad to mxk chars"""
914
        sx = str(setting.value)
915
        sx = sx.strip()
916
        if upc:
917
            sx = sx.upper()
918
        ary = b""
919
        v1 = len(sx)
920
        for vx in range(0, mxk):
921
            if vx < v1:
922
                ary += bytes(sx[vx], 'utf-8')
923
            else:
924
                ary += pad.to_bytes(1, sys.byteorder)
925
        setattr(obj, atrb, ary)
926
        return
927

    
928
    def _c2u8(self, setting, obj, atrb):
929
        """Callback: Convert single char string to u8"""
930
        b0 = str(setting.value)
931
        vx = ord(b0)
932
        setattr(obj, atrb, vx)
933
        return
934

    
935
    def get_memory(self, number):
936
        mem = chirp_common.Memory()
937
        if number < 1000:      # channel memory
938
            _mem = self._memobj.main[number - 1]
939
            _tag = self._memobj.names[number - 1]
940
        else:
941
            _mem = self._memobj.pms[number - 1000]
942
            _tag = self._memobj.pms_name[number - 1000]
943
        mem.number = number
944
        if mem.number == 1:
945
            mem.immutable += ["empty"]
946
        if not _mem.used:
947
            mem.empty = True
948
            return mem
949
        mem.freq = self._freqdcode(_mem.freq)
950
        mem.rtone = chirp_common.TONES[_mem.rtone]
951
        mem.mode = self.MODES[_mem.mode]
952
        mem.duplex = self.DUPLEX[_mem.duplex]
953
        if (_mem.duplex == 4):          # Split mode
954
            mem.offset = self._freqdcode(_mem.frqtx)
955
        else:
956
            mem.offset = int(_mem.offset) * 50000
957
        # Re-map funky radio tone and cross-modes
958
        tmd = _mem.tmode   # 0,1,2
959
        cmx = 0
960
        if _mem.tmode == 3:     # DCS
961
            tmd = 5             # Cross
962
            cmx = 0             # -> DTCS
963
        if _mem.tmode == 4:     # Rev Tone
964
            tmd = 3             # TSQL-R
965
        if _mem.tmode == 5:     # PR Freq
966
            tmd = 1
967
        if _mem.tmode == 6:     # Pager
968
            tmd = 1
969
        if _mem.tmode == 7:     # Tx DTCS, Open RX
970
            tmd = 4             # DTCS
971
        elif _mem.tmode == 8:   # Tone - DCS
972
            tmd = 5             # Cross
973
            cmx = 1             # Tone -> DTCS
974
        elif _mem.tmode == 9:   # D CD-> Tone
975
            tmd = 5             # Cross
976
            cmx = 2             # DTCS->Tone
977
        mem.tmode = self.TMODES[tmd]
978
        mem.cross_mode = self.CROSS_MODES[cmx]
979
        mem.skip = self.SKIPS[_mem.skip]
980
        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
981
        vx = _mem.power
982
        mem.power = self.POWER_LEVELS[vx]
983
        mem.tuning_step = self.T_STEPS[_mem.step]
984
        tx = self._b2s(_tag.chname, 0xff).strip()
985
        if self.FTM200 is False:
986
            tx = tx.upper()
987
        mem.name = tx
988
        # Echo name string back into memory.
989
        # In case it got loaded flakey in radio; trailing spaces
990
        for i in range(0, self.NAME_LEN):
991
            if i < len(mem.name):
992
                _tag.chname[i] = ord(mem.name[i])
993
            else:
994
                _tag.chname[i] = 0xff
995
        # mem.extra: Clock Type A/B
996
        mem.extra = RadioSettingGroup("extra", "Extra")
997
        options = ["Auto", "On"]
998
        rx = RadioSettingValueList(options, options[_mem.clktyp])
999
        rs = RadioSetting("clktyp", "Clock Shift (CLK.TYP)", rx)
1000
        mem.extra.append(rs)
1001

    
1002
        options = ["None", "REV Tone", "PR Freq", "Pager"]
1003
        tmd = _mem.tmode - 3
1004
        if _mem.tmode < 4:
1005
            tmd = 0
1006
        if _mem.tmode > 6:
1007
            tmd = 0
1008
        rx = RadioSettingValueList(options, options[tmd])
1009
        rs = RadioSetting("spclmode", "Radio Special Tone Mode", rx)
1010
        mem.extra.append(rs)
1011

    
1012
        options = []
1013
        for vx in range(300, 3100, 100):
1014
            sx = str(vx)
1015
            options.append(sx)
1016
        if _mem.prfreq > 30:
1017
            _mem.prfreq = 30       # Can get loaded wrong...?
1018
        if _mem.prfreq < 3:
1019
            _mem.prfreq = 3
1020
        v0 = _mem.prfreq * 100       # .prfreq is 0x03 - 0x1E (3 -30 decimal)
1021
        sx = str(v0)
1022
        rx = RadioSettingValueList(options, sx)
1023
        rs = RadioSetting("prfreq", "PR (User) Freq Htz", rx)
1024
        mem.extra.append(rs)
1025
        return mem
1026

    
1027
    def set_memory(self, mem):
1028
        if mem.number < 1000:
1029
            _mem = self._memobj.main[mem.number - 1]
1030
            _tag = self._memobj.names[mem.number - 1]
1031
        else:
1032
            _mem = self._memobj.pms[mem.number - 1000]
1033
            _tag = self._memobj.pms_name[mem.number - 1000]
1034
        if mem.empty:       # Chan 1 is immutable for empty
1035
            _mem.used = 0
1036
            return mem
1037
        _mem.used = 1
1038
        _mem.freq = self._freqncode(mem.freq)
1039
        _mem.mode = self.MODES.index(mem.mode)
1040
        _mem.duplex = self.DUPLEX.index(mem.duplex)
1041
        if _mem.duplex == 4:         # split mode
1042
            _mem.frqtx = self._freqncode(mem.offset)
1043
        else:
1044
            _mem.offset = mem.offset / 50000
1045
        tmd = 0     # no tone
1046
        sqlx = 0
1047
        # Re-map Tmode and Cross mode to radio combined
1048
        # Using MY TMODE list index
1049
        tx = mem.tmode.strip()
1050
        cmx = mem.cross_mode.strip()
1051
        if tx == "Cross":
1052
            if cmx == "->DTCS":
1053
                tmd = 3
1054
            if cmx == "Tone->DTCS":
1055
                tmd = 8
1056
            if cmx == "DTCS->Tone":
1057
                tmd = 9
1058
            sqlx = 1
1059
        else:
1060
            if tx == "Tone":
1061
                tmd = 1
1062
            if tx == "TSQL":
1063
                tmd = 2
1064
            if tx == "TSQL-R":
1065
                tmd = 4
1066
            if tx == "DTCS":
1067
                tmd = 7
1068
        _mem.tmode = tmd
1069
        _mem.rtone = chirp_common.TONES.index(mem.rtone)
1070
        _mem.step = self.T_STEPS.index(mem.tuning_step)
1071
        _mem.skip = self.SKIPS.index(mem.skip)
1072
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
1073
        sx = mem.power
1074
        if sx is None:
1075
            sx = self.POWER_LEVELS[1]    # Mid
1076
        _mem.power = self.POWER_LEVELS.index(sx)
1077
        setattr(self._memobj.setts, "sqlexp1", sqlx)
1078
        setattr(self._memobj.setts, "sqlexp2", sqlx)
1079
        tx = self.filter_name(mem.name)
1080
        for i in range(0, self.NAME_LEN):
1081
            if i < len(tx):
1082
                _tag.chname[i] = ord(tx[i])
1083
            else:
1084
                _tag.chname[i] = 0xff
1085
        for setting in mem.extra:
1086
            if setting.get_name() == "clktyp":
1087
                sx = str(setting.value)
1088
                vx = 0
1089
                if sx == "On":
1090
                    vx = 1
1091
                setattr(_mem, "clktyp", vx)
1092
            if setting.get_name() == "spclmode":
1093
                sx = str(setting.value)
1094
                tmd = 0
1095
                tx = mem.tmode
1096
                if sx == "None":
1097
                    if tx == "PR Freq" or tx == "Pager":
1098
                        tmd = 0      # Reset from special to none
1099
                    else:
1100
                        tmd = _mem.tmode    # no change
1101
                        if sx == "PR Freq":
1102
                            tmd = 5
1103
                        if sx == "Pager":
1104
                            tmd = 6
1105
                setattr(_mem, "tmode",  tmd)
1106
            if setting.get_name() == "prfreq":
1107
                sx = str(setting.value)
1108
                vx = int(sx) // 100
1109
                setattr(_mem, "prfreq", vx)
1110
        return
1111

    
1112
    def _micpkey(self, setting, obj, atrb, opts):
1113
        """Callback: Adjust stored value for microphone P keys"""
1114
        sx = str(setting.value)      # the list string
1115
        v0 = opts.index(sx)     # the options array index, 0-based
1116
        v1 = v0 + 0xdc
1117
        if self.FTM200 is False:
1118
            v1 = v0 + 0xdd
1119
            if v1 == 0xe6:
1120
                v1 = 0xe7        # skip e6
1121
        setattr(obj, atrb, v1)
1122
        return
1123

    
1124
    def _setfunc(self, setting, obj, atrb, ndx):
1125
        """Callback: Convert boolean to function setting"""
1126
        # Stored in shared memory 'share1' as 2 bytes.
1127
        # When set: first byte is menu number, 2nd is 00
1128
        # Unset: ff, ff
1129
        bx = bool(setting.value)      # boolean
1130
        # ndx is 0-based menu number
1131
        x1 = ndx * 2    # _shr index of first byte
1132
        v0 = 0xff       # disabled
1133
        v1 = 0xff
1134
        if bx:
1135
            v0 = ndx
1136
            v1 = 0
1137
        setattr(obj[x1], atrb, v0)
1138
        setattr(obj[x1 + 1], atrb, v1)
1139
        return
1140

    
1141
    def _setQA(self, setting, obj, atrb, opts):
1142
        """Callback: FTM-200 menu list selection to menu code"""
1143
        sx = str(setting.value)
1144
        v0 = opts.index(sx)
1145
        setattr(obj, atrb, v0)
1146
        return
1147

    
1148
    def _sqlexp1(self, setting, obj, atrb):
1149
        """ Callback: SQL.EXP requires setting two objects """
1150
        sx = str(setting.value)
1151
        bx = False
1152
        if sx == "On":
1153
            bx = True
1154
        setattr(obj, atrb, bx)      # sqlexp1
1155
        setattr(obj, "sqlexp2", bx)
1156
        return
1157

    
1158
    def _settot(self, setting, obj, atrb, opts):
1159
        """Callback: convert non-linear TOT values"""
1160
        sx = str(setting.value)
1161
        v0 = opts.index(sx)
1162
        v1 = v0
1163
        if v0 > 0:
1164
            v1 = v0 + 5
1165
        if v0 > 3:
1166
            v1 = v0 - 3
1167
        setattr(obj, atrb, v1)
1168
        return
1169

    
1170
    def _adjint(self, setting, obj, atrb):
1171
        """Callback: decrement integer value to 0-based"""
1172
        v0 = int(setting.value)
1173
        v1 = v0 - 1
1174
        setattr(obj, atrb, v1)
1175
        return
1176

    
1177
    def _unpack_str(self, codestr):
1178
        """Convert u8 DTMF array to a string: NOT a callback."""
1179
        sx = ""
1180
        for i in range(0, 16):    # unpack up to ff
1181
            if codestr[i] != 0xff:
1182
                if codestr[i] == 0x0E:
1183
                    sx += "*"
1184
                elif codestr[i] == 0x0F:
1185
                    sx += "#"
1186
                else:
1187
                    sx += format(int(codestr[i]), '0X')
1188
        return sx
1189

    
1190
    def _pack_chars(self, setting, obj, atrb, ndx):
1191
        """Callback to build 0-9,A-D,*# nibble array from string"""
1192
        # String will be ff padded to 16 bytes
1193
        # Chars are stored as hex values
1194
        ary = []
1195
        sx = str(setting.value).upper().strip()
1196
        sx = sx.strip()       # trim spaces
1197
        # Remove illegal characters first
1198
        sty = ""
1199
        for j in range(0, len(sx)):
1200
            if sx[j] in self.DTMF_CHARS:
1201
                sty += sx[j]
1202
        for j in range(0, 16):
1203
            if j < len(sty):
1204
                if sty[j] == "*":
1205
                    chrv = 0xE
1206
                elif sty[j] == "#":
1207
                    chrv = 0xF
1208
                else:
1209
                    chrv = int(sty[j], 16)
1210
            else:   # pad to 16 bytes
1211
                chrv = 0xFF
1212
            ary.append(chrv)    # append byte
1213
        setattr(obj[ndx], atrb, ary)
1214
        return
1215

    
1216
    def _pmgset(self, setting, obj, ndx):
1217
        """Callback: Convert pmg chan to 0-based, and store """
1218
        v0 = int(setting.value)
1219
        if v0 == 0:     # Deleted
1220
            setattr(obj[ndx], "za", 0xff)
1221
            setattr(obj[ndx], "fb", 0xff)
1222
            setattr(obj[ndx], "chnp", 0)
1223
            return
1224
        v1 = v0 - 1     # 0-based
1225
        setattr(obj[ndx], "za", 0)
1226
        setattr(obj[ndx], "fb", 0xff)
1227
        setattr(obj[ndx], "chnp", v1)
1228
        return
1229

    
1230
    def _adjlist(self, setting, obj, atrb, opts, ofst):
1231
        """Callback: Universal add/subtract list index"""
1232
        sx = str(setting.value)      # the list string
1233
        vx = opts.index(sx) + ofst
1234
        if atrb == "tzone":             # special case for time zone
1235
            if vx < 0:
1236
                vx = abs(vx) + 0x80
1237
        setattr(obj, atrb, vx)
1238
        return
1239

    
1240
    def _ft2apo(self, setting, obj):
1241
        """Callback: Set FTM-200 APO coded decimal in 2 bytes"""
1242
        sx = str(setting.value)     # Ex '1.5 Hours'
1243
        if sx == "Off":
1244
            setattr(obj, "apo", 0)
1245
            setattr(obj, "apo1", 0)
1246
            return
1247
        if "." in sx:
1248
            vx = sx.index(".")
1249
            v1 = int("0x" + sx[:vx], 16)  # encode as hcd
1250
            v2 = 1
1251
        else:
1252
            vx = sx.index(" H")
1253
            v1 = int(sx[:vx])
1254
            v2 = 0
1255
        setattr(obj, "apo", v1)
1256
        setattr(obj, "apo1", v2)
1257
        return
1258

    
1259
    def _ft2lcd(self, setting, obj, atrb):
1260
        """Callback: to set LCD Display Brightness"""
1261
        sx = str(setting.value)
1262
        v0 = 3
1263
        if sx == "Mid":
1264
            v0 = 1
1265
        if sx == "Full":
1266
            v0 = 2
1267
        setattr(obj, atrb, v0)
1268
        return
1269

    
1270
    def _scndrm(self, setting, obj):
1271
        """Callback:FTM-200 Scan Dual Rcv Mode """
1272
        # Requires setting a bit in 2 bytes
1273
        sx = str(setting.value)
1274
        v0 = 0
1275
        v1 = 0
1276
        if "Pri" in sx:
1277
            v0 = 1
1278
        if "A-B" in sx:
1279
            v1 = 1
1280
        setattr(obj, "scndrm1", v0)
1281
        setattr(obj, "scndrm2", v1)
1282
        return
1283

    
1284
    def _skp_other(self, setting, obj, bnd, opts):
1285
        """Callback: FTM-200 Band Skip Other requires 2 bits"""
1286
        sx = str(setting.value)     # Off/On
1287
        v0 = 0
1288
        if "On" in sx:
1289
            v0 = 1
1290
        b0 = "bskpa0"
1291
        b3 = "bskpa3"
1292
        if "B" in bnd:
1293
            b0 = "bskpb0"
1294
            b3 = "bskpb3"
1295
        setattr(obj, b0, v0)
1296
        setattr(obj, b3, v0)
1297
        return
1298

    
1299
    def _beepset(self, setting, obj, optns):
1300
        """Callback: Stupid split-beep"""
1301
        sx = str(setting.value)
1302
        vx = optns.index(sx)    # 0,1,2
1303
        v0 = vx & 1       # Off/Low
1304
        v1 = 0            # High
1305
        if vx == 2:
1306
            v0 = 1
1307
            v1 = 1
1308
        setattr(obj, "beep1", v0)
1309
        setattr(obj, "beep2", v1)
1310
        return
1311

    
1312
    def _xmsg(self, setting, obj, indx, xmp, mode=0):
1313
        """Callback: Wires-X message # indx, text encoder """
1314
        # mode=0 is GM Message, mode=1 is Category
1315
        sx = str(setting.value).strip()
1316
        tx = b""
1317
        v0 = 128
1318
        v1 = 0xff
1319
        hx = "msg%02d" % indx
1320
        if mode == 1:
1321
            v0 = 16
1322
            v1 = 0xca
1323
            hx = "c%d" % indx
1324
        for cx in range(0, v0):
1325
            if cx < len(sx):
1326
                vx = sx[cx]
1327
                try:
1328
                    v2 = xmp.index(vx)
1329
                except Exception:
1330
                    raise Exception("Unable to decode character hex %x at "
1331
                                    "index %d of %s" % (vx, cx, hx))
1332
                tx += util.int_to_byte(v2)
1333
            else:
1334
                tx += util.int_to_byte(v1)    # pad
1335
        setattr(obj, hx, tx)
1336
        return
1337

    
1338
    def _wrxfrq(self, setting, obj, atrb):
1339
        """Callback: Encode the 3-byte non-mem channel frequency"""
1340
        vx = float(setting.value) * 1000000
1341
        bx = self._freqncode(vx)
1342
        setattr(obj, atrb, bx)
1343
        return
1344

    
1345
    def _aprsfx(self, setting, obj, atrb, pad):
1346
        """Callback: Trap the APRS callsign SSID"""
1347
        v0 = int(setting.value)
1348
        vx = v0
1349
        if (pad == 0xca) and (v0 == 0):
1350
            vx = pad
1351
        if (pad == 0x2a) and (v0 == 16):
1352
            vx = pad
1353
        setattr(obj, atrb, vx)
1354
        return
1355

    
1356
    def _aprspop(self, setting, obj, atrb):
1357
        """Callback: APRS Beacon and Message Popup times; not 1:1"""
1358
        # Can these radiso get any wierder?
1359
        vx = int(setting.value)
1360
        v0 = 0          # Off
1361
        if vx == 1:
1362
            v0 = 3
1363
        if vx == 2:
1364
            v0 = 5
1365
        if vx == 3:
1366
            v0 = 10
1367
        if vx == 4:
1368
            v0 = 0xff   # Hold
1369
        setattr(obj, atrb, v0)
1370
        return
1371

    
1372
    def _enchcd(self, setting, obj, atrb):
1373
        """Callback: Generic convert 1-byte decimal value to hex-coded"""
1374
        sx = str(setting.value)    # Decimal value, Ex: "33"
1375
        v0 = int("0x" + sx, 16)    # encode as hcd 0x33
1376
        setattr(obj, atrb, v0)
1377
        return
1378

    
1379
    def _mdot(self, setting, obj, atrb1, atrb2):
1380
        """Callback: Convert lat/long mm/mm to  u8 and u16"""
1381
        # Half as hex coded decimal, half as binary !!??
1382
        vx = float(setting.value)   # mm.mm
1383
        tx = math.modf(vx)
1384
        sx = str(int(tx[1]))
1385
        v0 = int("0x" + sx, 16)     # hcd
1386
        v1 = int(tx[0] * 1000)      # 2-byte binary
1387
        setattr(obj, atrb1, v0)
1388
        setattr(obj, atrb2, v1)
1389
        return
1390

    
1391
    def _getdgpath(self, pn):
1392
        """Method for extracting Digipeater route paths"""
1393
        # pn is path number 0 - 5
1394
        _shr = self._memobj.share1
1395
        sx = ""
1396
        x1 = pn * 8
1397
        for i in range(x1, x1 + 6):   # route name: 6 bytes
1398
            v0 = _shr[i].shbyt
1399
            if v0 != 0xca:
1400
                sx += chr(v0)
1401
        vx = _shr[x1 + 6].shbyt     # route suffix 0-15
1402
        if vx == 0xca:
1403
            vx = 0
1404
        return sx, vx
1405

    
1406
    def _putdgpname(self, setting, pn):
1407
        """Callback: store Digipath name"""
1408
        # Stored in 'share1' shared memory
1409
        # pn is path number 0-5
1410
        _shr = self._memobj.share1
1411
        sx = str(setting.value)     # modified path name; 0-6 chars
1412
        sx = sx.upper().strip()
1413
        x1 = pn * 8                 # starting position in _shr
1414
        sp = 0
1415
        for i in range(x1, x1 + 6):
1416
            if sp >= len(sx):
1417
                v1 = 0xca
1418
            else:
1419
                v1 = ord(sx[sp])
1420
            setattr(_shr[i], "shbyt", v1)
1421
            sp += 1
1422
        return
1423

    
1424
    def _putdgpsfx(self, setting, pn):
1425
        """Callback: Digipath, Ringer SSID"""
1426
        _shr = self._memobj.share1
1427
        v1 = int(setting.value)
1428
        x1 = pn * 8 + 6
1429
        setattr(_shr[x1], "shbyt", v1)
1430
        return
1431

    
1432
    def _rnglmt(self, setting, obj, atrb):
1433
        """Callback: APRS Beacon Range Limits"""
1434
        v1 = int(setting.value)      # index
1435
        vx = v1         # for 0, 1
1436
        if atrb == "aprbrrr":     # Range Ringer
1437
            if v1 == 2:
1438
                vx = 5
1439
            if v1 == 3:
1440
                vx = 10
1441
            if v1 == 4:
1442
                vx = 50
1443
            if v1 == 5:
1444
                vx = 100
1445
        else:                     # Filter Range
1446
            if v1 == 2:
1447
                vx = 10
1448
            if v1 == 3:
1449
                vx = 100
1450
            if v1 == 4:
1451
                vx = 1000
1452
            if v1 == 5:
1453
                vx = 3000
1454
        setattr(obj, atrb, vx)
1455
        return
1456

    
1457
    def _datefmt(self, setting, obj, atrb):
1458
        """Callback: Stupid index jump in date format list"""
1459
        vx = int(setting.value)
1460
        if vx == 3:
1461
            vx = 4
1462
        setattr(obj, atrb, vx)
1463
        return
1464

    
1465
    def get_settings(self):
1466
        _setx = self._memobj.setts
1467
        _mic = self._memobj.micset
1468
        _wierd = self._memobj.wierd
1469
        _dtm = self._memobj.dtmfcode
1470

    
1471
        cfg = RadioSettingGroup("cfg", "Config")
1472
        dsp = RadioSettingGroup("dsp", "Display")
1473
        fmenu = RadioSettingGroup("fmnu", "Quick Functions")
1474
        mic = RadioSettingGroup("mic", "Microphone")
1475
        sig = RadioSettingGroup("sig", "Signalling")
1476
        opts = RadioSettingGroup("opts", "Options")
1477
        dtmf = RadioSettingGroup("dtmf", "DTMF")
1478
        scan = RadioSettingGroup("scan", "Scanning")
1479
        other = RadioSettingGroup("other", "Other")
1480

    
1481
        if self.FTM200:
1482
            dat = RadioSettingGroup("dat", "Data")
1483
            wires = RadioSettingGroup("wires", "GM/WIRES-X")
1484
            aprscom = RadioSettingGroup("aprscom", "APRS Settings")
1485
            aprsdgp = RadioSettingGroup("aprsdgp", "APRS Digipeater")
1486
            aprsmsg = RadioSettingGroup("aprsmsg", "APRS Messages")
1487
            bcnfltr = RadioSettingGroup("bcnfltr", "APRS Beacon Filter")
1488
            bcnunit = RadioSettingGroup("bcnunit", "APRS Beacon Units")
1489
            bcnrngr = RadioSettingGroup("bcnrngr", "APRS Beacon Ringer")
1490
            bcnstat = RadioSettingGroup("bcnstat", "APRS Beacon Status")
1491
            bcnsmrt = RadioSettingGroup("bcnsmrt", "APRS Smart Beaconing")
1492
            group = RadioSettings(cfg, dsp, fmenu, mic, sig, opts, dtmf,
1493
                                  scan, dat, wires, aprscom, aprsmsg,
1494
                                  aprsdgp, bcnfltr, bcnunit, bcnrngr,
1495
                                  bcnstat, bcnsmrt, other)
1496
        else:
1497
            group = RadioSettings(cfg, dsp, fmenu, mic, sig, opts, dtmf,
1498
                                  scan, other)
1499

    
1500
        menu_items = ["01: Auto Power Off (APO)", "02: ARTS Mode",
1501
                      "03: ARTS Interval", "04: Busy Channel Lockout (BCLO)",
1502
                      "05: Beep", "06: Bell", "07: Clock Type",
1503
                      "08: LCD Dimmer", "09: DTMF Manual/Auto",
1504
                      "10: DTMF TX", "11:DTMF Codes", "12: Home",
1505
                      "13: Microphone Gain", "14: Microphone P Keys",
1506
                      "15: Pager TX/RX", "16: Packet Speed", "17:RX Mode",
1507
                      "18: Band Select", "19: Repeater Reverse",
1508
                      "20: Repeater Set", "21: Repeater Other", "22: Scan On",
1509
                      "23: Scan Type", "24: Squelch Type", "25:Squelch Code",
1510
                      "26: Squelch Expansion", "27: Step",
1511
                      "28: Radio Temperature", "29: Time Out Timer (TOT)",
1512
                      "30: TX Power", "31: Version", "32: Voltage",
1513
                      "33: Width", "34: Weather Alert", "35: Bluetooth"]
1514
        offon = ["Off", "On"]
1515
        onoff = ["On", "Off"]     # Inverted logic
1516

    
1517
        # Begin Config settings
1518
        if self.FTM200:
1519
            sx = self._b2s(_mic.csign, 0xff)
1520
            rx = RadioSettingValueString(0, 10, sx, False)
1521
            rs = RadioSetting("micset.csign", "Call Sign Label", rx)
1522
            rs.set_apply_callback(self._s2b, _mic, "csign", 10)
1523
            cfg.append(rs)
1524

    
1525
            options = ["yyyy/mmm/dd", "yyyy/dd/mmm", "mmm/dd/yyyy",
1526
                       "dd/mmm/yyyy"]
1527
            vx = _setx.datefmt
1528
            if vx == 4:
1529
                vx = 3      # Just to make my life difficult!
1530
            rx = RadioSettingValueList(options, options[vx])
1531
            rs = RadioSetting("setts.datefmt", "Date Format", rx)
1532
            rs.set_apply_callback(self._datefmt, _setx, "datefmt")
1533
            cfg.append(rs)
1534

    
1535
            options = ["24 Hour", "12 Hour"]
1536
            rx = RadioSettingValueList(options, options[_setx.timefmt])
1537
            rs = RadioSetting("setts.timefmt", "Time Format", rx)
1538
            cfg.append(rs)
1539

    
1540
            options = []
1541
            v0 = -14.0
1542
            for vx in range(0, 57):
1543
                options.append(str(v0))
1544
                v0 += 0.5
1545
            v0 = _setx.tzone
1546
            v1 = v0 + 28       # positive offset; index 28 is GMT
1547
            if v0 & 0x80:      # negative, before GMT
1548
                v1 = 28 - (v0 & 0x1f)
1549
            rx = RadioSettingValueList(options, options[v1])
1550
            rs = RadioSetting("setts.tzone", "Time Zone GMT +/- hours", rx)
1551
            rs.set_apply_callback(self._adjlist, _setx, "tzone", options, -28)
1552
            cfg.append(rs)
1553

    
1554
            rx = RadioSettingValueList(offon, offon[_setx.rpt_ars])
1555
            rs = RadioSetting("setts.rpt_ars",
1556
                              "A: Repeater Auto Offset Enabled", rx)
1557
            cfg.append(rs)
1558
            rx = RadioSettingValueList(offon, offon[_setx.rpt_arsb])
1559
            rs = RadioSetting("setts.rpt_arsb",
1560
                              "B: Repeater Auto Offset Enabled", rx)
1561
            cfg.append(rs)
1562

    
1563
            options = ["Off", "0.5 Hours", "1 Hours", "1.5 Hours", "2 Hours",
1564
                       "3 Hours", "4 Hours", "5 Hours", "6 Hours", "7 Hours",
1565
                       "8 Hours", "9 Hours", "10 Hours", "11 Hours",
1566
                       "12 Hours"]
1567
            v0 = _setx.apo      # Hours BCD
1568
            v1 = _setx.apo1     # 0.5 hours yes/no
1569
            sx = "%d" % v0
1570
            if v1:
1571
                sx += ".5"
1572
            if sx == "0":
1573
                sx = "Off"
1574
            else:
1575
                sx += " Hours"
1576
            rx = RadioSettingValueList(options, sx.strip())
1577
            rs = RadioSetting("setts.apo", "Auto Power Off time (APO)", rx)
1578
            rs.set_apply_callback(self._ft2apo, _setx)
1579
            cfg.append(rs)
1580

    
1581
        else:       # FTM-6000, different than 200
1582
            rx = RadioSettingValueList(menu_items, menu_items[_setx.f1key])
1583
            rs = RadioSetting("setts.f1key", "F1 Key Assignment", rx)
1584
            cfg.append(rs)
1585

    
1586
            options = ["Off", "1 Hours", "1.5 Hours", "2 Hours", "3 Hours",
1587
                       "4 Hours", "5 Hours", "6 Hours", "7 Hours", "8 Hours",
1588
                       "9 Hours", "10 Hours", "11 Hours", "12 Hours"]
1589
            rx = RadioSettingValueList(options, options[_setx.apo])
1590
            rs = RadioSetting("setts.apo", "Auto Power Off time (APO)", rx)
1591
            cfg.append(rs)
1592

    
1593
            rx = RadioSettingValueList(offon, offon[_setx.rpt_ars])
1594
            rs = RadioSetting("setts.rpt_ars", "Repeater Auto Offset Enabled",
1595
                              rx)
1596
            cfg.append(rs)
1597

    
1598
        # Config - Common to all radios
1599

    
1600
        options = ["Off", "Low", "High"]
1601
        # Yaesu splits the beep!!!
1602
        vx = _setx.beep1        # Off/Low
1603
        if _setx.beep2:
1604
            vx = 2  # High
1605
        rx = RadioSettingValueList(options, options[vx])
1606
        rs = RadioSetting("setts.beep1", "Beep", rx)
1607
        rs.set_apply_callback(self._beepset, _setx, options)
1608
        cfg.append(rs)
1609

    
1610
        options = ["Off", "1 minute", "2 minutes", "3 minutes", "5 minutes",
1611
                   "10 minutes", "15 minutes", "20 minutes", "30 minutes"]
1612
        vx = _setx.tot      # non-sequential index
1613
        if vx >= 6:
1614
            bx = vx - 5
1615
        if vx >= 1:
1616
            bx = vx + 3
1617
        if vx == 0:
1618
            bx = 0
1619
        rx = RadioSettingValueList(options, options[bx])
1620
        rs = RadioSetting("setts.tot", "Transmit Time Out Timer (TOT)", rx)
1621
        rs.set_apply_callback(self._settot, _setx, "tot", options)
1622
        cfg.append(rs)
1623

    
1624
        if self.FTM200:     # Stuff at the end of Config
1625
            options = ["Metric", "Imperial (Inch)"]
1626
            rx = RadioSettingValueList(options, options[_setx.unit])
1627
            rs = RadioSetting("setts.unit", "Units", rx)
1628
            cfg.append(rs)
1629

    
1630
            options = ["WGS 84", "Tokyo Mean"]
1631
            rx = RadioSettingValueList(options, options[_setx.gpsdtm])
1632
            rs = RadioSetting("setts.gpsdtm", "GPS Datum", rx)
1633
            cfg.append(rs)
1634

    
1635
            options = ["Internal", "External"]
1636
            rx = RadioSettingValueList(options, options[_setx.gpsdev])
1637
            rs = RadioSetting("setts.gpsdev", "GPS Device", rx)
1638
            cfg.append(rs)
1639

    
1640
            options = ["Off", "1 second", "2 sec", "5 sec", "10 sec",
1641
                       "30 sec", "60 sec"]
1642
            rx = RadioSettingValueList(options, options[_setx.gpslog])
1643
            rs = RadioSetting("setts.gpslog", "GPS Log Interval", rx)
1644
            cfg.append(rs)
1645

    
1646
        else:
1647
            options = ["30 Seconds", "1 Minute"]
1648
            rx = RadioSettingValueList(options, options[_setx.arts_int])
1649
            rs = RadioSetting("setts.arts_int", "ARTS Interval", rx)
1650
            cfg.append(rs)
1651

    
1652
            options = ["Off", "In Range", "Out Range"]
1653
            rx = RadioSettingValueList(options, options[_setx.arts_mode])
1654
            rs = RadioSetting("setts.arts_mode", "ARTS Mode", rx)
1655
            cfg.append(rs)
1656

    
1657
            rx = RadioSettingValueList(offon, offon[_setx.bclo])
1658
            rs = RadioSetting("setts.bclo", "Busy Channel Lockout (BCLO)",
1659
                              rx)
1660
            cfg.append(rs)
1661

    
1662
        # End of Config settings
1663

    
1664
        # Start of Display settings
1665
        if self.FTM200:
1666
            options = ["Backtrack", "Altitude", "Timer/Clock", "GPS Info"]
1667
            rx = RadioSettingValueList(options, options[_setx.dspmode])
1668
            rs = RadioSetting("setts.dspmode", "Display Select", rx)
1669
            dsp.append(rs)
1670

    
1671
            options = ["Compass", "Numeric Lat/Long"]
1672
            rx = RadioSettingValueList(options, options[_setx.locinfo])
1673
            rs = RadioSetting("setts.locinfo", "Location Info", rx)
1674
            dsp.append(rs)
1675

    
1676
            options = ["North Up", "Heading Up"]
1677
            rx = RadioSettingValueList(options, options[_mic.compass])
1678
            rs = RadioSetting("micset.compass", "Compass Display", rx)
1679
            dsp.append(rs)
1680

    
1681
            options = ["Wide", "Narrow"]
1682
            rx = RadioSettingValueList(options, options[_setx.bscope])
1683
            rs = RadioSetting("setts.bscope", "A: Display Scope", rx)
1684
            dsp.append(rs)
1685
            rx = RadioSettingValueList(options, options[_setx.bscopeb])
1686
            rs = RadioSetting("setts.bscopeb", "B: Display Scope", rx)
1687
            dsp.append(rs)
1688

    
1689
            rx = RadioSettingValueList(offon, offon[_setx.memlist])
1690
            rs = RadioSetting("setts.memlist", "Memory List Mode", rx)
1691
            dsp.append(rs)
1692

    
1693
            options = ["Dim", "Mid", "Full"]
1694
            vx = _mic.lcd       # stupid indexing: 3,1,2
1695
            v0 = vx      # Mid: vx = 1, Max: vx = 2
1696
            if vx == 3:
1697
                v0 = 0
1698
            rx = RadioSettingValueList(options, options[v0])
1699
            rs = RadioSetting("micset.lcd", "Display Brightness", rx)
1700
            rs.set_apply_callback(self._ft2lcd, _mic, "lcd")
1701
            dsp.append(rs)
1702

    
1703
            options = ["White", "Blue", "Red"]
1704
            rx = RadioSettingValueList(options, options[_setx.lcd_clr])
1705
            rs = RadioSetting("setts.lcd_clr", "LCD Upper Band Color", rx)
1706
            dsp.append(rs)
1707

    
1708
        else:       # FTM-6000
1709
            options = ["Dim", "Mid", "Full"]
1710
            rx = RadioSettingValueList(options, options[_mic.lcd])
1711
            rs = RadioSetting("micset.lcd", "Display Brightness", rx)
1712
            dsp.append(rs)
1713

    
1714
        # End of Display settings
1715

    
1716
        # Start of Signalling settings
1717
        options = ["Off", "1 Time", "3 Times", "5 Times", "8 Times",
1718
                   "Continous"]
1719
        if self.FTM200:
1720
            rx = RadioSettingValueList(options, options[_mic.bell])
1721
            rs = RadioSetting("micset.bell",
1722
                              "A: Remote Station Calling Bell", rx)
1723
            sig.append(rs)
1724
            rx = RadioSettingValueList(options, options[_mic.bellb])
1725
            rs = RadioSetting("micset.bellb",
1726
                              "B: Remote Station Calling Bell", rx)
1727
            sig.append(rs)
1728
        else:
1729
            rx = RadioSettingValueList(options, options[_mic.bell])
1730
            rs = RadioSetting("micset.bell", "Remote Station Calling Bell",
1731
                              rx)
1732
            sig.append(rs)
1733

    
1734
        # All radios, Signalling opetions
1735
        vx = _mic.pgrcdr1 + 1
1736
        rx = RadioSettingValueInteger(1, 50, vx)
1737
        rs = RadioSetting("micset.pcrcdr1", "Pager Receive First Code", rx)
1738
        rs.set_apply_callback(self._adjint, _mic, "pgrcdr1")
1739
        sig.append(rs)
1740

    
1741
        vx = _mic.pgrcdr2 + 1
1742
        rx = RadioSettingValueInteger(1, 50, vx)
1743
        rs = RadioSetting("micset.pcrcdr2", "Pager Receive Second Code", rx)
1744
        rs.set_apply_callback(self._adjint, _mic, "pgrcdr2")
1745
        sig.append(rs)
1746

    
1747
        rx = RadioSettingValueInteger(1, 50, vx)
1748
        rs = RadioSetting("micset.pgrcdt1", "Pager Transmit First Code", rx)
1749
        rs.set_apply_callback(self._adjint, _mic, "pgrcdt1")
1750
        sig.append(rs)
1751

    
1752
        vx = _mic.pgrcdt2 + 1
1753
        rx = RadioSettingValueInteger(1, 50, vx)
1754
        rs = RadioSetting("micset.pgrcdt2", "Pager Transmit Second Code", rx)
1755
        rs.set_apply_callback(self._adjint, _mic, "pgrcdt2")
1756
        sig.append(rs)
1757

    
1758
        rx = RadioSettingValueList(offon, offon[_setx.wxalrt])
1759
        rs = RadioSetting("setts.wxalrt", "Weather Alert Enabled", rx)
1760
        sig.append(rs)
1761

    
1762
        # End of Signalling settings
1763

    
1764
        # Begin fmenu settings      For each entry in menu_Items
1765
        if self.FTM200:     # 124 menu options possible
1766
            options = ["1:Freq Input", "2:LCD Brightness", "3:Freq Color",
1767
                       "4:Band Scope", "5:Location Info", "6:Compass",
1768
                       "7:Display Mode"]
1769
            more = ("8:TX Power", "9:AMS TX Mode", "10:Mic Gain", "11:VOX",
1770
                    "12:Auto Dialer", "13:Time Out Timer (TOT)",
1771
                    "14:Digital VW")
1772
            options.extend(more)
1773
            more = ("15:FM Bandwidth", "16:RX Mode", "17:Home",
1774
                    "18:Memory List", "19:Memory List Mode", "20:PMG Clear")
1775
            options.extend(more)
1776
            more = ("21:Beep", "22:Band Skip", "23:RPT ARS", "24:RPT Shift",
1777
                    "25:RPT Shift Freq", "26:Rpt Reverse", "27:Mic P Key",
1778
                    "28:Date & Time Adjust", "29:Date  &Time Format",
1779
                    "30:Time Zone", "31:Step", "32:Clock Type", "33:Unit",
1780
                    "34:Auto Power Off (APO)", "35:GPS Datum",
1781
                    "36:GPS Device", "37:GPS Log")
1782
            options.extend(more)
1783
            more = ("38:Audio Recording", "39:Audio Rec Stop")
1784
            options.extend(more)
1785
            more = ("40:DTMF Mode", "41:DTMF Memory", "42:SQL Type",
1786
                    "43:Tone Freq / DCS Code", "44:SQL Expansion",
1787
                    "45:Pager Code", "46:PR Freq", "47:Bell Ringer",
1788
                    "48:WX Alert")
1789
            options.extend(more)
1790
            more = ("49:Scan", "50:Dual Rcv Mode", "51:Dual Rx Interval",
1791
                    "52:Priority Revert", "53:Scan Resume")
1792
            options.extend(more)
1793
            more = ("54:Digital Popup", "55:Location Service",
1794
                    "56:Standy Beep", "57:GM FP-ID List", "58:Range Ringer",
1795
                    "59:Radio ID", "60:Log List", "61:Rpt / WIRES Freq",
1796
                    "62:Search Step", "63:Edit Category Tag",
1797
                    "64:Delete Room/Mode", "65:WIRES Dg-ID", "66:Com Port",
1798
                    "67:Data Band", "68:Data Speed", "69:Data SQL")
1799
            options.extend(more)
1800
            more = ("70:APRS Destination", "71:Filter", "72:Msg Text",
1801
                    "73:APRS On/Off", "74:Mute", "75:Popup", "76:Ringer",
1802
                    "77:Ringer CS", "78:TX Delay", "79:Units",
1803
                    "80:Beacon Info", "81:Beacon Status Txt",
1804
                    "82:Beacon TX Set", "83:DIGI Path", "84:DIGI Path 1",
1805
                    "85:DIGI Path 2", "86:DIGI Path 3", "87:DIGI Path 4",
1806
                    "88:DIGI Path Full 1", "89: DIGI Path Full 2")
1807
            options.extend(more)
1808
            more = ("90:APRS Call Sign", "91:Msg Group", "92:Msg Reply",
1809
                    "93:My Position Set", "94:My Position", "95:My Symbol",
1810
                    "96:Position Comment", "97:Smart Beaconing",
1811
                    "98:Sort Filter", "99:Voice alert", "100:Station List",
1812
                    "101:Msg List", "102:Beacon TX Select", "103:Beacon TX")
1813
            options.extend(more)
1814
            more = ("104:SD Card Backup", "105:SD Card Mem Info",
1815
                    "106:SD Card Format", "107:Bluetooth", "108:Voice Memory",
1816
                    "109:FVS Rec", "110:Track Select", "111:Play",
1817
                    "112:Stop", "113:Clear", "114:Voice Guide",
1818
                    "115:USB Camera")
1819
            options.extend(more)
1820
            more = ("116:This->Other", "117:Other->This", "118:Call Sign",
1821
                    "119:Mem Chn Reset", "120:APRS Reset", "121:Config Set",
1822
                    "122:Config Recall", "123:SW Version",
1823
                    "124:Factory Reset")
1824
            options.extend(more)
1825
            rx = RadioSettingValueList(options, options[_mic.fkp1])
1826
            rs = RadioSetting("micset.fkp1", "Keypad Slot 1", rx)
1827
            rs.set_apply_callback(self._setQA, _mic, "fkp1", options)
1828
            fmenu.append(rs)
1829
            rx = RadioSettingValueList(options, options[_mic.fkp2])
1830
            rs = RadioSetting("micset.fkp2", "Keypad Slot 2", rx)
1831
            rs.set_apply_callback(self._setQA, _mic, "fkp2", options)
1832
            fmenu.append(rs)
1833
            rx = RadioSettingValueList(options, options[_mic.fkp3])
1834
            rs = RadioSetting("micset.fkp3", "Keypad Slot 3", rx)
1835
            rs.set_apply_callback(self._setQA, _mic, "fkp3", options)
1836
            fmenu.append(rs)
1837
            rx = RadioSettingValueList(options, options[_mic.fkp4])
1838
            rs = RadioSetting("micset.fkp4", "Keypad Slot 4", rx)
1839
            rs.set_apply_callback(self._setQA, _mic, "fkp4", options)
1840
            fmenu.append(rs)
1841

    
1842
            rx = RadioSettingValueList(options, options[_mic.fhm1])
1843
            rs = RadioSetting("micset.fhm1", "Home Slot 1", rx)
1844
            rs.set_apply_callback(self._setQA, _mic, "fhm1", options)
1845
            fmenu.append(rs)
1846
            rx = RadioSettingValueList(options, options[_mic.fhm2])
1847
            rs = RadioSetting("micset.fhm2", "Home Slot 2", rx)
1848
            rs.set_apply_callback(self._setQA, _mic, "fhm2", options)
1849
            fmenu.append(rs)
1850
            rx = RadioSettingValueList(options, options[_mic.fhm3])
1851
            rs = RadioSetting("micset.fhm3", "Home Slot 3", rx)
1852
            rs.set_apply_callback(self._setQA, _mic, "fhm3", options)
1853
            fmenu.append(rs)
1854
            rx = RadioSettingValueList(options, options[_mic.fhm4])
1855
            rs = RadioSetting("micset.fhm4", "Home Slot 4", rx)
1856
            rs.set_apply_callback(self._setQA, _mic, "fhm4", options)
1857
            fmenu.append(rs)
1858
        else:           # FTM-6000
1859
            _shr = self._memobj.share1
1860
            for mnu in range(0, 35):
1861
                x1 = mnu * 2    # _shr index of first byte
1862
                vx = _shr[x1].shbyt
1863
                bx = False
1864
                if vx != 0xff:
1865
                    bx = True
1866
                sx = "share1/%d.shbyt" % mnu
1867
                rx = RadioSettingValueBoolean(bx)
1868
                rs = RadioSetting(sx, menu_items[mnu], rx)
1869
                fmenu.append(rs)
1870
                rs.set_apply_callback(self._setfunc, _shr, "shbyt", mnu)
1871
        # End fmenu settings
1872

    
1873
        # Begin microphone settings
1874
        options = ["Min", "Low", "Normal", "High", "Max"]
1875
        rx = RadioSettingValueList(options, options[_mic.micgain])
1876
        rs = RadioSetting("micset.micgain", "Microphone Gain", rx)
1877
        mic.append(rs)
1878

    
1879
        options = ["ARTS", "SCAN On", "HOME Recall", "Repeater Duplex",
1880
                   "Repeater Reverse", "Transmit Power", "Squelch Off",
1881
                   "T-Call", "Dual Watch", "Weather Channel"]
1882
        if self.FTM200:
1883
            options = ["Off", "Band Scope", "Scan", "Home", "Rpt Shift",
1884
                       "Reverse", "TX Power", "SQL Off", "T-Call", "Voice",
1885
                       "D_X", "WX", "Stn_List", "Msg List", "Reply",
1886
                       "M-Edit"]
1887

    
1888
        else:               # FTM200- P1 is fixed as GM, immutable
1889
            vx = _mic.micp1 - 0xdd
1890
            if vx == 10:
1891
                vx = 9     # stupid index jump
1892
            rx = RadioSettingValueList(options, options[vx])
1893
            rs = RadioSetting("micset.micp1", "Microphone Key P1", rx)
1894
            rs.set_apply_callback(self._micpkey, _mic, "micp1", options)
1895
            mic.append(rs)
1896

    
1897
        vx = _mic.micp2 - 0xdd
1898
        if vx == 10:
1899
            vx = 9
1900
        if self.FTM200:
1901
            vx = _mic.micp2 - 0xdc
1902
        rx = RadioSettingValueList(options, options[vx])
1903
        rs = RadioSetting("micset.micp2", "Microphone Key P2", rx)
1904
        rs.set_apply_callback(self._micpkey, _mic, "micp2",  options)
1905
        mic.append(rs)
1906

    
1907
        vx = _mic.micp3 - 0xdd
1908
        if vx == 10:
1909
            vx = 9
1910
        if self.FTM200:
1911
            vx = _mic.micp3 - 0xdc
1912
        rx = RadioSettingValueList(options, options[vx])
1913
        rs = RadioSetting("micset.micp3", "Microphone Key P3", rx)
1914
        rs.set_apply_callback(self._micpkey, _mic, "micp3",  options)
1915
        mic.append(rs)
1916

    
1917
        vx = _mic.micp4 - 0xdd
1918
        if vx == 10:
1919
            vx = 9
1920
        if self.FTM200:
1921
            vx = _mic.micp4 - 0xdc
1922
        rx = RadioSettingValueList(options, options[vx])
1923
        rs = RadioSetting("micset.micp4", "Microphone Key P4", rx)
1924
        rs.set_apply_callback(self._micpkey, _mic, "micp4",  options)
1925
        mic.append(rs)
1926
        # End mic settings
1927

    
1928
        # Begin DTMF settings
1929
        options = ["Manual", "Auto"]
1930
        rx = RadioSettingValueList(options, options[_setx.dtmf_auto])
1931
        rs = RadioSetting("setts.dtmf_auto", "DTMF Transmit Mode", rx)
1932
        dtmf.append(rs)
1933

    
1934
        for kx in range(0, 9):
1935
            sx = self._unpack_str(_dtm[kx].codes)
1936
            rx = RadioSettingValueString(0, 16, sx, False)
1937
            # NOTE the / to indicate indexed array
1938
            vx = kx + 1
1939
            rs = RadioSetting("dtmfcode/%d.codes" % kx,
1940
                              "DTMF Code %d" % vx, rx)
1941
            rs.set_apply_callback(self._pack_chars, _dtm, "codes", kx)
1942
            dtmf.append(rs)
1943
        # End of dtmf settings
1944

    
1945
        # Begin Scan settings
1946
        if self.FTM200:
1947
            options = ["Busy", "Hold", "1 Second", "3 Seconds", "5 Seconds"]
1948
            rx = RadioSettingValueList(options, options[_setx.scnrsm2a])
1949
            rs = RadioSetting("setts.scnrsm2a", "A: Scan Resume Mode", rx)
1950
            scan.append(rs)
1951
            rx = RadioSettingValueList(options, options[_setx.scnrsm2b])
1952
            rs = RadioSetting("setts.scnrsm2b", "B: Scan Resume Mode", rx)
1953
            scan.append(rs)
1954

    
1955
            options = ["0.5 seconds", "1 sec", "2 sec", "3 sec", "5 sec",
1956
                       "7 sec", "10 secs"]
1957
            rx = RadioSettingValueList(options, options[_setx.scndria])
1958
            rs = RadioSetting("setts.scndria", "A: Dual Receive Interval",
1959
                              rx)
1960
            scan.append(rs)
1961
            rx = RadioSettingValueList(options, options[_setx.scndrib])
1962
            rs = RadioSetting("setts.scndrib", "B: Dual Receive Interval",
1963
                              rx)
1964
            scan.append(rs)
1965

    
1966
            rx = RadioSettingValueList(offon, offon[_setx.dwrvt])
1967
            rs = RadioSetting("setts.dwrvt", "A: Priority Revert", rx)
1968
            scan.append(rs)
1969
            rx = RadioSettingValueList(offon, offon[_setx.dwrvtb])
1970
            rs = RadioSetting("setts.dwrvtb", "B: Priority Revert", rx)
1971
            scan.append(rs)
1972

    
1973
            options = ["Off", "Priority Scan", "A-B Dual Receive"]
1974
            # unbelievable 2- byte configuration
1975
            v0 = _setx.scndrm1      # at 17f62 bit 8
1976
            v1 = _setx.scndrm2      # at 17f6b bit 1
1977
            vx = 0
1978
            if v0 == 1:
1979
                vx = 1
1980
            elif v1 == 1:
1981
                vx = 2
1982
            rx = RadioSettingValueList(options, options[vx])
1983
            rs = RadioSetting("setts.scndrm1", "Scan Dual Receive Mode", rx)
1984
            rs.set_apply_callback(self._scndrm, _setx)
1985
            scan.append(rs)
1986

    
1987
        else:
1988
            options = ["Busy", "Hold", "1 Second", "3 Seconds", "5 Seconds"]
1989
            rx = RadioSettingValueList(options, options[_setx.scnrsm])
1990
            rs = RadioSetting("setts.scnrsm", "Scan Resume Mode", rx)
1991
            scan.append(rs)
1992

    
1993
            rx = RadioSettingValueList(offon, offon[_setx.dwrvt])
1994
            rs = RadioSetting("setts.dwrvt", "Dual Watch Revert", rx)
1995
            scan.append(rs)
1996
        # End of Scan settings
1997

    
1998
        # Begin Data settings
1999
        if self.FTM200:
2000
            options = ["4700 bps", "9600 bps", "19200 bps", "38400 bps",
2001
                       "57600 bps"]
2002
            rx = RadioSettingValueList(options, options[_wierd.comspd])
2003
            rs = RadioSetting("wierd.comspd", "COM Port Speed", rx)
2004
            dat.append(rs)
2005

    
2006
            options = ["Off", "GPS Out", "Packet", "Waypoint"]
2007
            rx = RadioSettingValueList(options, options[_wierd.comout])
2008
            rs = RadioSetting("wierd.comout", "COM Port Output", rx)
2009
            dat.append(rs)
2010

    
2011
            options = ["NMEA9", "NMEA8", "NMEA7", "NMEA6"]
2012
            rx = RadioSettingValueList(options, options[_wierd.comwpf])
2013
            rs = RadioSetting("wierd.comwpf", "COM Port Waypoint Format", rx)
2014
            dat.append(rs)
2015

    
2016
            options = ["All", "Mobile", "Frequency", "Object/Item",
2017
                       "Digipeater", "VoIP", "Weather", "Yaesu",
2018
                       "Call Ringer", "Rng Ringer"]
2019
            rx = RadioSettingValueList(options, options[_wierd.comflt])
2020
            rs = RadioSetting("wierd.comflt", "COM Port Waypoint Filter", rx)
2021
            dat.append(rs)
2022

    
2023
            options = ["Main Band", "Sub Band", "A-Band Fix", "B-Band Fix"]
2024
            rx = RadioSettingValueList(options, options[_wierd.dbsela])
2025
            rs = RadioSetting("wierd.dbsela", "Data Band Select: APRS", rx)
2026
            dat.append(rs)
2027
            rx = RadioSettingValueList(options, options[_wierd.dbseld])
2028
            rs = RadioSetting("wierd.dbseld", "Data Band Select: Data", rx)
2029
            dat.append(rs)
2030

    
2031
            options = ["1200 bps", "9600 bps"]
2032
            rx = RadioSettingValueList(options, options[_wierd.dspda])
2033
            rs = RadioSetting("wierd.dspda", "Data Speed: APRS", rx)
2034
            dat.append(rs)
2035
            rx = RadioSettingValueList(options, options[_wierd.pktspd])
2036
            rs = RadioSetting("wierd.pktspd", "Data Speed: Data", rx)
2037
            dat.append(rs)
2038

    
2039
            rx = RadioSettingValueList(offon, offon[_wierd.datsql])
2040
            rs = RadioSetting("wierd.datsql", "Data Squelch", rx)
2041
            dat.append(rs)
2042
        # End of Data Settings
2043

    
2044
        # Begin Options settings
2045
        if self.FTM200:
2046
            options = ["FREE 5 min", "LAST 30 sec"]
2047
            rx = RadioSettingValueList(options, options[_setx.fvsrec])
2048
            rs = RadioSetting("setts.fvsrec", "FVS-2: Play/Record", rx)
2049
            opts.append(rs)
2050

    
2051
            options = ["Off", "Manual", "Auto"]
2052
            rx = RadioSettingValueList(options, options[_setx.fvsanc])
2053
            rs = RadioSetting("setts.fvsanc", "FVS-2: Announce Mode", rx)
2054
            opts.append(rs)
2055

    
2056
            options = ["English", "Japanese"]
2057
            rx = RadioSettingValueList(options, options[_setx.fvslan])
2058
            rs = RadioSetting("setts.fvslan", "FVS-2: Language", rx)
2059
            opts.append(rs)
2060

    
2061
            options = ["Low", "Mid", "High"]
2062
            rx = RadioSettingValueList(options, options[_setx.fvsvol])
2063
            rs = RadioSetting("setts.fvsvol", "FVS-2: Volume", rx)
2064
            opts.append(rs)
2065

    
2066
            # Yaesu split the FTM-200 BEEP 0/1/2 into 2 byte locations!!
2067
            # and assigned this bit to the upper Beep of the FTM-6000!!
2068
            rx = RadioSettingValueList(onoff, onoff[_setx.fvsrxm])
2069
            rs = RadioSetting("setts.fvsrxm", "FVS-2: RX Mute", rx)
2070
            opts.append(rs)
2071

    
2072
        # Common Options - BLUETOOTH
2073
        rx = RadioSettingValueList(offon, offon[_setx.bton])
2074
        rs = RadioSetting("setts.bton", "Bluetooth: Enabled", rx)
2075
        opts.append(rs)
2076

    
2077
        rx = RadioSettingValueList(offon, offon[_setx.btsave])
2078
        rs = RadioSetting("setts.btsave", "Bluetooth: Save", rx)
2079
        opts.append(rs)
2080

    
2081
        options = ["Auto", "Fix"]
2082
        rx = RadioSettingValueList(options, options[_setx.btaud])
2083
        rs = RadioSetting("setts.btaud", "Bluetooth: Audio", rx)
2084
        opts.append(rs)
2085

    
2086
        if self.FTM200:
2087
            options = ["320 x 240", "160 x 120"]
2088
            rx = RadioSettingValueList(options, options[_mic.usbcamsz])
2089
            rs = RadioSetting("micset.usbcamsz", "USB Camera: Image Size", rx)
2090
            opts.append(rs)
2091

    
2092
            options = ["Low", "Normal", "High"]
2093
            rx = RadioSettingValueList(options, options[_mic.usbcamql])
2094
            rs = RadioSetting("micset.usbcamql", "USB Camera: Image Quality",
2095
                              rx)
2096
            opts.append(rs)
2097

    
2098
        # End of Options settings
2099

    
2100
        # Begin Other settings
2101

    
2102
        if self.FTM200:
2103
            options = ["A", "B", "A+B"]
2104
            rx = RadioSettingValueList(options, options[_setx.audrec - 1])
2105
            rs = RadioSetting("setts.audrec", "Audio Recording Band", rx)
2106
            rs.set_apply_callback(self._adjlist, _setx, "audrec", options,
2107
                                  1)
2108
            other.append(rs)
2109

    
2110
            rx = RadioSettingValueList(offon, offon[_setx.audmic])
2111
            rs = RadioSetting("setts.audmic", "Microphone", rx)
2112
            other.append(rs)
2113

    
2114
            options = ["Off", "Low", "High"]
2115
            rx = RadioSettingValueList(options, options[_setx.voxsen])
2116
            rs = RadioSetting("setts.voxsen", "VOX Sensitivity", rx)
2117
            other.append(rs)
2118
            options = ["0.5 seconds", "1 sec", "1.5 sec", "2.0 sec",
2119
                       "2.5sec", "3 sec"]
2120
            rx = RadioSettingValueList(options, options[_setx.voxdly])
2121
            rs = RadioSetting("setts.voxdly", "VOX Delay", rx)
2122
            other.append(rs)
2123

    
2124
            rx = RadioSettingValueList(offon, offon[_setx.bskpa1])
2125
            rs = RadioSetting("setts.bskpa1", "A: Air Band Enabled", rx)
2126
            other.append(rs)
2127
            rx = RadioSettingValueList(offon, offon[_setx.bskpa2])
2128
            rs = RadioSetting("setts.bskpa2", "A: VHF Band Enabled", rx)
2129
            other.append(rs)
2130
            rx = RadioSettingValueList(offon, offon[_setx.bskpa4])
2131
            rs = RadioSetting("setts.bskpa4", "A: UHF Band Enabled", rx)
2132
            other.append(rs)
2133
            rx = RadioSettingValueList(offon, offon[_setx.bskpa3])
2134
            rs = RadioSetting("setts.bskpa3", "A: Other Band Enabled", rx)
2135
            rs.set_apply_callback(self._skp_other, _setx, "A", options)
2136
            other.append(rs)
2137

    
2138
            rx = RadioSettingValueList(offon, offon[_setx.bskpb1])
2139
            rs = RadioSetting("setts.bskpb1", "B: Air Band Enabled", rx)
2140
            other.append(rs)
2141
            rx = RadioSettingValueList(offon, offon[_setx.bskpb2])
2142
            rs = RadioSetting("setts.bskpb2", "B: VHF Band Enabled", rx)
2143
            other.append(rs)
2144
            rx = RadioSettingValueList(offon, offon[_setx.bskpb4])
2145
            rs = RadioSetting("setts.bskpb4", "B: UHF Band Enabled", rx)
2146
            other.append(rs)
2147
            rx = RadioSettingValueList(offon, offon[_setx.bskpb3])
2148
            rs = RadioSetting("setts.bskpb3", "B: Other Band Enabled", rx)
2149
            rs.set_apply_callback(self._skp_other, _setx, "B", options)
2150
            other.append(rs)
2151

    
2152
        else:
2153
            options = ["1200 bps", "9600 bps"]
2154
            rx = RadioSettingValueList(options, options[_wierd.pktspd])
2155
            rs = RadioSetting("wierd.pktspd", "Data Port Packet Baud Rate",
2156
                              rx)
2157
            other.append(rs)
2158

    
2159
            rx = RadioSettingValueList(offon, offon[_setx.airb])
2160
            rs = RadioSetting("setts.airb", "Air Band Enabled", rx)
2161
            other.append(rs)
2162

    
2163
            rx = RadioSettingValueList(offon, offon[_setx.vhfb])
2164
            rs = RadioSetting("setts.vhfb", "VHF Band Enabled", rx)
2165
            other.append(rs)
2166

    
2167
            rx = RadioSettingValueList(offon, offon[_setx.uhfb])
2168
            rs = RadioSetting("setts.uhfb", "UHF Band Enabled", rx)
2169
            other.append(rs)
2170

    
2171
            rx = RadioSettingValueList(offon, offon[_setx.otrb])
2172
            rs = RadioSetting("setts.otrb", "Other Band Enabled", rx)
2173
            other.append(rs)
2174
        # End of Other Settings
2175

    
2176
        # Start FTM200 Unique settings
2177
        if self.FTM200:
2178
            _wrx = self._memobj.wrxmsg
2179
            xmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop"
2180
            xmap += "qrstuvwxyz!\"#$%&\\()*+,-./:;<=>?@[']^_`{|}~****** "
2181
            # NOTE: Unknown characters at index 94-99; replaced with *
2182
            for ix in range(1, 11):
2183
                atr = "msg%02d" % ix
2184
                sx = getattr(_wrx, atr)
2185
                tx = ""
2186
                for cx in sx:
2187
                    if cx == 0xff:
2188
                        break
2189
                    tx += xmap[cx]
2190
                tx = tx.strip()
2191
                rx = RadioSettingValueString(0, 128, tx, False)
2192
                atr = "wrxmsg.msg%02d" % ix
2193
                rs = RadioSetting(atr, "Message M%02d" % ix, rx)
2194
                rs.set_apply_callback(self._xmsg, _wrx, ix, xmap)
2195
                wires.append(rs)
2196

    
2197
            rx = RadioSettingValueList(offon, offon[_setx.wrxrng])
2198
            rs = RadioSetting("setts.wrxrng", "Range Ringer", rx)
2199
            wires.append(rs)
2200

    
2201
            options = ["Auto", "TX FM Fixed", "TX DN Fixed"]
2202
            rx = RadioSettingValueList(options, options[_setx.wrxams])
2203
            rs = RadioSetting("setts.wrxams", "AMS TX Mode", rx)
2204
            wires.append(rs)
2205

    
2206
            options = ["Off", "2 seconds", "4 sec", "6 sec", "8 sec",
2207
                       "10 sec", "20 sec", "30 sec", "60 sec", "Continuous"]
2208
            rx = RadioSettingValueList(options, options[_mic.wrxpop])
2209
            rs = RadioSetting("micset.wrxpop", "Digital Popup", rx)
2210
            wires.append(rs)
2211

    
2212
            rx = RadioSettingValueList(offon, offon[_setx.wrxloc])
2213
            rs = RadioSetting("setts.wrxloc", "Location Service", rx)
2214
            wires.append(rs)
2215

    
2216
            rx = RadioSettingValueList(onoff, onoff[_setx.wrxstb])
2217
            rs = RadioSetting("setts.wrxstb", "Standby Beep", rx)
2218
            wires.append(rs)
2219

    
2220
            rx = RadioSettingValueList(offon, offon[_setx.wrxdvw])
2221
            rs = RadioSetting("setts.wrxdvw", "Digital VW Enable", rx)
2222
            wires.append(rs)
2223

    
2224
            options = ["Manual", "Preset"]
2225
            rx = RadioSettingValueList(options, options[_setx.wrxrwf])
2226
            rs = RadioSetting("setts.wrxrwf", "RPT/WIRES Frequency Mode", rx)
2227
            wires.append(rs)
2228

    
2229
            options = ["VHF Preset", "UHF Preset"]
2230
            rx = RadioSettingValueList(options, options[_mic.wrxbsel])
2231
            rs = RadioSetting("micset.wrxbsel", "RPT/WIRES Preset Band Select",
2232
                              rx)
2233
            wires.append(rs)
2234

    
2235
            vx = self._freqdcode(_mic.wrxvfrq) / 1000000
2236
            rx = RadioSettingValueFloat(144.0, 149.0, vx, 0.001, 3)
2237
            rs = RadioSetting("micset.wrxvfrq", "RPT/WIRES VHF Preset Freq",
2238
                              rx)
2239
            rs.set_apply_callback(self._wrxfrq, _mic, "wrxvfrq")
2240
            wires.append(rs)
2241

    
2242
            vx = self._freqdcode(_mic.wrxufrq) / 1000000
2243
            rx = RadioSettingValueFloat(420.0, 460.0, vx, 0.001, 3)
2244
            rs = RadioSetting("micset.wrxufrq", "RPT/WIRES UHF Preset Freq",
2245
                              rx)
2246
            rs.set_apply_callback(self._wrxfrq, _mic, "wrxufrq")
2247
            wires.append(rs)
2248

    
2249
            options = ["History", "Activity"]
2250
            rx = RadioSettingValueList(options, options[_setx.wrxsrch])
2251
            rs = RadioSetting("setts.wrxsrch", "RPT/WIRES Search Setup", rx)
2252
            wires.append(rs)
2253

    
2254
            options = ["Auto"]
2255
            for vx in range(1, 100):
2256
                options.append("%02d" % vx)
2257
            rx = RadioSettingValueList(options, options[_mic.wrxdid])
2258
            rs = RadioSetting("micset.wrxdid", "RPT/WIRES Radio Digital ID",
2259
                              rx)
2260
            wires.append(rs)
2261

    
2262
            _wrxc = self._memobj.wrxcat
2263
            for ix in range(1, 6):
2264
                atr = "c%d" % ix
2265
                sx = getattr(_wrxc, atr)
2266
                tx = ""
2267
                for cx in sx:
2268
                    if cx == 0xca:
2269
                        break
2270
                    tx += xmap[cx]
2271
                tx = tx.strip()
2272
                rx = RadioSettingValueString(0, 16, tx, False)
2273
                atr = "wrxcat.c%d" % ix
2274
                rs = RadioSetting(atr, "Category Text C%d" % ix, rx)
2275
                rs.set_apply_callback(self._xmsg, _wrxc, ix, xmap, 1)
2276
                wires.append(rs)
2277

    
2278
            rx = RadioSettingValueList(offon, offon[_wierd.aprson])
2279
            rs = RadioSetting("wierd.aprson", "APRS Enabled", rx)
2280
            aprscom.append(rs)
2281

    
2282
            rx = RadioSettingValueList(offon, offon[_wierd.aprmut])
2283
            rs = RadioSetting("wierd.aprmut", "Mute", rx)
2284
            aprscom.append(rs)
2285

    
2286
            options = ["100ms", "150ms", "200ms", "250ms", "300ms", "400ms",
2287
                       "500ms", "750ms", "1000ms"]
2288
            rx = RadioSettingValueList(options, options[_wierd.aprtxd])
2289
            rs = RadioSetting("wierd.aprtxd", "TX Delay", rx)
2290
            aprscom.append(rs)
2291

    
2292
            options = ["Off", "1 digit", "2 digits", "3 digits", "4 digits"]
2293
            rx = RadioSettingValueList(options, options[_wierd.aprbamb])
2294
            rs = RadioSetting("wierd.aprbamb", "Beacon Ambiguity", rx)
2295
            aprscom.append(rs)
2296

    
2297
            rx = RadioSettingValueList(offon, offon[_wierd.aprspdc])
2298
            rs = RadioSetting("wierd.aprspdc", "Beacon Speed/Course", rx)
2299
            aprscom.append(rs)
2300

    
2301
            rx = RadioSettingValueList(offon, offon[_wierd.aprsalt])
2302
            rs = RadioSetting("wierd.aprsalt", "Beacon Altitude", rx)
2303
            aprscom.append(rs)
2304

    
2305
            options = ["Off", "On", "Smart"]
2306
            rx = RadioSettingValueList(options, options[_wierd.aprbaut])
2307
            rs = RadioSetting("wierd.aprbaut", "Beacon TX Mode", rx)
2308
            aprscom.append(rs)
2309

    
2310
            options = ["30 sec", "1 minute", "2 mins", "3 mins", "5 mins",
2311
                       "10 mins", "15 mins", "20 mins", "30 mins", "60 mins"]
2312
            rx = RadioSettingValueList(options, options[_wierd.aprstxi])
2313
            rs = RadioSetting("wierd.aprstxi", "Beacon TX Interval", rx)
2314
            aprscom.append(rs)
2315

    
2316
            rx = RadioSettingValueList(offon, offon[_wierd.aprspro])
2317
            rs = RadioSetting("wierd.aprspro", "Beacon TX Proportional", rx)
2318
            aprscom.append(rs)
2319

    
2320
            rx = RadioSettingValueList(offon, offon[_wierd.aprsdcy])
2321
            rs = RadioSetting("wierd.aprsdcy", "Beacon TX Decay", rx)
2322
            aprscom.append(rs)
2323

    
2324
            # Lets try using integer values instead of list; just for kicks
2325
            rx = RadioSettingValueInteger(1, 99, _wierd.aprslow)
2326
            rs = RadioSetting("wierd.aprslow", "TX Low Speed (mph)", rx)
2327
            aprscom.append(rs)
2328

    
2329
            rx = RadioSettingValueInteger(5, 180, _wierd.aprsrate)
2330
            rs = RadioSetting("wierd.aprsrate", "TX Rate Limit (secs)", rx)
2331
            aprscom.append(rs)
2332

    
2333
            sx = self._b2s(_wierd.aprcsgn, 0xca)
2334
            rx = RadioSettingValueString(0, 6, sx, False)
2335
            rs = RadioSetting("wierd.aprcsgn", "APRS Callsign", rx)
2336
            rs.set_apply_callback(self._s2b, _wierd, "aprcsgn", 6, 0xca)
2337
            aprscom.append(rs)
2338

    
2339
            options = [""]
2340
            for i in range(1, 16):
2341
                options.append("-%d" % i)
2342
            vx = _wierd.aprcsfx
2343
            if vx == 0xca:
2344
                vx = 0
2345
            rx = RadioSettingValueList(options, options[vx])
2346
            rs = RadioSetting("wierd.aprcsfx", "Call Sign SSID ", rx)
2347
            rs.set_apply_callback(self._aprsfx, _wierd, "aprcsfx", 0xca)
2348
            aprscom.append(rs)
2349

    
2350
            options = ["Off Duty", "En Route", "In Service", "Returning",
2351
                       "Committed", "Special", "Priority", "Sustom 0",
2352
                       "Custom 1", "Custom 2", "Custom 3", "Custom 4",
2353
                       "Custom 5", "Custom 6", "EMERGENCY!"]
2354
            rx = RadioSettingValueList(options, options[_wierd.aprcmt])
2355
            rs = RadioSetting("wierd.aprcmt", "Position Comment", rx)
2356
            aprscom.append(rs)
2357

    
2358
            options = ["Off", "3 secs", " 5 secs", "10 secs", "HOLD"]
2359
            vx = _wierd.aprpopb    # hex encoded decimal plus ff= hold
2360
            v1 = 0
2361
            if vx == 3:
2362
                v1 = 1
2363
            if vx == 5:
2364
                v1 = 2
2365
            if vx == 10:
2366
                v1 = 3
2367
            if vx == 0xff:
2368
                v1 = 4
2369
            rx = RadioSettingValueList(options, options[v1])
2370
            rs = RadioSetting("wierd.aprpopb", "Beacon Popup Display Time",
2371
                              rx)
2372
            rs.set_apply_callback(self._aprspop, _wierd, "aprpopb")
2373
            aprscom.append(rs)
2374

    
2375
            vx = _wierd.aprpopm
2376
            v1 = 0
2377
            if vx == 3:
2378
                v1 = 1
2379
            if vx == 5:
2380
                v1 = 2
2381
            if vx == 10:
2382
                v1 = 3
2383
            if vx == 0xff:
2384
                v1 = 4
2385
            rx = RadioSettingValueList(options, options[v1])
2386
            rs = RadioSetting("wierd.aprpopm",
2387
                              "Message Popup Display Time", rx)
2388
            rs.set_apply_callback(self._aprspop, _wierd, "aprpopm")
2389
            aprscom.append(rs)
2390

    
2391
            rx = RadioSettingValueList(offon, offon[_wierd.aprmpkt])
2392
            rs = RadioSetting("wierd.aprmpkt", "My Packet", rx)
2393
            aprscom.append(rs)
2394

    
2395
            options = ["Time", "Call Sign", "Distance"]
2396
            rx = RadioSettingValueList(options, options[_wierd.aprsfs])
2397
            rs = RadioSetting("wierd.aprsfs", "Sort Filter: Sort By", rx)
2398
            aprscom.append(rs)
2399

    
2400
            options = ["All", "Mobile", "Frequency", "Object/Item",
2401
                       "Digipeater", "VoIP", "Weather", "Yaesu",
2402
                       "Other Packet", " Call Ringer", "Range Ringer",
2403
                       "1200 bps", "9600 bps"]
2404
            rx = RadioSettingValueList(options, options[_wierd.aprsff])
2405
            rs = RadioSetting("wierd.aprsff", "Sort Filter: Filter Type", rx)
2406
            aprscom.append(rs)
2407

    
2408
            options = ["Normal", "Tone-SQL", "DCS", "Rx-TSQL", "Rx-DCS"]
2409
            rx = RadioSettingValueList(options, options[_wierd.aprvlrt])
2410
            rs = RadioSetting("wierd.aprvlrt", "Voice Alert: Mode", rx)
2411
            aprscom.append(rs)
2412

    
2413
            options = []
2414
            for vx in chirp_common.TONES:
2415
                options.append("%.1f" % vx)
2416
            rx = RadioSettingValueList(options, options[_wierd.aprtsql])
2417
            rs = RadioSetting("wierd.aprtsql", "Voice Alert: TSQL Tone", rx)
2418
            aprscom.append(rs)
2419

    
2420
            options = []
2421
            for vx in chirp_common.DTCS_CODES:
2422
                options.append("%03d" % vx)
2423
            rx = RadioSettingValueList(options, options[_wierd.aprvdcs])
2424
            rs = RadioSetting("wierd.aprvdcs", "Voice Alert: DCS Code", rx)
2425
            aprscom.append(rs)
2426

    
2427
            options = ["GPS", "Manual"]
2428
            rx = RadioSettingValueList(options, options[_wierd.aprmpos])
2429
            rs = RadioSetting("wierd.aprmpos", "My Position Mode", rx)
2430
            aprscom.append(rs)
2431

    
2432
            optns = ["North", "South"]
2433
            rx = RadioSettingValueList(optns, optns[_wierd.aprltns])
2434
            rs = RadioSetting("wierd.aprltns",
2435
                              "My Position Manual Lat N/S", rx)
2436
            aprscom.append(rs)
2437

    
2438
            v0 = int("%X" % _wierd.aprlad)      # Hex coded Decimal
2439
            rx = RadioSettingValueInteger(0, 90, v0)
2440
            rs = RadioSetting("wierd.aprlad",
2441
                              "My Position Manual Lat Degrees", rx)
2442
            rs.set_apply_callback(self._enchcd, _wierd, "aprlad")
2443
            aprscom.append(rs)
2444

    
2445
            v0 = int("%X" % _wierd.aprlam)      # Whole Minutes, HCD
2446
            v1 = _wierd.aprlas
2447
            v1 = v1 / 1000
2448
            vx = v0 + v1
2449
            rx = RadioSettingValueFloat(0, 59.99, vx, 0.01, 2)
2450
            rs = RadioSetting("wierd.aprlas",
2451
                              "My Position Manual Lat Minutes", rx)
2452
            rs.set_apply_callback(self._mdot, _wierd, "aprlam", "aprlas")
2453
            aprscom.append(rs)
2454

    
2455
            optns = ["East", "West"]
2456
            rx = RadioSettingValueList(optns, optns[_wierd.aprlgew])
2457
            rs = RadioSetting("wierd.aprlgew",
2458
                              "My Position Manual Long N/S", rx)
2459
            aprscom.append(rs)
2460

    
2461
            v0 = int("%X" % _wierd.aprlgd)
2462
            rx = RadioSettingValueInteger(0, 180, v0)
2463
            rs = RadioSetting("wierd.aprlgd",
2464
                              "My Position Manual Long Degrees", rx)
2465
            rs.set_apply_callback(self._enchcd, _wierd, "aprlgd")
2466
            aprscom.append(rs)
2467

    
2468
            v0 = int("%X" % _wierd.aprlgm)
2469
            v1 = _wierd.aprlgs
2470
            v1 = v1 / 1000
2471
            vx = v0 + v1
2472
            rx = RadioSettingValueFloat(0, 59.99, vx, 0.01, 2)
2473
            rs = RadioSetting("wierd.aprlgs",
2474
                              "My Position Manual Long Minutes", rx)
2475
            rs.set_apply_callback(self._mdot, _wierd, "aprlgm", "aprlgs")
2476
            aprscom.append(rs)
2477

    
2478
            # APRS Digipeater Settings
2479

    
2480
            options = ["Off", "Wide 1-1", "Wide 1-1, 1-2", "Path 1",
2481
                       "Path 2", "Path 3", "Path 4", "Full 1", "Full2"]
2482
            rx = RadioSettingValueList(options, options[_wierd.aprdig])
2483
            rs = RadioSetting("wierd.aprdig", "Digipeater Route Selection",
2484
                              rx)
2485
            aprsdgp.append(rs)
2486

    
2487
            # Using shared memory 'share1'; extracting Digipath data
2488
            _shr = self._memobj.share1
2489
            optsfx = [""]           # List for path suffix
2490
            for i in range(1, 16):
2491
                optsfx.append("-%d" % i)
2492
            py = 0                  # route index 0-7 for 'paths'
2493
            pz = 0                  # index to _shr array for dummy memobj
2494
            for px in range(0, 4):      # Path 1 - 4
2495
                sx, sfx = self._getdgpath(py)
2496
                rx = RadioSettingValueString(0, 6, sx, False)
2497
                tx = "Path-%d First Route Callsign" % (px + 1)
2498
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2499
                rs.set_apply_callback(self._putdgpname, py)
2500
                aprsdgp.append(rs)
2501
                pz += 1
2502
                rx = RadioSettingValueList(optsfx, optsfx[sfx])
2503
                tx = "Path-%d First Route SSID" % (px + 1)
2504
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2505
                rs.set_apply_callback(self._putdgpsfx, py)
2506
                aprsdgp.append(rs)
2507
                py += 1
2508
                pz += 1
2509
                sx, sfx = self._getdgpath(py)
2510
                rx = RadioSettingValueString(0, 6, sx, False)
2511
                tx = "Path-%d Second Route Callsign" % (px + 1)
2512
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2513
                rs.set_apply_callback(self._putdgpname, py)
2514
                aprsdgp.append(rs)
2515
                pz += 1
2516
                rx = RadioSettingValueList(optsfx, optsfx[sfx])
2517
                tx = "Path-%d Second Route SSID" % (px + 1)
2518
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2519
                rs.set_apply_callback(self._putdgpsfx, py)
2520
                aprsdgp.append(rs)
2521
                py += 1
2522
                pz += 1
2523
            # Now 8 routes for 'Full1'
2524
            for px in range(0, 8):
2525
                sx, sfx = self._getdgpath(py)
2526
                rx = RadioSettingValueString(0, 6, sx, False)
2527
                tx = "Full-1 Route %d Callsign" % (px + 1)
2528
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2529
                rs.set_apply_callback(self._putdgpname, py)
2530
                aprsdgp.append(rs)
2531
                pz += 1
2532
                rx = RadioSettingValueList(optsfx, optsfx[sfx])
2533
                tx = "Full-1 Route %d SSID" % (px + 1)
2534
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2535
                rs.set_apply_callback(self._putdgpsfx, py)
2536
                aprsdgp.append(rs)
2537
                py += 1
2538
                pz += 1
2539
            # and 8 more for 'Full2'
2540
            for px in range(0, 8):
2541
                sx, sfx = self._getdgpath(py)
2542
                rx = RadioSettingValueString(0, 6, sx, False)
2543
                tx = "Full-2 Route %d Callsign" % (px + 1)
2544
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2545
                rs.set_apply_callback(self._putdgpname, py)
2546
                aprsdgp.append(rs)
2547
                pz += 1
2548
                rx = RadioSettingValueList(optsfx, optsfx[sfx])
2549
                tx = "Full-2 Route %d SSID" % (px + 1)
2550
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2551
                rs.set_apply_callback(self._putdgpsfx, py)
2552
                aprsdgp.append(rs)
2553
                py += 1
2554
                pz += 1
2555

    
2556
            # --- APRS Messages =====
2557

    
2558
            _apmsg = self._memobj.aprsmsg
2559
            for vx in range(1, 7):
2560
                tx = "grp%d" % vx
2561
                bx = getattr(_apmsg, tx)
2562
                sx = self._b2s(bx, 0xca)
2563
                qx = "aprsmsg.grp%d" % vx
2564
                rx = RadioSettingValueString(0, 9, sx, False)
2565
                rs = RadioSetting(qx, "Message Group %d Text" % vx, rx)
2566
                rs.set_apply_callback(self._s2b, _apmsg, tx, 9, 0xca)
2567
                aprsmsg.append(rs)
2568

    
2569
            for vx in range(1, 4):
2570
                tx = "blt%d" % vx
2571
                bx = getattr(_apmsg, tx)
2572
                sx = self._b2s(bx, 0xca)
2573
                qx = "aprsmsg.blt%d" % vx
2574
                rx = RadioSettingValueString(0, 9, sx, False)
2575
                rs = RadioSetting(qx, "Message Bulletin %d Text" % vx, rx)
2576
                rs.set_apply_callback(self._s2b, _apmsg, tx, 9, 0xca)
2577
                aprsmsg.append(rs)
2578

    
2579
            _aptxt = self._memobj.aprstxt
2580
            for vx in range(1, 9):
2581
                tx = "txt%d" % vx
2582
                bx = getattr(_aptxt, tx)
2583
                sx = self._b2s(bx, 0xca)
2584
                qx = "aprstxt.txt%d" % vx
2585
                rx = RadioSettingValueString(0, 16, sx, False)
2586
                rs = RadioSetting(qx, "Message %d Text" % vx, rx)
2587
                rs.set_apply_callback(self._s2b, _aptxt, tx, 16, 0xca)
2588
                aprsmsg.append(rs)
2589

    
2590
            options = []
2591
            for i in range(1, 9):
2592
                options.append("%d" % i)
2593
            rx = RadioSettingValueList(options, options[_wierd.aprstxn])
2594
            rs = RadioSetting("wierd.aprstxn", "Message # Selected", rx)
2595
            aprsmsg.append(rs)
2596

    
2597
            rx = RadioSettingValueList(offon, offon[_wierd.aprrply])
2598
            rs = RadioSetting("wierd.aprrply", "Message Reply Enabled", rx)
2599
            aprsmsg.append(rs)
2600

    
2601
            sx = self._b2s(_wierd.aprrcsn, 0x2a)
2602
            rx = RadioSettingValueString(0, 6, sx, False)
2603
            rs = RadioSetting("wierd.aprrcsn", "Message Reply Callsign", rx)
2604
            rs.set_apply_callback(self._s2b, _wierd, "aprrcsn", 6, 0x2a)
2605
            aprsmsg.append(rs)
2606

    
2607
            options = [" "]
2608
            for i in range(1, 16):
2609
                options.append("-%d" % i)
2610
            options.append("-**")       # index 16
2611
            vx = _wierd.aprrcfx
2612
            if vx == 0x2a:
2613
                vx = 16
2614
            rx = RadioSettingValueList(options, options[vx])
2615
            rs = RadioSetting("wierd.aprrcfx", "Reply Call Sign SSID", rx)
2616
            rs.set_apply_callback(self._aprsfx, _wierd, "aprrcfx", 0x2a)
2617
            aprsmsg.append(rs)
2618

    
2619
            sx = self._b2s(_wierd.aprrtxt, 0xca)
2620
            rx = RadioSettingValueString(0, 64, sx, False)
2621
            rs = RadioSetting("wierd.aprrtxt", "Message Reply Text", rx)
2622
            rs.set_apply_callback(self._s2b, _wierd, "aprrtxt", 64, 0xca)
2623
            aprsmsg.append(rs)
2624

    
2625
            options = ["/#", "/&", "/'", "/-", "/.", "/0", "/:", "/;", "/<",
2626
                       "/=", "/>", "/C", "/E", "/I", "/K", "/O", "/P", "/R",
2627
                       "/T", "/U", "/V", "/W", "/X", "Y", "/[", "/\\", "/^",
2628
                       "/_", "/a", "/b", "/f", "/g", "/j", "/k", "/m", "/r",
2629
                       "/s", "/u", "/v", "/y", "\\#", "\\&", "\\-", "\\.",
2630
                       "\\0", "E0", "IO", "W0", "\\;", "\\>", "\\A", "\\K",
2631
                       "\\W", "\\Y", "KY", "YY", "\\^", "\\_", "\\m", "\\n",
2632
                       "\\s", "\\u", "\\v", "\\x"]
2633
            for i in range(1, 4):
2634
                tx = "aprsym%d" % i
2635
                bx = getattr(_wierd, tx)
2636
                sx = self._b2s(bx, 0)           # 2-byte char string
2637
                qx = "wierd.aprsym%d" % i
2638
                rx = RadioSettingValueList(options, sx)
2639
                rs = RadioSetting(qx, "My Symbol #%d" % i, rx)
2640
                rs.set_apply_callback(self._s2b, _wierd, tx, 2, 0)
2641
                aprsmsg.append(rs)
2642

    
2643
            options = ["/"]
2644
            for i in range(48, 58):
2645
                options.append(chr(i))   # numerals
2646
            for i in range(65, 91):
2647
                options.append(chr(i))   # Cap letters
2648
            options.append("\\")
2649
            sx = chr(_wierd.aprsym4a)
2650
            rx = RadioSettingValueList(options, sx)
2651
            rs = RadioSetting("wierd.aprsym4a", "My Symbol #4 First Value",
2652
                              rx)
2653
            rs.set_apply_callback(self._c2u8, _wierd, "aprsym4a")
2654
            aprsmsg.append(rs)
2655

    
2656
            options = []
2657
            for i in range(33, 127):
2658
                options.append(chr(i))   # full ascii
2659
            sx = chr(_wierd.aprsym4b)
2660
            rx = RadioSettingValueList(options, sx)
2661
            rs = RadioSetting("wierd.aprsym4b", "My Symbol #4 Second Value",
2662
                              rx)
2663
            rs.set_apply_callback(self._c2u8, _wierd, "aprsym4b")
2664
            aprsmsg.append(rs)
2665

    
2666
            options = ["#1", "#2", "#3", "#4"]
2667
            rx = RadioSettingValueList(options, options[_wierd.aprsym])
2668
            rs = RadioSetting("wierd.aprsym", "My Symbol Selected", rx)
2669
            aprsmsg.append(rs)
2670

    
2671
            # --- APRS Beacon Settings -----
2672
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfme])
2673
            rs = RadioSetting("wierd.aprbfme", "Filter: MIC-E", rx)
2674
            bcnfltr.append(rs)
2675

    
2676
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfpo])
2677
            rs = RadioSetting("wierd.aprbfpo", "Filter: Position", rx)
2678
            bcnfltr.append(rs)
2679

    
2680
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfwx])
2681
            rs = RadioSetting("wierd.aprbfwx", "Filter: Weather", rx)
2682
            bcnfltr.append(rs)
2683

    
2684
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfob])
2685
            rs = RadioSetting("wierd.aprbfob", "Filter: Object", rx)
2686
            bcnfltr.append(rs)
2687

    
2688
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfit])
2689
            rs = RadioSetting("wierd.aprbfit", "Filter: Item", rx)
2690
            bcnfltr.append(rs)
2691

    
2692
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfst])
2693
            rs = RadioSetting("wierd.aprbfst", "Filter: Status", rx)
2694
            bcnfltr.append(rs)
2695

    
2696
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfot])
2697
            rs = RadioSetting("wierd.aprbfot", "Filter: Other", rx)
2698
            bcnfltr.append(rs)
2699

    
2700
            options = ["Off", "1", "10", "100", "1000", "3000"]
2701
            v0 = int(_wierd.aprbfrl)     # u16 int: 0,1,10,100,1000,3000
2702
            if v0 == 0:
2703
                vx = 0
2704
            else:
2705
                vx = options.index(str(v0))
2706
            rx = RadioSettingValueList(options, options[vx])
2707
            rs = RadioSetting("wierd.aprbfrl",
2708
                              "Filter: Range Limit (km:miles)", rx)
2709
            rs.set_apply_callback(self._rnglmt, _wierd, "aprbfrl")
2710
            bcnfltr.append(rs)
2711

    
2712
            rx = RadioSettingValueList(offon, offon[_wierd.aprbfan])
2713
            rs = RadioSetting("wierd.aprbfan", "Filter: AltNet", rx)
2714
            bcnfltr.append(rs)
2715

    
2716
            options = ["dd mm ss", "dd mm.mm"]
2717
            rx = RadioSettingValueList(options, options[_wierd.aprbupo])
2718
            rs = RadioSetting("wierd.aprbupo", "Units: Position Format", rx)
2719
            bcnunit.append(rs)
2720

    
2721
            options = ["kilometers", "miles"]
2722
            rx = RadioSettingValueList(options, options[_wierd.aprbudi])
2723
            rs = RadioSetting("wierd.aprbudi", "Units: Distance", rx)
2724
            bcnunit.append(rs)
2725

    
2726
            options = ["km/h", "mph", "knots"]
2727
            rx = RadioSettingValueList(options, options[_wierd.aprbusp])
2728
            rs = RadioSetting("wierd.aprbusp", "Units: Speed", rx)
2729
            bcnunit.append(rs)
2730

    
2731
            options = ["meters", "feet"]
2732
            rx = RadioSettingValueList(options, options[_wierd.aprbual])
2733
            rs = RadioSetting("wierd.aprbual", "Units: Altitude", rx)
2734
            bcnunit.append(rs)
2735

    
2736
            options = ["hPa", "mb", "mmHg", "inHg"]
2737
            rx = RadioSettingValueList(options, options[_wierd.aprbubo])
2738
            rs = RadioSetting("wierd.aprbubo", "Units: Barometric Pressure",
2739
                              rx)
2740
            bcnunit.append(rs)
2741

    
2742
            options = ["Celsius", "Farenheit"]
2743
            rx = RadioSettingValueList(options, options[_wierd.aprbutp])
2744
            rs = RadioSetting("wierd.aprbutp", "Units: Temperature", rx)
2745
            bcnunit.append(rs)
2746

    
2747
            options = ["mm", "inch"]
2748
            rx = RadioSettingValueList(options, options[_wierd.aprburn])
2749
            rs = RadioSetting("wierd.aprburn", "Units: Rain", rx)
2750
            bcnunit.append(rs)
2751

    
2752
            options = ["m/s", "mph", "knots"]
2753
            rx = RadioSettingValueList(options, options[_wierd.aprbuwd])
2754
            rs = RadioSetting("wierd.aprbuwd", "Units: Wind Speed", rx)
2755
            bcnunit.append(rs)
2756

    
2757
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrtb])
2758
            rs = RadioSetting("wierd.aprbrtb", "Ringer: TX Beacon", rx)
2759
            bcnrngr.append(rs)
2760

    
2761
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrtm])
2762
            rs = RadioSetting("wierd.aprbrtm", "Ringer: TX Message", rx)
2763
            bcnrngr.append(rs)
2764

    
2765
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrrb])
2766
            rs = RadioSetting("wierd.aprbrrb", "Ringer: RX Beacon", rx)
2767
            bcnrngr.append(rs)
2768

    
2769
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrrm])
2770
            rs = RadioSetting("wierd.aprbrrm", "Ringer: RX Message", rx)
2771
            bcnrngr.append(rs)
2772

    
2773
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrmp])
2774
            rs = RadioSetting("wierd.aprbrmp", "Ringer: My Packet", rx)
2775
            bcnrngr.append(rs)
2776

    
2777
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrcr])
2778
            rs = RadioSetting("wierd.aprbrcr", "Ringer: Call Ringer", rx)
2779
            bcnrngr.append(rs)
2780

    
2781
            options = ["Off", "1", "5", "10", "50", "100"]
2782
            v0 = int(_wierd.aprbrrr)     # u8 int: 0,1,5,10,50,100
2783
            if v0 == 0:
2784
                vx = 0
2785
            else:
2786
                vx = options.index(str(v0))
2787
            rx = RadioSettingValueList(options, options[vx])
2788
            rs = RadioSetting("wierd.aprbrrr", "Ringer: Range Ringer", rx)
2789
            rs.set_apply_callback(self._rnglmt, _wierd, "aprbrrr")
2790
            bcnrngr.append(rs)
2791

    
2792
            rx = RadioSettingValueList(offon, offon[_wierd.aprbrmv])
2793
            rs = RadioSetting("wierd.aprbrmv", "Ringer: Message Voice", rx)
2794
            bcnrngr.append(rs)
2795

    
2796
            # --- APRS Ringer Call Signs and SSID Suffix ---
2797
            py = 0x18                  # Share1 cs index
2798
            pz = 0x30                  # dummy share1 index
2799
            for px in range(0, 8):      # Calls 1-8
2800
                sx, sfx = self._getdgpath(py)
2801
                rx = RadioSettingValueString(0, 6, sx, False)
2802
                tx = "Ringer Callsign %d" % (px + 1)
2803
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2804
                rs.set_apply_callback(self._putdgpname, py)
2805
                bcnrngr.append(rs)
2806
                pz += 1
2807
                rx = RadioSettingValueList(optsfx, optsfx[sfx])
2808
                tx = "Ringer Callsign %d SSID" % (px + 1)
2809
                rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
2810
                rs.set_apply_callback(self._putdgpsfx, py)
2811
                bcnrngr.append(rs)
2812
                py += 1
2813
                pz += 1
2814

    
2815
            # --- Beacon Status Text ----
2816
            _bstat = self._memobj.bcntxt
2817
            options = ["Off", "Text 1", "Text 2", "Text 3", "Text 4",
2818
                       "Text 5"]
2819
            rx = RadioSettingValueList(options, options[_wierd.bcnstat])
2820
            rs = RadioSetting("wierd.bcnstat", "APRS Beacon Text Select", rx)
2821
            bcnstat.append(rs)
2822

    
2823
            options = ["1/1", "1/2", "1/3", "1/4", "1/5", "1/6", "1/7", "1/8",
2824
                       "1/2(Freq)", "1/3(Freq)", "1/4(Freq)", "1/5(Freq)",
2825
                       "1/6(Freq)", "1/7(Freq)", "1/8(Freq)"]
2826
            rx = RadioSettingValueList(options, options[_wierd.bcntxra])
2827
            rs = RadioSetting("wierd.bcntxra", "Beacon Text Rate", rx)
2828
            bcnstat.append(rs)
2829

    
2830
            options = ["None", "Frequency", "Freq & SQL & Shift"]
2831

    
2832
            for i in range(0, 5):
2833
                vx = i + 1
2834
                sx = self._b2s(_bstat[i].msg, 0xca)
2835
                rx = RadioSettingValueString(0, 60, sx)
2836
                rs = RadioSetting("bcntxt/%d.msg" % i,
2837
                                  "Status Text %d" % vx, rx)
2838
                rs.set_apply_callback(self._s2b, _bstat[i], "msg", 60, 0xca)
2839
                bcnstat.append(rs)
2840

    
2841
                rx = RadioSettingValueList(options, options[_bstat[i].mode])
2842
                rs = RadioSetting("bcntxt/%d.mode" % i,
2843
                                  "Status Text %d Mode" % vx, rx)
2844
                bcnstat.append(rs)
2845

    
2846
            # --- Smart Beaconing
2847

    
2848
            options = ["Off", "Type 1", "Type 2", "Type 3"]
2849
            rx = RadioSettingValueList(options, options[_wierd.bcnsmart])
2850
            rs = RadioSetting("wierd.bcnsmart", "Smart Beaconing Status", rx)
2851
            bcnsmrt.append(rs)
2852

    
2853
            labls = ["Low Speed (2-30)", "High Speed (3-90)",
2854
                     "Slow Rate (1-100 min)", "Fast Rate (10-180 sec)",
2855
                     "Turn Angle (5-90 deg)", "Turn Slope (1-255)",
2856
                     "Turn Time (5-180 sec)"]
2857
            minvals = [2, 3, 1, 10, 5, 1, 5]
2858
            maxvals = [30, 90, 100, 180, 90, 255, 180]
2859
            for v0 in range(1, 4):       # Type
2860
                sx = "Type %d" % v0
2861
                for v1 in range(0, 7):  # Parameters
2862
                    v2 = getattr(_wierd, "typ%dval%d" % (v0, v1))
2863
                    rx = RadioSettingValueInteger(minvals[v1], maxvals[v1], v2)
2864
                    rs = RadioSetting("wierd.typ%dval%d" % (v0, v1),
2865
                                      "%s %s" % (sx, labls[v1]), rx)
2866
                    bcnsmrt.append(rs)
2867
        # End If FTM-200 Unique settings
2868
        return group       # End get_settings()
2869

    
2870
    def set_settings(self, settings):
2871
        # _setx = self._memobj.setts
2872
        for element in settings:
2873
            if not isinstance(element, RadioSetting):
2874
                self.set_settings(element)
2875
                continue
2876
            else:
2877
                try:
2878
                    name = element.get_name()
2879
                    if "." in name:
2880
                        bits = name.split(".")
2881
                        obj = self._memobj
2882
                        for bit in bits[: -1]:
2883
                            if "/" in bit:
2884
                                bit, index = bit.split("/", 1)
2885
                                index = int(index)
2886
                                obj = getattr(obj, bit)[index]
2887
                            else:
2888
                                obj = getattr(obj, bit)
2889
                        setting = bits[-1]
2890
                    else:
2891
                        # obj = _setx
2892
                        obj = self._memobj
2893
                        setting = element.get_name()
2894

    
2895
                    if element.has_apply_callback():
2896
                        LOG.debug("Using apply callback")
2897
                        element.run_apply_callback()
2898
                    elif element.value.get_mutable():
2899
                        LOG.debug("Setting %s = %s" % (setting, element.value))
2900
                        setattr(obj, setting, element.value)
2901
                except Exception:
2902
                    LOG.debug(element.get_name())
2903
                    raise
2904

    
2905

    
2906
class Ftm200dAlias(chirp_common.Alias):
2907
    VENDOR = "Yaesu"
2908
    MODEL = "FTM-200D"
2909

    
2910

    
2911
@directory.register
2912
class FTM200radio(FTM6000Radio):
2913
    """Yaesu FTM-200"""
2914
    FTM200 = True
2915
    MODEL = "FTM-200"
2916
    NAME_LEN = 16       # Valid name length
2917
    ALIASES = [Ftm200dAlias]
2918
    CHARSET = list(chirp_common.CHARSET_ASCII)
2919
    MODES = ["AM", "FM", "NFM", "DN"]
2920
    NEEDS_COMPAT_SERIAL = False
2921
    TESTME = False
    (1-1/1)