diff --git a/chirp/drivers/ftm6000.py b/chirp/drivers/ftm6000.py
new file mode 100644
index 000000000..a42cb26b0
--- /dev/null
+++ b/chirp/drivers/ftm6000.py
@@ -0,0 +1,2910 @@
+# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com) V1.0
+# Issues 9719 for FTM-6000 and 11137 for FTM-200
+# Note: 'TESTME' flags are intended for use with future sub-models
+# FTM-100, FTM-300 and FTM-500 can probably be supported
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+"""FTM-6000 Yaesu Radio Driver"""
+
+from chirp.drivers import yaesu_clone
+from chirp import chirp_common, util, memmap, errors, directory, bitwise
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+ RadioSettingValueInteger, RadioSettingValueList, \
+ RadioSettingValueBoolean, RadioSettingValueString, \
+ RadioSettingValueFloat, RadioSettings
+import time
+import struct
+import logging
+import math
+import sys
+
+LOG = logging.getLogger(__name__)
+CMD_ACK = b'\x06'
+
+
+@directory.register
+class FTM6000Radio(yaesu_clone.YaesuCloneModeRadio):
+ """Yaesu FTM-6000"""
+ VENDOR = "Yaesu"
+ FTM200 = False
+ TESTME = False
+ NEEDS_COMPAT_SERIAL = False
+ BAUD_RATE = 38400
+ COM_BITS = 8
+ COM_PRTY = 'N'
+ COM_STOP = 1
+ MODEL = "FTM-6000"
+ NAME_LEN = 6 # Valid Name Length
+ MODES = ["FM", "AM", "NFM"]
+ TMODES = ["", "Tone", "TSQL", "TSQL-R", "DTCS", "Cross"]
+ CROSS_MODES = ["->DTCS", "Tone->DTCS", "DTCS->Tone"]
+ DUPLEX = ["", "off", "-", "+", "split"]
+ T_STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
+ VALID_BANDS = [(108000000, 137000000),
+ (137000000, 174000000),
+ (174000000, 400000000),
+ (400000000, 480000000),
+ (480000000, 999999999)]
+ POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=50),
+ chirp_common.PowerLevel("Mid", watts=20),
+ chirp_common.PowerLevel("Low", watts=5)]
+ SKIPS = ["", "S"]
+ # No lowercase,{} or ~
+ CHARSET = [chr(x) for x in list(range(ord(" "), ord("_") + 1)) +
+ [ord("|")]]
+ DTMF_CHARS = list("0123456789ABCD*#")
+ MEM_FORMAT = """
+ struct memdat { // 16 bytes per chan
+ u8 used:1, // 00
+ skip:2,
+ unk1:2,
+ band:3;
+ u8 clks:1, // 01
+ xx:1,
+ mode:2,
+ duplex:4;
+ u24 freq; // 02-04
+ u8 tmode:4, // 05
+ step:4;
+ u24 frqtx; // 06-08
+ u8 power:2, // 09
+ rtone:6;
+ u8 clktyp:1, // 0A,
+ dtcs:7;
+ u8 prfreq; // 0B
+ u16 offset; // 0C-0D 0-99.95 MHz in 0.5 steps 06.0 is 00 0c
+ u8 unk3; // 0E
+ u8 unk4; // 0F
+ };
+
+ struct tag {
+ u8 chname[16];
+ };
+
+ struct pmgx {
+ u8 za; // 0x00
+ u8 fb; // 0xff
+ u16 chnp; // 0-based pmg channel number
+ };
+
+ struct { // In 1st block
+ u8 id[6];
+ } model;
+ #seekto 0x080;
+ struct memdat vfo_air; // 080
+ struct memdat vfo_144; // 090
+ struct memdat vfo_vhf; // 0a0
+ struct memdat vfo_430; // 0b0
+ struct memdat vfo_uhf; // 0c0
+ struct memdat unkc1[5]; // 0d0 - 0110 repeats vfo entries
+ u8 120[16]; // 0x120 all ff
+ struct memdat home; // 0x130
+ u8 ff2[64]; // 0x140 - 0x17f; ff's'
+ struct memdat unkc2; // 0x180 possible last chan accessed?
+ #seekto 0x200;
+ struct {
+ u8 20axd[14]; // 0x200 - 0x20d
+ u8 xm0au:5, // 0x20e
+ bell:3;
+ u8 xm0bu:6, // 020f
+ lcd:2;
+ u8 210xd[14]; // 0210 - 021d
+ u8 21ea:5,
+ bellb:3;
+ u8 wrxdid; // 21f
+ u8 220x1[2]; // 220 -221
+ u8 fkp1; // 0x222: FTM-200 keypad quick access 1
+ u8 fhm1; // 0x223: FTM-200 home qa 1
+ u8 fkp2;
+ u8 fhm2;
+ u8 fkp3;
+ u8 fhm3;
+ u8 fkp4;
+ u8 fhm4; // 0x229
+ u8 22axf[6];
+ u8 230x7[8]; // 230-237
+ u24 wrxvfrq;
+ u8 23b;
+ u24 wrxufrq;
+ u8 23f;
+ u8 240a:6,
+ wrxbsel:1,
+ 240b:1;
+ u8 241x7[7]; // 24-247
+ u8 csign[10]; // 248 - 251
+ u8 pgrcdr1; // 0252
+ u8 pgrcdr2;
+ u8 pgrcdt1;
+ u8 pgrcdt2; // 0255
+ u8 256a:7,
+ compass:1;
+ u8 257a:7,
+ usbcamsz:1;
+ u8 258a:6,
+ usbcamql:2;
+ u8 259;
+ u8 25a;
+ u8 25ba:4,
+ wrxpop:4;
+ u8 25c;
+ u8 micgain; // 25d
+ u8 25e;
+ u8 micp1; // 25f
+ u8 micp2; // 260
+ u8 micp3;
+ u8 micp4;
+ u8 263xf[13]; // 0x263 - 026f
+ u8 270xf[16]; // 0x270 - 27f
+ } micset;
+ struct {
+ u8 codes[16]; // 0x280-30f
+ } dtmfcode[9];
+ #seekto 0x400;
+ struct {
+ u8 400a:7, // 0400
+ aprbupo:1;
+ u8 401a:7,
+ aprbudi:1;
+ u8 402a:6,
+ aprbusp:2;
+ u8 403a:7, // 0403
+ aprbual:1;
+ u8 404a:5,
+ aprbubo:3;
+ u8 405a:7,
+ aprbutp:1;
+ u8 406a:7,
+ aprburn:1;
+ u8 407a:5,
+ aprbuwd:3;
+ u8 aprcsgn[6]; // 0408-040d
+ u8 aprcsfx;
+ u8 400f;
+ u8 410a:3,
+ aprltns:1,
+ 410b:4;
+ u8 aprlad; // 0411
+ u8 aprlam;
+ u16 aprlas; // 0413-0414
+ u8 415a:3,
+ aprlgew:1,
+ 415b:4;
+ u8 aprlgd;
+ u8 aprlgm;
+ u16 aprlgs;
+ u8 aprrcsn[6]; // 041a-41f
+ u8 aprrcfx; // 0420
+ u8 421;
+ u8 aprsym1[2]; // 0422, 423
+ u8 aprsym2[2];
+ u8 aprsym3[2];
+ u8 aprsym4a;
+ u8 aprsym4b; // 0429
+ u16 aprbfrl; // 042a & 042b
+ u8 42ca:4,
+ aprsym:4;
+ u8 42da:4, // 042d
+ aprdig:4;
+ u8 42ea:4,
+ aprstxi:4;
+ u8 aprsrate; // 042f
+ u8 aprslow; // 0430
+ u8 431a:5,
+ aprbamb:3;
+ u8 432a:4,
+ aprcmt:4;
+ u8 aprspro:1, // 0433
+ aprbaut:1,
+ 433b:1,
+ aprsdcy:1,
+ aprspdc:1,
+ aprsalt:1,
+ 433c:1,
+ aprrply:1;
+ u8 434a:1, // 0434
+ dspda:1, // FTM-200 APRS
+ pktspd:1, // Both
+ 434b:3,
+ aprmut:1,
+ aprson:1;
+ u8 435a:2,
+ datsql:1,
+ 435b:5;
+ u8 436a:5,
+ bcnstat:3;
+ u8 437a:4, // 0x437
+ bcntxra:4;
+ u8 438a:1,
+ aprbfot:1,
+ aprbfst:1,
+ aprbfit:1,
+ aprbfob:1,
+ aprbfwx:1,
+ aprbfpo:1,
+ aprbfme:1;
+ u8 439a:7,
+ aprbrtb:1;
+ u8 43aa:7,
+ aprbrtm:1;
+ u8 43ba:7,
+ aprbrrb:1;
+ u8 43ca:7,
+ aprbrrm:1;
+ u8 43da:7,
+ aprbrmp:1;
+ u8 43ea:7,
+ aprbrcr:1;
+ u8 aprbrrr; // 043f
+ u8 440a:7,
+ aprbrmv:1;
+ u8 441a:7,
+ aprmpos:1;
+ u8 442;
+ u8 443a:4,
+ dbsela:4;
+ u8 444a:4,
+ dbseld:4;
+ u8 aprpopb; // 0445
+ u8 aprpopm;
+ u8 447a:4,
+ aprtxd:4;
+ u8 448a:4,
+ comout:4;
+ u8 449a:4,
+ comspd:4;
+ u8 44aa:5,
+ bcnsmart:3;
+ u8 typ1val0; // 044b -> 045f
+ u8 typ1val1; // Smart Beaconing type settings
+ u8 typ1val2;
+ u8 typ1val3;
+ u8 typ1val4;
+ u8 typ1val5;
+ u8 typ1val6;
+ u8 typ2val0;
+ u8 typ2val1;
+ u8 typ2val2;
+ u8 typ2val3;
+ u8 typ2val4;
+ u8 typ2val5;
+ u8 typ2val6;
+ u8 typ3val0;
+ u8 typ3val1;
+ u8 typ3val2;
+ u8 typ3val3;
+ u8 typ3val4;
+ u8 typ3val5;
+ u8 typ3val6; // 045f
+ u8 460a:4,
+ aprstxn:4;
+ u8 461a:1,
+ aprmpkt:1,
+ aprbfan:1,
+ 461b:5;
+ u8 462a:4,
+ comwpf:4;
+ u8 463a:4,
+ comflt:4;
+ u8 464;
+ u8 465;
+ u8 466;
+ u8 467;
+ u8 468;
+ u8 469;
+ u8 46a;
+ u8 46b;
+ u8 46c;
+ u8 46da:5,
+ aprvlrt:3;
+ u8 aprtsql;
+ u8 aprvdcs; // 046f
+ u8 470a:6,
+ aprsfs:2;
+ u8 471a:4,
+ aprsff:4;
+ u8 472xf[14]; // -> 047f
+ u8 aprrtxt[64]; // 0480 - 04BF
+ } wierd;
+ #seekto 0x500;
+ struct {
+ u8 grp1[9];
+ u8 509;
+ u8 grp2[9];
+ u8 514;
+ u8 grp3[9];
+ u8 51d;
+ u8 grp4[9];
+ u8 527;
+ u8 grp5[9];
+ u8 532;
+ u8 grp6[9];
+ u8 53b;
+ u8 blt1[9];
+ u8 545;
+ u8 blt2[9];
+ u8 554;
+ u8 blt3[9];
+ u8 559;
+ } aprsmsg;
+ #seekto 0x580;
+ struct { // FMT-6000 Menu item Fntcn mode set table
+ u8 shbyt; // FTM-200 Digipath, RingerCS data
+ } share1[256]; // -> 0x67f
+ struct { // 0x680
+ u8 txt1[16];
+ u8 txt2[16];
+ u8 txt3[16];
+ u8 txt4[16];
+ u8 txt5[16];
+ u8 txt6[16];
+ u8 txt7[16];
+ u8 txt8[16];
+ } aprstxt;
+ struct memdat main[999]; // 0x700 16 bytes each, up to 4570
+ struct memdat pms[100]; // up to 0x4bb0
+ struct memdat unkc3[5]; // unknown, up to 0x4c00
+ #seekto 0x4c10;
+ struct pmgx pmg[6]; // 6th element is change record
+ #seekto 0x6980;
+ struct { // WIRES-X Messages
+ u8 msg01[128];
+ u8 msg02[128];
+ u8 msg03[128];
+ u8 msg04[128];
+ u8 msg05[128];
+ u8 msg06[128];
+ u8 msg07[128];
+ u8 msg08[128];
+ u8 msg09[128];
+ u8 msg10[128];
+ } wrxmsg;
+ struct { // 0x6e80
+ u8 c1[16];
+ u8 c2[16];
+ u8 c3[16];
+ u8 c4[16];
+ u8 c5[16];
+ } wrxcat;
+ #seekto 0x7d00;
+ struct { // --> 07e3f
+ u8 msg[60];
+ u8 mode;
+ u8 nax[3];
+ } bcntxt[5];
+ #seekto 0xff00;
+ struct tag names[999]; // 16 bytes each, up to 0x13d6f (83055.)
+ struct tag pms_name[100]; // 13d70 - 143af
+ #seekto 0x17f00;
+ struct { // Settings
+ u8 f00xf[16]; // 17f00-0f
+ u8 f10xf[16]; // 17f10-1f
+ u8 f20; // 17f20
+ u8 f21a:7,
+ unit:1;
+ u8 f22;
+ u8 f23;
+ u8 f24a:2, // 17f24
+ apo:5,
+ apo1:1;
+ u8 bclo:1, // 17f25
+ f25a:4,
+ arts_int:1,
+ arts_mode:2;
+ u8 f26:4, // 17f26
+ airb:1,
+ vhfb:1,
+ uhfb:1,
+ otrb:1;
+ u8 f27;
+ u8 tzone;
+ u8 f29a:6,
+ fvsvol:2;
+ u8 f2au:4, // 17f2a
+ tot:4;
+ u8 f2b;
+ u8 f2c;
+ u8 f2d:6,
+ dspmode:2;
+ u8 f2ea:6,
+ fvsanc:2;
+ u8 f2fa:6, // 17f2f
+ gpsdtm:1,
+ f2fb:1;
+ u8 lastmnu; // 17f30
+ u8 f31; // 17f31
+ u8 f1key; // 17f32
+ u8 f33;
+ u8 f34;
+ u8 f35a:4,
+ gpslog:4;
+ u8 f36a:6,
+ voxsen:2; // FTM-200
+ u8 f37a:5,
+ voxdly:3;
+ u8 f38a:6, // 17f38
+ audrec:2;
+ u8 lastfunc; // 17f39
+ u8 f3a:6, // 17f3a
+ lcd_clr:2;
+ u8 f3bxf[5]; // 17f3b-3f
+ u8 f40x2[3]; // 7f40-42
+ u8 a_chan; // FTM-200 17f43
+ u8 f44;
+ u8 f45u:5, // 17f45
+ scnrsm:3;
+ u8 f46;
+ u8 f47;
+ u8 f48u:4, // 17f48
+ sql:4;
+ u8 f49:4,
+ scnrsm2a:4; // FTM-200 A
+ u8 f4a; // 17f4a
+ u8 f4ba:4,
+ scndria:4;
+ u8 f4c;
+ u8 f4d;
+ u8 f4e;
+ u8 f4f;
+ u8 f50x2[3]; // 17f50-52
+ u8 b_chan; // FTM-200 17f53
+ u8 f54;
+ u8 f55;
+ u8 f56;
+ u8 f57;
+ u8 f58;
+ u8 f59:4,
+ scnrsm2b:4; // FTM-200 B
+ u8 f5a; // 17f5a
+ u8 f5ba:4,
+ scndrib:4;
+ u8 f5c;
+ u8 f5d;
+ u8 f5e;
+ u8 f5f;
+ u8 f60; // 17f60
+ u8 f61;
+ u8 scndrm1:1, // 17f62
+ wxalrt:1,
+ f62b:5,
+ dwrvt:1;
+ u8 f63a:7, // 17f63
+ sqlexp1:1;
+ u8 f64a:5, // 17f64
+ rpt_ars:1,
+ f64b:2;
+ u8 f65a:2,
+ bscope:1,
+ f65b:5;
+ u8 f66;
+ u8 f67;
+ u8 f68a:3,
+ wrxstb:1,
+ f68b:1,
+ locinfo:1,
+ wrxdvw:1,
+ f68c:1;
+ u8 f69;
+ u8 f6aa:4,
+ audmic:1,
+ f6ab:2,
+ btsave:1;
+ u8 wrxloc:1, // 17f6b
+ fvslan:1,
+ f6bb:2,
+ beep1:1, // Stupid split beep: this is Off/Low
+ fvsrxm:1,
+ f6bc:2;
+ u8 gpsdev:1, // 17f6c
+ memlist:1,
+ wrxrng:1,
+ wrxrwf:1,
+ wrxsrch:1,
+ f6cb:2,
+ scndrm2:1;
+ u8 bskpax:3, // 17f6d
+ bskpa0:1,
+ bskpa4:1,
+ bskpa3:1,
+ bskpa2:1,
+ bskpa1:1;
+ u8 f6e;
+ u8 f6f;
+ u8 f70; // 17f70
+ u8 f71;
+ u8 f72a:7,
+ dwrvtb:1;
+ u8 f73a:7,
+ sqlexp2:1;
+ u8 f74a:5,
+ rpt_arsb:1,
+ f74b:2;
+ u8 f75a:2,
+ bscopeb:1,
+ f75b:5;
+ u8 f76;
+ u8 f77;
+ u8 f78a:5, // 17f78
+ dtmf_auto:1,
+ bton:1,
+ btaud:1;
+ u8 f79a:3, // 17f79
+ timefmt:1,
+ datefmt:4;
+ u8 f7aa:3,
+ beep2:1, // Split beep: High
+ f7ab:3,
+ fvsrec:1;
+ u8 f7ba:6,
+ wrxams:2;
+ u8 f7c;
+ u8 bskpbx:3, // 17f7d
+ bskpb0:1,
+ bskpb4:1,
+ bskpb3:1,
+ bskpb2:1,
+ bskpb1:1;
+ u8 f7e;
+ u8 f7f;
+ u8 f80[16]; // 17f80
+ u8 f90[16]; // 17f90
+ u8 fA0[16]; // 17fA0
+ u8 fB0[16]; // 17fB0
+ u8 FC0[16]; // 17fC0
+ u8 FD0[16]; // 17fD0
+ u8 FE0[16]; // 17fE0
+ u8 FF0[16]; // 17fF0
+ } setts;
+ """
+
+ @classmethod
+ def get_prompts(cls):
+ rp = chirp_common.RadioPrompts()
+ rp.info = (
+ "The duplex setting 'split' allows for non-standard offsets "
+ "by configuring both a\n"
+ "receive and transmit frequency. Set the receive frequency, "
+ "then the duplex mode,\n"
+ "then the desired transmit frequency as the offset.\n"
+ "The offset value of a split channel is the transmit frequency, "
+ "and can be modified.\n"
+ "PMS (Programmable Memory Channel Scan) pairs are displayed "
+ "as memory channels 1000-1099.\n"
+ "The Clock Shift Function can be set in the Properties "
+ "> Extra tab.\n"
+ "The radio special tone modes (REV, PR, PAGER) can also be set "
+ "in the Properties > Extra tab,\n"
+ "but will be displayed as Tone in CHIRP.")
+ if cls.FTM200:
+ rp.pre_download = (
+ "(OK then TX)\n"
+ "With the USB cable connected to the radio rear data port, "
+ "and the radio powered off-\n"
+ "1. Long-Press F-Menu.\n"
+ "2. Rotate the dial knob to menu 116: 'This -> Other'.\n"
+ "3. Press the dial knob.\n"
+ "4. Rotate to select OK, but don't press yet.\n"
+ "5. Press the OK button below.\n"
+ "6. Press the dial knob to begin sending data.\n"
+ "7. Wait for Complete.\n")
+ rp.pre_upload = (
+ "(Rx then OK)\n"
+ "With the USB cable connected to the radio rear data port, "
+ "and the radio powered off-\n"
+ "1. Long-Press F-Menu.\n"
+ "2. Rotate the dial knob to menu 117: 'Other -> This'.\n"
+ "3. Press the dial knob.\n"
+ "4. Rotate to select OK, and press the knob.\n"
+ "5. Press the OK button below.\n"
+ "6. Wait for Complete.\n")
+ else:
+ rp.pre_download = (
+ "(OK then TX)\n"
+ "With the USB cable connected to the radio rear data port, "
+ "and the radio powered off-\n"
+ "1. Press both the power ON and F1 keys.\n"
+ "2. Press the dial knob.\n"
+ "3. Rotate the dial to show CLN TX.\n"
+ "4. Press the OK button below/\n"
+ "5. Press the dial knob to begin sending data.\n"
+ "6. Wait for Complete.\n"
+ "7. Long-press the power On button to leave the clone mode.")
+ rp.pre_upload = (
+ "(Rx then OK)\n"
+ "With the USB cable connected to the radio rear data port, "
+ "and the radio powered off-\n"
+ "1. Press both the power ON and F1 keys.\n"
+ "2. Press the dial knob, CLN RX is displayed.\n"
+ "3. Press the dial knob again to put the radio in the "
+ "receiving state.\n"
+ "4. Press the OK button below.\n"
+ "5. Wait for Complete.\n"
+ "6. Long-press the power On button to leave the clone mode.")
+ return rp
+
+ def _read(self, blck, blsz):
+ # be very patient at first block
+ if blck == 0:
+ attempts = 60
+ else:
+ attempts = 5
+ for _i in range(0, attempts):
+ data = self.pipe.read(blsz)
+ if data:
+ break
+ time.sleep(0.5)
+ if len(data) == blsz:
+ LOG.debug("Received block %s, sub %s" % (hex(data[0]),
+ hex(data[1])))
+ checksum = data[blsz - 1]
+ cs = 0
+ for i in data[:-1]:
+ cs = (cs + i) % 256
+ if cs != checksum:
+ raise Exception("Checksum Failed [%02X<>%02X] block %02X" %
+ (checksum, cs, blck))
+ # Remove the 2-byte block header and checksum
+ data = data[2:blsz - 1]
+ else:
+ raise Exception("Unable to read block %i expected %i got %i"
+ % (blck, blsz, len(data)))
+ return data
+
+ def _clone_in(self):
+ # Be very patient with the radio
+ self.pipe.timeout = 2
+ self.pipe.baudrate = self.BAUD_RATE
+ self.pipe.bytesize = self.COM_BITS
+ self.pipe.parity = self.COM_PRTY
+ self.pipe.stopbits = self.COM_STOP
+ self.pipe.rtscts = False
+ data = b""
+ status = chirp_common.Status()
+ status.msg = "Cloning from radio"
+ nblocks = 768
+ status.max = nblocks - 1
+ blksz = 131
+ for block in range(0, nblocks): # SPS - reads to nblocks-1
+ LOG.debug("Reading packet %d" % block)
+ data += self._read(block, blksz)
+ self.pipe.write(CMD_ACK)
+ status.cur = block
+ self.status_fn(status)
+ return memmap.MemoryMapBytes(data)
+
+ def _write(self, nblk, nsub, pntr, bksz, zblk):
+ LOG.debug("Block %02X %02X" % (nblk, nsub))
+ if self.TESTME:
+ LOG.warning("Block %02X %02X" % (nblk, nsub))
+ data = struct.pack('B', nblk)
+ data += struct.pack('B', nsub)
+ if zblk:
+ blkdat = bytearray(bksz) # All Zeros
+ else:
+ blkdat = self.get_mmap()[pntr:pntr + bksz]
+ data += blkdat
+ cs = 0
+ for i in data:
+ cs = (cs + i) % 256
+ LOG.debug("Checksum %s" % hex(cs))
+ if self.TESTME:
+ LOG.warning("Checksum %s" % hex(cs))
+ data += struct.pack('B', cs)
+ LOG.debug("Writing %d bytes:\n%s" % (len(data),
+ util.hexprint(data)))
+ if self.TESTME: # Dont send data yet
+ LOG.warning("Writing %d bytes:\n%s" % (len(data),
+ util.hexprint(data)))
+ buf = CMD_ACK
+ else:
+ self.pipe.write(data)
+ buf = self.pipe.read(1)
+ if not buf or buf[:1] != CMD_ACK:
+ time.sleep(0.5)
+ buf = self.pipe.read(1)
+ if not buf or buf[:1] != CMD_ACK:
+ return False
+ return True
+
+ def _clone_out(self):
+ self.pipe.baudrate = self.BAUD_RATE
+ self.pipe.bytesize = self.COM_BITS
+ self.pipe.parity = self.COM_PRTY
+ self.pipe.stopbits = self.COM_STOP
+ self.pipe.rtscts = False
+
+ status = chirp_common.Status()
+ status.msg = "Cloning to radio"
+ blksz = 128 # actual data; excluding block, sub, checksum
+ pkt = 0
+ pos = 0
+ block = 0
+ wrtzero = False # Special last block flag
+ nblocks = 767 # last block number
+ status.max = nblocks - 2
+ # This sucker took FOREVER to figure out the repeating block
+ # + sub-block + special EoD numbering!
+ while pkt <= nblocks:
+ LOG.debug("Writing packet #: %d." % pkt)
+ if self.TESTME:
+ LOG.warning("Writing packet #: %d , pos %x." % (pkt, pos))
+ time.sleep(0.01)
+ sub2 = True
+ sublk = 0
+ if pkt == 0:
+ block = pkt
+ sub2 = False
+ elif pkt == 1:
+ block = pkt
+ elif pkt == 7: # Block 04 00 is skipped !!??
+ block = 0x04
+ sublk = 0x80
+ sub2 = False # block increments to 05 00
+ elif pkt == 510: # Reset to block 0
+ block = 0
+ elif pkt == 766:
+ block = 0xff
+ sublk = 0xfd
+ sub2 = False
+ elif pkt == 767:
+ block = 0xff
+ sublk = 0xfe
+ sub2 = False
+ # pkt 767, block ff fe must be all zeros!!???
+ wrtzero = True
+ rslt = self._write(block, sublk, pos, blksz, wrtzero)
+ if rslt and sub2: # write same block, new sublk
+ sublk = 0x80
+ pkt += 1
+ pos += blksz
+ LOG.debug("Writing packet #: %.d" % pkt)
+ if self.TESTME:
+ LOG.warning("Writing packet #: %d , pos %x." % (pkt, pos))
+ rslt = self._write(block, sublk, pos, blksz, wrtzero)
+ if not rslt:
+ raise Exception("Radio did not ack block %i %i" %
+ (block, sublk))
+ pos += blksz
+ block += 1
+ pkt += 1
+ status.cur = pkt
+ self.status_fn(status)
+ return
+
+ def sync_in(self):
+ try:
+ self._mmap = self._clone_in()
+ except errors.RadioError:
+ raise
+ except Exception as e:
+ raise errors.RadioError("Failed to communicate with radio: %s"
+ % e)
+ self.process_mmap()
+
+ def sync_out(self):
+ try:
+ self._clone_out()
+ except errors.RadioError:
+ raise
+ except Exception as e:
+ raise errors.RadioError("Failed to communicate with radio: %s"
+ % e)
+
+ def process_mmap(self):
+ self._memobj = bitwise.parse(self.MEM_FORMAT, self._mmap)
+
+ def get_features(self):
+ rf = chirp_common.RadioFeatures()
+ rf.has_dtcs_polarity = False
+ rf.has_bank = False
+ rf.has_dtcs = True
+ rf.has_cross = True
+ rf.has_tuning_step = True
+ rf.has_ctone = False # Common Tones
+ rf.has_rx_dtcs = False # Common codes
+ rf.has_settings = True
+ rf.can_odd_split = True
+ rf.valid_modes = [x for x in self.MODES if x in chirp_common.MODES]
+ rf.valid_tmodes = self.TMODES
+ rf.valid_cross_modes = self.CROSS_MODES
+ rf.valid_duplexes = self.DUPLEX
+ rf.valid_tuning_steps = self.T_STEPS
+ rf.valid_bands = self.VALID_BANDS
+ rf.valid_power_levels = self.POWER_LEVELS
+ rf.valid_characters = "".join(self.CHARSET)
+ rf.valid_name_length = self.NAME_LEN
+ rf.memory_bounds = (1, 1099)
+ rf.valid_skips = self.SKIPS
+ return rf
+
+ def get_raw_memory(self, number):
+ return repr(self._memobj.main[number - 1])
+
+ def validate_memory(self, mem):
+ msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem)
+ return msgs
+
+ def filter_name(self, namsx):
+ """Name must be <= NAME_LEN and contain only CHARSET chars"""
+ tx = namsx.strip()
+ sx = ""
+ if self.FTM200 is False:
+ tx = tx.upper()
+ for i in range(0, len(tx)):
+ if i >= self.NAME_LEN:
+ break
+ if tx[i] in self.CHARSET:
+ sx += tx[i]
+ else:
+ sx = "?"
+ return sx
+
+ def _freqdcode(self, frqb):
+ """Decode .0025 Mhz upper 2 MSB of freq"""
+ # frqb is u24: 2bits suffix, 22 bits bcd
+ sx = "%06X" % frqb
+ v0 = int(sx[0], 16) * 625
+ v1 = int(sx[1]) # 100 Mhz
+ v2 = int(sx[2:4]) # 10 Mhz
+ v3 = int(sx[4:]) # 100 Khz
+ vx = (v1 * 10000) + (v2 * 100) + v3
+ vx = vx * 10000 + v0
+ return vx
+
+ def _freqncode(self, frq):
+ """Encode .0625 Mhz in upper MSB of u24 bcd freq"""
+ v0 = frq % 10000 # .025 value
+ bx = v0 // 625
+ bx = bx * 0x100000
+ vx = int((frq - v0) / 10000)
+ sx = "%06d" % vx # Ex: 012345 for frq = 123.45 MHz
+ v1 = int("0x" + sx[0:2], 16)
+ v2 = int("0x" + sx[2:4], 16)
+ v3 = int("0x" + sx[4:], 16)
+ frqb = bx + v1 * 0x10000 + v2 * 0x100 + v3
+ return frqb
+
+ def _b2s(self, bary, term=0xff):
+ """Convert byte array into string """
+ strx = ""
+ for i in bary:
+ if i == term:
+ break
+ strx += chr(i)
+ return strx
+
+ def _s2b(self, setting, obj, atrb, mxk, pad=0xff, upc=True):
+ """Callback: Convert string to byte array, pad to mxk chars"""
+ sx = str(setting.value)
+ sx = sx.strip()
+ if upc:
+ sx = sx.upper()
+ ary = b""
+ v1 = len(sx)
+ for vx in range(0, mxk):
+ if vx < v1:
+ ary += bytes(sx[vx], 'utf-8')
+ else:
+ ary += pad.to_bytes(1, sys.byteorder)
+ setattr(obj, atrb, ary)
+ return
+
+ def _c2u8(self, setting, obj, atrb):
+ """Callback: Convert single char string to u8"""
+ b0 = str(setting.value)
+ vx = ord(b0)
+ setattr(obj, atrb, vx)
+ return
+
+ def get_memory(self, number):
+ mem = chirp_common.Memory()
+ if number < 1000: # channel memory
+ _mem = self._memobj.main[number - 1]
+ _tag = self._memobj.names[number - 1]
+ else:
+ _mem = self._memobj.pms[number - 1000]
+ _tag = self._memobj.pms_name[number - 1000]
+ mem.number = number
+ if mem.number == 1:
+ mem.immutable += ["empty"]
+ if not _mem.used:
+ mem.empty = True
+ return mem
+ mem.freq = self._freqdcode(_mem.freq)
+ mem.rtone = chirp_common.TONES[_mem.rtone]
+ mem.mode = self.MODES[_mem.mode]
+ mem.duplex = self.DUPLEX[_mem.duplex]
+ if (_mem.duplex == 4): # Split mode
+ mem.offset = self._freqdcode(_mem.frqtx)
+ else:
+ mem.offset = int(_mem.offset) * 50000
+ # Re-map funky radio tone and cross-modes
+ tmd = _mem.tmode # 0,1,2
+ cmx = 0
+ if _mem.tmode == 3: # DCS
+ tmd = 5 # Cross
+ cmx = 0 # -> DTCS
+ if _mem.tmode == 4: # Rev Tone
+ tmd = 3 # TSQL-R
+ if _mem.tmode == 5: # PR Freq
+ tmd = 1
+ if _mem.tmode == 6: # Pager
+ tmd = 1
+ if _mem.tmode == 7: # Tx DTCS, Open RX
+ tmd = 4 # DTCS
+ elif _mem.tmode == 8: # Tone - DCS
+ tmd = 5 # Cross
+ cmx = 1 # Tone -> DTCS
+ elif _mem.tmode == 9: # D CD-> Tone
+ tmd = 5 # Cross
+ cmx = 2 # DTCS->Tone
+ mem.tmode = self.TMODES[tmd]
+ mem.cross_mode = self.CROSS_MODES[cmx]
+ mem.skip = self.SKIPS[_mem.skip]
+ mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
+ vx = _mem.power
+ mem.power = self.POWER_LEVELS[vx]
+ mem.tuning_step = self.T_STEPS[_mem.step]
+ tx = self._b2s(_tag.chname, 0xff).strip()
+ if self.FTM200 is False:
+ tx = tx.upper()
+ mem.name = tx
+ # Echo name string back into memory.
+ # In case it got loaded flakey in radio; trailing spaces
+ for i in range(0, self.NAME_LEN):
+ if i < len(mem.name):
+ _tag.chname[i] = ord(mem.name[i])
+ else:
+ _tag.chname[i] = 0xff
+ # mem.extra: Clock Type A/B
+ mem.extra = RadioSettingGroup("extra", "Extra")
+ options = ["Auto", "On"]
+ rx = RadioSettingValueList(options, options[_mem.clktyp])
+ rs = RadioSetting("clktyp", "Clock Shift (CLK.TYP)", rx)
+ mem.extra.append(rs)
+
+ options = ["None", "REV Tone", "PR Freq", "Pager"]
+ tmd = _mem.tmode - 3
+ if _mem.tmode < 4:
+ tmd = 0
+ if _mem.tmode > 6:
+ tmd = 0
+ rx = RadioSettingValueList(options, options[tmd])
+ rs = RadioSetting("spclmode", "Radio Special Tone Mode", rx)
+ mem.extra.append(rs)
+
+ options = []
+ for vx in range(300, 3100, 100):
+ sx = str(vx)
+ options.append(sx)
+ if _mem.prfreq > 30:
+ _mem.prfreq = 30 # Can get loaded wrong...?
+ if _mem.prfreq < 3:
+ _mem.prfreq = 3
+ v0 = _mem.prfreq * 100 # .prfreq is 0x03 - 0x1E (3 -30 decimal)
+ sx = str(v0)
+ rx = RadioSettingValueList(options, sx)
+ rs = RadioSetting("prfreq", "PR (User) Freq Htz", rx)
+ mem.extra.append(rs)
+ return mem
+
+ def set_memory(self, mem):
+ if mem.number < 1000:
+ _mem = self._memobj.main[mem.number - 1]
+ _tag = self._memobj.names[mem.number - 1]
+ else:
+ _mem = self._memobj.pms[mem.number - 1000]
+ _tag = self._memobj.pms_name[mem.number - 1000]
+ if mem.empty: # Chan 1 is immutable for empty
+ _mem.used = 0
+ return mem
+ _mem.used = 1
+ _mem.freq = self._freqncode(mem.freq)
+ _mem.mode = self.MODES.index(mem.mode)
+ _mem.duplex = self.DUPLEX.index(mem.duplex)
+ if _mem.duplex == 4: # split mode
+ _mem.frqtx = self._freqncode(mem.offset)
+ else:
+ _mem.offset = mem.offset / 50000
+ tmd = 0 # no tone
+ sqlx = 0
+ # Re-map Tmode and Cross mode to radio combined
+ # Using MY TMODE list index
+ tx = mem.tmode.strip()
+ cmx = mem.cross_mode.strip()
+ if tx == "Cross":
+ if cmx == "->DTCS":
+ tmd = 3
+ if cmx == "Tone->DTCS":
+ tmd = 8
+ if cmx == "DTCS->Tone":
+ tmd = 9
+ sqlx = 1
+ else:
+ if tx == "Tone":
+ tmd = 1
+ if tx == "TSQL":
+ tmd = 2
+ if tx == "TSQL-R":
+ tmd = 4
+ if tx == "DTCS":
+ tmd = 7
+ _mem.tmode = tmd
+ _mem.rtone = chirp_common.TONES.index(mem.rtone)
+ _mem.step = self.T_STEPS.index(mem.tuning_step)
+ _mem.skip = self.SKIPS.index(mem.skip)
+ _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
+ sx = mem.power
+ if sx is None:
+ sx = self.POWER_LEVELS[1] # Mid
+ _mem.power = self.POWER_LEVELS.index(sx)
+ setattr(self._memobj.setts, "sqlexp1", sqlx)
+ setattr(self._memobj.setts, "sqlexp2", sqlx)
+ tx = self.filter_name(mem.name)
+ for i in range(0, self.NAME_LEN):
+ if i < len(tx):
+ _tag.chname[i] = ord(tx[i])
+ else:
+ _tag.chname[i] = 0xff
+ for setting in mem.extra:
+ if setting.get_name() == "clktyp":
+ sx = str(setting.value)
+ vx = 0
+ if sx == "On":
+ vx = 1
+ setattr(_mem, "clktyp", vx)
+ if setting.get_name() == "spclmode":
+ sx = str(setting.value)
+ tmd = 0
+ tx = mem.tmode
+ if sx == "None":
+ if tx == "PR Freq" or tx == "Pager":
+ tmd = 0 # Reset from special to none
+ else:
+ tmd = _mem.tmode # no change
+ if sx == "PR Freq":
+ tmd = 5
+ if sx == "Pager":
+ tmd = 6
+ setattr(_mem, "tmode", tmd)
+ if setting.get_name() == "prfreq":
+ sx = str(setting.value)
+ vx = int(sx) // 100
+ setattr(_mem, "prfreq", vx)
+ return
+
+ def _micpkey(self, setting, obj, atrb, opts):
+ """Callback: Adjust stored value for microphone P keys"""
+ sx = str(setting.value) # the list string
+ v0 = opts.index(sx) # the options array index, 0-based
+ v1 = v0 + 0xdc
+ if self.FTM200 is False:
+ v1 = v0 + 0xdd
+ if v1 == 0xe6:
+ v1 = 0xe7 # skip e6
+ setattr(obj, atrb, v1)
+ return
+
+ def _setfunc(self, setting, obj, atrb, ndx):
+ """Callback: Convert boolean to function setting"""
+ # Stored in shared memory 'share1' as 2 bytes.
+ # When set: first byte is menu number, 2nd is 00
+ # Unset: ff, ff
+ bx = bool(setting.value) # boolean
+ # ndx is 0-based menu number
+ x1 = ndx * 2 # _shr index of first byte
+ v0 = 0xff # disabled
+ v1 = 0xff
+ if bx:
+ v0 = ndx
+ v1 = 0
+ setattr(obj[x1], atrb, v0)
+ setattr(obj[x1 + 1], atrb, v1)
+ return
+
+ def _setQA(self, setting, obj, atrb, opts):
+ """Callback: FTM-200 menu list selection to menu code"""
+ sx = str(setting.value)
+ v0 = opts.index(sx)
+ setattr(obj, atrb, v0)
+ return
+
+ def _sqlexp1(self, setting, obj, atrb):
+ """ Callback: SQL.EXP requires setting two objects """
+ sx = str(setting.value)
+ bx = False
+ if sx == "On":
+ bx = True
+ setattr(obj, atrb, bx) # sqlexp1
+ setattr(obj, "sqlexp2", bx)
+ return
+
+ def _settot(self, setting, obj, atrb, opts):
+ """Callback: convert non-linear TOT values"""
+ sx = str(setting.value)
+ v0 = opts.index(sx)
+ v1 = v0
+ if v0 > 0:
+ v1 = v0 + 5
+ if v0 > 3:
+ v1 = v0 - 3
+ setattr(obj, atrb, v1)
+ return
+
+ def _adjint(self, setting, obj, atrb):
+ """Callback: decrement integer value to 0-based"""
+ v0 = int(setting.value)
+ v1 = v0 - 1
+ setattr(obj, atrb, v1)
+ return
+
+ def _unpack_str(self, codestr):
+ """Convert u8 DTMF array to a string: NOT a callback."""
+ sx = ""
+ for i in range(0, 16): # unpack up to ff
+ if codestr[i] != 0xff:
+ if codestr[i] == 0x0E:
+ sx += "*"
+ elif codestr[i] == 0x0F:
+ sx += "#"
+ else:
+ sx += format(int(codestr[i]), '0X')
+ return sx
+
+ def _pack_chars(self, setting, obj, atrb, ndx):
+ """Callback to build 0-9,A-D,*# nibble array from string"""
+ # String will be ff padded to 16 bytes
+ # Chars are stored as hex values
+ ary = []
+ sx = str(setting.value).upper().strip()
+ sx = sx.strip() # trim spaces
+ # Remove illegal characters first
+ sty = ""
+ for j in range(0, len(sx)):
+ if sx[j] in self.DTMF_CHARS:
+ sty += sx[j]
+ for j in range(0, 16):
+ if j < len(sty):
+ if sty[j] == "*":
+ chrv = 0xE
+ elif sty[j] == "#":
+ chrv = 0xF
+ else:
+ chrv = int(sty[j], 16)
+ else: # pad to 16 bytes
+ chrv = 0xFF
+ ary.append(chrv) # append byte
+ setattr(obj[ndx], atrb, ary)
+ return
+
+ def _pmgset(self, setting, obj, ndx):
+ """Callback: Convert pmg chan to 0-based, and store """
+ v0 = int(setting.value)
+ if v0 == 0: # Deleted
+ setattr(obj[ndx], "za", 0xff)
+ setattr(obj[ndx], "fb", 0xff)
+ setattr(obj[ndx], "chnp", 0)
+ return
+ v1 = v0 - 1 # 0-based
+ setattr(obj[ndx], "za", 0)
+ setattr(obj[ndx], "fb", 0xff)
+ setattr(obj[ndx], "chnp", v1)
+ return
+
+ def _adjlist(self, setting, obj, atrb, opts, ofst):
+ """Callback: Universal add/subtract list index"""
+ sx = str(setting.value) # the list string
+ vx = opts.index(sx) + ofst
+ if atrb == "tzone": # special case for time zone
+ if vx < 0:
+ vx = abs(vx) + 0x80
+ setattr(obj, atrb, vx)
+ return
+
+ def _ft2apo(self, setting, obj):
+ """Callback: Set FTM-200 APO coded decimal in 2 bytes"""
+ sx = str(setting.value) # Ex '1.5 Hours'
+ if sx == "Off":
+ setattr(obj, "apo", 0)
+ setattr(obj, "apo1", 0)
+ return
+ if "." in sx:
+ vx = sx.index(".")
+ v1 = int("0x" + sx[:vx], 16) # encode as hcd
+ v2 = 1
+ else:
+ vx = sx.index(" H")
+ v1 = int(sx[:vx])
+ v2 = 0
+ setattr(obj, "apo", v1)
+ setattr(obj, "apo1", v2)
+ return
+
+ def _ft2lcd(self, setting, obj, atrb):
+ """Callback: to set LCD Display Brightness"""
+ sx = str(setting.value)
+ v0 = 3
+ if sx == "Mid":
+ v0 = 1
+ if sx == "Full":
+ v0 = 2
+ setattr(obj, atrb, v0)
+ return
+
+ def _scndrm(self, setting, obj):
+ """Callback:FTM-200 Scan Dual Rcv Mode """
+ # Requires setting a bit in 2 bytes
+ sx = str(setting.value)
+ v0 = 0
+ v1 = 0
+ if "Pri" in sx:
+ v0 = 1
+ if "A-B" in sx:
+ v1 = 1
+ setattr(obj, "scndrm1", v0)
+ setattr(obj, "scndrm2", v1)
+ return
+
+ def _skp_other(self, setting, obj, bnd, opts):
+ """Callback: FTM-200 Band Skip Other requires 2 bits"""
+ sx = str(setting.value) # Off/On
+ v0 = 0
+ if "On" in sx:
+ v0 = 1
+ b0 = "bskpa0"
+ b3 = "bskpa3"
+ if "B" in bnd:
+ b0 = "bskpb0"
+ b3 = "bskpb3"
+ setattr(obj, b0, v0)
+ setattr(obj, b3, v0)
+ return
+
+ def _beepset(self, setting, obj, optns):
+ """Callback: Stupid split-beep"""
+ sx = str(setting.value)
+ vx = optns.index(sx) # 0,1,2
+ v0 = vx & 1 # Off/Low
+ v1 = 0 # High
+ if vx == 2:
+ v0 = 1
+ v1 = 1
+ setattr(obj, "beep1", v0)
+ setattr(obj, "beep2", v1)
+ return
+
+ def _xmsg(self, setting, obj, indx, xmp, mode=0):
+ """Callback: Wires-X message # indx, text encoder """
+ # mode=0 is GM Message, mode=1 is Category
+ sx = str(setting.value).strip()
+ tx = b""
+ v0 = 128
+ v1 = 0xff
+ hx = "msg%02d" % indx
+ if mode == 1:
+ v0 = 16
+ v1 = 0xca
+ hx = "c%d" % indx
+ for cx in range(0, v0):
+ if cx < len(sx):
+ vx = sx[cx]
+ try:
+ v2 = xmp.index(vx)
+ except Exception:
+ raise Exception("Unable to decode character hex %x at "
+ "index %d of %s" % (vx, cx, hx))
+ tx += util.int_to_byte(v2)
+ else:
+ tx += util.int_to_byte(v1) # pad
+ setattr(obj, hx, tx)
+ return
+
+ def _wrxfrq(self, setting, obj, atrb):
+ """Callback: Encode the 3-byte non-mem channel frequency"""
+ vx = float(setting.value) * 1000000
+ bx = self._freqncode(vx)
+ setattr(obj, atrb, bx)
+ return
+
+ def _aprsfx(self, setting, obj, atrb, pad):
+ """Callback: Trap the APRS callsign SSID"""
+ v0 = int(setting.value)
+ vx = v0
+ if (pad == 0xca) and (v0 == 0):
+ vx = pad
+ if (pad == 0x2a) and (v0 == 16):
+ vx = pad
+ setattr(obj, atrb, vx)
+ return
+
+ def _aprspop(self, setting, obj, atrb):
+ """Callback: APRS Beacon and Message Popup times; not 1:1"""
+ # Can these radiso get any wierder?
+ vx = int(setting.value)
+ v0 = 0 # Off
+ if vx == 1:
+ v0 = 3
+ if vx == 2:
+ v0 = 5
+ if vx == 3:
+ v0 = 10
+ if vx == 4:
+ v0 = 0xff # Hold
+ setattr(obj, atrb, v0)
+ return
+
+ def _enchcd(self, setting, obj, atrb):
+ """Callback: Generic convert 1-byte decimal value to hex-coded"""
+ sx = str(setting.value) # Decimal value, Ex: "33"
+ v0 = int("0x" + sx, 16) # encode as hcd 0x33
+ setattr(obj, atrb, v0)
+ return
+
+ def _mdot(self, setting, obj, atrb1, atrb2):
+ """Callback: Convert lat/long mm/mm to u8 and u16"""
+ # Half as hex coded decimal, half as binary !!??
+ vx = float(setting.value) # mm.mm
+ tx = math.modf(vx)
+ sx = str(int(tx[1]))
+ v0 = int("0x" + sx, 16) # hcd
+ v1 = int(tx[0] * 1000) # 2-byte binary
+ setattr(obj, atrb1, v0)
+ setattr(obj, atrb2, v1)
+ return
+
+ def _getdgpath(self, pn):
+ """Method for extracting Digipeater route paths"""
+ # pn is path number 0 - 5
+ _shr = self._memobj.share1
+ sx = ""
+ x1 = pn * 8
+ for i in range(x1, x1 + 6): # route name: 6 bytes
+ v0 = _shr[i].shbyt
+ if v0 != 0xca:
+ sx += chr(v0)
+ vx = _shr[x1 + 6].shbyt # route suffix 0-15
+ if vx == 0xca:
+ vx = 0
+ return sx, vx
+
+ def _putdgpname(self, setting, pn):
+ """Callback: store Digipath name"""
+ # Stored in 'share1' shared memory
+ # pn is path number 0-5
+ _shr = self._memobj.share1
+ sx = str(setting.value) # modified path name; 0-6 chars
+ sx = sx.upper().strip()
+ x1 = pn * 8 # starting position in _shr
+ sp = 0
+ for i in range(x1, x1 + 6):
+ if sp >= len(sx):
+ v1 = 0xca
+ else:
+ v1 = ord(sx[sp])
+ setattr(_shr[i], "shbyt", v1)
+ sp += 1
+ return
+
+ def _putdgpsfx(self, setting, pn):
+ """Callback: Digipath, Ringer SSID"""
+ _shr = self._memobj.share1
+ v1 = int(setting.value)
+ x1 = pn * 8 + 6
+ setattr(_shr[x1], "shbyt", v1)
+ return
+
+ def _rnglmt(self, setting, obj, atrb):
+ """Callback: APRS Beacon Range Limits"""
+ v1 = int(setting.value) # index
+ vx = v1 # for 0, 1
+ if atrb == "aprbrrr": # Range Ringer
+ if v1 == 2:
+ vx = 5
+ if v1 == 3:
+ vx = 10
+ if v1 == 4:
+ vx = 50
+ if v1 == 5:
+ vx = 100
+ else: # Filter Range
+ if v1 == 2:
+ vx = 10
+ if v1 == 3:
+ vx = 100
+ if v1 == 4:
+ vx = 1000
+ if v1 == 5:
+ vx = 3000
+ setattr(obj, atrb, vx)
+ return
+
+ def _datefmt(self, setting, obj, atrb):
+ """Callback: Stupid index jump in date format list"""
+ vx = int(setting.value)
+ if vx == 3:
+ vx = 4
+ setattr(obj, atrb, vx)
+ return
+
+ def get_settings(self):
+ _setx = self._memobj.setts
+ _mic = self._memobj.micset
+ _wierd = self._memobj.wierd
+ _dtm = self._memobj.dtmfcode
+
+ cfg = RadioSettingGroup("cfg", "Config")
+ dsp = RadioSettingGroup("dsp", "Display")
+ fmenu = RadioSettingGroup("fmnu", "Quick Functions")
+ mic = RadioSettingGroup("mic", "Microphone")
+ sig = RadioSettingGroup("sig", "Signalling")
+ opts = RadioSettingGroup("opts", "Options")
+ dtmf = RadioSettingGroup("dtmf", "DTMF")
+ scan = RadioSettingGroup("scan", "Scanning")
+ other = RadioSettingGroup("other", "Other")
+
+ if self.FTM200:
+ dat = RadioSettingGroup("dat", "Data")
+ wires = RadioSettingGroup("wires", "GM/WIRES-X")
+ aprscom = RadioSettingGroup("aprscom", "APRS Settings")
+ aprsdgp = RadioSettingGroup("aprsdgp", "APRS Digipeater")
+ aprsmsg = RadioSettingGroup("aprsmsg", "APRS Messages")
+ bcnfltr = RadioSettingGroup("bcnfltr", "APRS Beacon Filter")
+ bcnunit = RadioSettingGroup("bcnunit", "APRS Beacon Units")
+ bcnrngr = RadioSettingGroup("bcnrngr", "APRS Beacon Ringer")
+ bcnstat = RadioSettingGroup("bcnstat", "APRS Beacon Status")
+ bcnsmrt = RadioSettingGroup("bcnsmrt", "APRS Smart Beaconing")
+ group = RadioSettings(cfg, dsp, fmenu, mic, sig, opts, dtmf,
+ scan, dat, wires, aprscom, aprsmsg,
+ aprsdgp, bcnfltr, bcnunit, bcnrngr,
+ bcnstat, bcnsmrt, other)
+ else:
+ group = RadioSettings(cfg, dsp, fmenu, mic, sig, opts, dtmf,
+ scan, other)
+
+ menu_items = ["01: Auto Power Off (APO)", "02: ARTS Mode",
+ "03: ARTS Interval", "04: Busy Channel Lockout (BCLO)",
+ "05: Beep", "06: Bell", "07: Clock Type",
+ "08: LCD Dimmer", "09: DTMF Manual/Auto",
+ "10: DTMF TX", "11:DTMF Codes", "12: Home",
+ "13: Microphone Gain", "14: Microphone P Keys",
+ "15: Pager TX/RX", "16: Packet Speed", "17:RX Mode",
+ "18: Band Select", "19: Repeater Reverse",
+ "20: Repeater Set", "21: Repeater Other", "22: Scan On",
+ "23: Scan Type", "24: Squelch Type", "25:Squelch Code",
+ "26: Squelch Expansion", "27: Step",
+ "28: Radio Temperature", "29: Time Out Timer (TOT)",
+ "30: TX Power", "31: Version", "32: Voltage",
+ "33: Width", "34: Weather Alert", "35: Bluetooth"]
+ offon = ["Off", "On"]
+ onoff = ["On", "Off"] # Inverted logic
+
+ # Begin Config settings
+ if self.FTM200:
+ sx = self._b2s(_mic.csign, 0xff)
+ rx = RadioSettingValueString(0, 10, sx, False)
+ rs = RadioSetting("micset.csign", "Call Sign Label", rx)
+ rs.set_apply_callback(self._s2b, _mic, "csign", 10)
+ cfg.append(rs)
+
+ options = ["yyyy/mmm/dd", "yyyy/dd/mmm", "mmm/dd/yyyy",
+ "dd/mmm/yyyy"]
+ vx = _setx.datefmt
+ if vx == 4:
+ vx = 3 # Just to make my life difficult!
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("setts.datefmt", "Date Format", rx)
+ rs.set_apply_callback(self._datefmt, _setx, "datefmt")
+ cfg.append(rs)
+
+ options = ["24 Hour", "12 Hour"]
+ rx = RadioSettingValueList(options, options[_setx.timefmt])
+ rs = RadioSetting("setts.timefmt", "Time Format", rx)
+ cfg.append(rs)
+
+ options = []
+ v0 = -14.0
+ for vx in range(0, 57):
+ options.append(str(v0))
+ v0 += 0.5
+ v0 = _setx.tzone
+ v1 = v0 + 28 # positive offset; index 28 is GMT
+ if v0 & 0x80: # negative, before GMT
+ v1 = 28 - (v0 & 0x1f)
+ rx = RadioSettingValueList(options, options[v1])
+ rs = RadioSetting("setts.tzone", "Time Zone GMT +/- hours", rx)
+ rs.set_apply_callback(self._adjlist, _setx, "tzone", options, -28)
+ cfg.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.rpt_ars])
+ rs = RadioSetting("setts.rpt_ars",
+ "A: Repeater Auto Offset Enabled", rx)
+ cfg.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.rpt_arsb])
+ rs = RadioSetting("setts.rpt_arsb",
+ "B: Repeater Auto Offset Enabled", rx)
+ cfg.append(rs)
+
+ options = ["Off", "0.5 Hours", "1 Hours", "1.5 Hours", "2 Hours",
+ "3 Hours", "4 Hours", "5 Hours", "6 Hours", "7 Hours",
+ "8 Hours", "9 Hours", "10 Hours", "11 Hours",
+ "12 Hours"]
+ v0 = _setx.apo # Hours BCD
+ v1 = _setx.apo1 # 0.5 hours yes/no
+ sx = "%d" % v0
+ if v1:
+ sx += ".5"
+ if sx == "0":
+ sx = "Off"
+ else:
+ sx += " Hours"
+ rx = RadioSettingValueList(options, sx.strip())
+ rs = RadioSetting("setts.apo", "Auto Power Off time (APO)", rx)
+ rs.set_apply_callback(self._ft2apo, _setx)
+ cfg.append(rs)
+
+ else: # FTM-6000, different than 200
+ rx = RadioSettingValueList(menu_items, menu_items[_setx.f1key])
+ rs = RadioSetting("setts.f1key", "F1 Key Assignment", rx)
+ cfg.append(rs)
+
+ options = ["Off", "1 Hours", "1.5 Hours", "2 Hours", "3 Hours",
+ "4 Hours", "5 Hours", "6 Hours", "7 Hours", "8 Hours",
+ "9 Hours", "10 Hours", "11 Hours", "12 Hours"]
+ rx = RadioSettingValueList(options, options[_setx.apo])
+ rs = RadioSetting("setts.apo", "Auto Power Off time (APO)", rx)
+ cfg.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.rpt_ars])
+ rs = RadioSetting("setts.rpt_ars", "Repeater Auto Offset Enabled",
+ rx)
+ cfg.append(rs)
+
+ # Config - Common to all radios
+
+ options = ["Off", "Low", "High"]
+ # Yaesu splits the beep!!!
+ vx = _setx.beep1 # Off/Low
+ if _setx.beep2:
+ vx = 2 # High
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("setts.beep1", "Beep", rx)
+ rs.set_apply_callback(self._beepset, _setx, options)
+ cfg.append(rs)
+
+ options = ["Off", "1 minute", "2 minutes", "3 minutes", "5 minutes",
+ "10 minutes", "15 minutes", "20 minutes", "30 minutes"]
+ vx = _setx.tot # non-sequential index
+ if vx >= 6:
+ bx = vx - 5
+ if vx >= 1:
+ bx = vx + 3
+ if vx == 0:
+ bx = 0
+ rx = RadioSettingValueList(options, options[bx])
+ rs = RadioSetting("setts.tot", "Transmit Time Out Timer (TOT)", rx)
+ rs.set_apply_callback(self._settot, _setx, "tot", options)
+ cfg.append(rs)
+
+ if self.FTM200: # Stuff at the end of Config
+ options = ["Metric", "Imperial (Inch)"]
+ rx = RadioSettingValueList(options, options[_setx.unit])
+ rs = RadioSetting("setts.unit", "Units", rx)
+ cfg.append(rs)
+
+ options = ["WGS 84", "Tokyo Mean"]
+ rx = RadioSettingValueList(options, options[_setx.gpsdtm])
+ rs = RadioSetting("setts.gpsdtm", "GPS Datum", rx)
+ cfg.append(rs)
+
+ options = ["Internal", "External"]
+ rx = RadioSettingValueList(options, options[_setx.gpsdev])
+ rs = RadioSetting("setts.gpsdev", "GPS Device", rx)
+ cfg.append(rs)
+
+ options = ["Off", "1 second", "2 sec", "5 sec", "10 sec",
+ "30 sec", "60 sec"]
+ rx = RadioSettingValueList(options, options[_setx.gpslog])
+ rs = RadioSetting("setts.gpslog", "GPS Log Interval", rx)
+ cfg.append(rs)
+
+ else:
+ options = ["30 Seconds", "1 Minute"]
+ rx = RadioSettingValueList(options, options[_setx.arts_int])
+ rs = RadioSetting("setts.arts_int", "ARTS Interval", rx)
+ cfg.append(rs)
+
+ options = ["Off", "In Range", "Out Range"]
+ rx = RadioSettingValueList(options, options[_setx.arts_mode])
+ rs = RadioSetting("setts.arts_mode", "ARTS Mode", rx)
+ cfg.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.bclo])
+ rs = RadioSetting("setts.bclo", "Busy Channel Lockout (BCLO)",
+ rx)
+ cfg.append(rs)
+
+ # End of Config settings
+
+ # Start of Display settings
+ if self.FTM200:
+ options = ["Backtrack", "Altitude", "Timer/Clock", "GPS Info"]
+ rx = RadioSettingValueList(options, options[_setx.dspmode])
+ rs = RadioSetting("setts.dspmode", "Display Select", rx)
+ dsp.append(rs)
+
+ options = ["Compass", "Numeric Lat/Long"]
+ rx = RadioSettingValueList(options, options[_setx.locinfo])
+ rs = RadioSetting("setts.locinfo", "Location Info", rx)
+ dsp.append(rs)
+
+ options = ["North Up", "Heading Up"]
+ rx = RadioSettingValueList(options, options[_mic.compass])
+ rs = RadioSetting("micset.compass", "Compass Display", rx)
+ dsp.append(rs)
+
+ options = ["Wide", "Narrow"]
+ rx = RadioSettingValueList(options, options[_setx.bscope])
+ rs = RadioSetting("setts.bscope", "A: Display Scope", rx)
+ dsp.append(rs)
+ rx = RadioSettingValueList(options, options[_setx.bscopeb])
+ rs = RadioSetting("setts.bscopeb", "B: Display Scope", rx)
+ dsp.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.memlist])
+ rs = RadioSetting("setts.memlist", "Memory List Mode", rx)
+ dsp.append(rs)
+
+ options = ["Dim", "Mid", "Full"]
+ vx = _mic.lcd # stupid indexing: 3,1,2
+ v0 = vx # Mid: vx = 1, Max: vx = 2
+ if vx == 3:
+ v0 = 0
+ rx = RadioSettingValueList(options, options[v0])
+ rs = RadioSetting("micset.lcd", "Display Brightness", rx)
+ rs.set_apply_callback(self._ft2lcd, _mic, "lcd")
+ dsp.append(rs)
+
+ options = ["White", "Blue", "Red"]
+ rx = RadioSettingValueList(options, options[_setx.lcd_clr])
+ rs = RadioSetting("setts.lcd_clr", "LCD Upper Band Color", rx)
+ dsp.append(rs)
+
+ else: # FTM-6000
+ options = ["Dim", "Mid", "Full"]
+ rx = RadioSettingValueList(options, options[_mic.lcd])
+ rs = RadioSetting("micset.lcd", "Display Brightness", rx)
+ dsp.append(rs)
+
+ # End of Display settings
+
+ # Start of Signalling settings
+ options = ["Off", "1 Time", "3 Times", "5 Times", "8 Times",
+ "Continous"]
+ if self.FTM200:
+ rx = RadioSettingValueList(options, options[_mic.bell])
+ rs = RadioSetting("micset.bell",
+ "A: Remote Station Calling Bell", rx)
+ sig.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.bellb])
+ rs = RadioSetting("micset.bellb",
+ "B: Remote Station Calling Bell", rx)
+ sig.append(rs)
+ else:
+ rx = RadioSettingValueList(options, options[_mic.bell])
+ rs = RadioSetting("micset.bell", "Remote Station Calling Bell",
+ rx)
+ sig.append(rs)
+
+ # All radios, Signalling opetions
+ vx = _mic.pgrcdr1 + 1
+ rx = RadioSettingValueInteger(1, 50, vx)
+ rs = RadioSetting("micset.pcrcdr1", "Pager Receive First Code", rx)
+ rs.set_apply_callback(self._adjint, _mic, "pgrcdr1")
+ sig.append(rs)
+
+ vx = _mic.pgrcdr2 + 1
+ rx = RadioSettingValueInteger(1, 50, vx)
+ rs = RadioSetting("micset.pcrcdr2", "Pager Receive Second Code", rx)
+ rs.set_apply_callback(self._adjint, _mic, "pgrcdr2")
+ sig.append(rs)
+
+ rx = RadioSettingValueInteger(1, 50, vx)
+ rs = RadioSetting("micset.pgrcdt1", "Pager Transmit First Code", rx)
+ rs.set_apply_callback(self._adjint, _mic, "pgrcdt1")
+ sig.append(rs)
+
+ vx = _mic.pgrcdt2 + 1
+ rx = RadioSettingValueInteger(1, 50, vx)
+ rs = RadioSetting("micset.pgrcdt2", "Pager Transmit Second Code", rx)
+ rs.set_apply_callback(self._adjint, _mic, "pgrcdt2")
+ sig.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.wxalrt])
+ rs = RadioSetting("setts.wxalrt", "Weather Alert Enabled", rx)
+ sig.append(rs)
+
+ # End of Signalling settings
+
+ # Begin fmenu settings For each entry in menu_Items
+ if self.FTM200: # 124 menu options possible
+ options = ["1:Freq Input", "2:LCD Brightness", "3:Freq Color",
+ "4:Band Scope", "5:Location Info", "6:Compass",
+ "7:Display Mode"]
+ more = ("8:TX Power", "9:AMS TX Mode", "10:Mic Gain", "11:VOX",
+ "12:Auto Dialer", "13:Time Out Timer (TOT)",
+ "14:Digital VW")
+ options.extend(more)
+ more = ("15:FM Bandwidth", "16:RX Mode", "17:Home",
+ "18:Memory List", "19:Memory List Mode", "20:PMG Clear")
+ options.extend(more)
+ more = ("21:Beep", "22:Band Skip", "23:RPT ARS", "24:RPT Shift",
+ "25:RPT Shift Freq", "26:Rpt Reverse", "27:Mic P Key",
+ "28:Date & Time Adjust", "29:Date &Time Format",
+ "30:Time Zone", "31:Step", "32:Clock Type", "33:Unit",
+ "34:Auto Power Off (APO)", "35:GPS Datum",
+ "36:GPS Device", "37:GPS Log")
+ options.extend(more)
+ more = ("38:Audio Recording", "39:Audio Rec Stop")
+ options.extend(more)
+ more = ("40:DTMF Mode", "41:DTMF Memory", "42:SQL Type",
+ "43:Tone Freq / DCS Code", "44:SQL Expansion",
+ "45:Pager Code", "46:PR Freq", "47:Bell Ringer",
+ "48:WX Alert")
+ options.extend(more)
+ more = ("49:Scan", "50:Dual Rcv Mode", "51:Dual Rx Interval",
+ "52:Priority Revert", "53:Scan Resume")
+ options.extend(more)
+ more = ("54:Digital Popup", "55:Location Service",
+ "56:Standy Beep", "57:GM FP-ID List", "58:Range Ringer",
+ "59:Radio ID", "60:Log List", "61:Rpt / WIRES Freq",
+ "62:Search Step", "63:Edit Category Tag",
+ "64:Delete Room/Mode", "65:WIRES Dg-ID", "66:Com Port",
+ "67:Data Band", "68:Data Speed", "69:Data SQL")
+ options.extend(more)
+ more = ("70:APRS Destination", "71:Filter", "72:Msg Text",
+ "73:APRS On/Off", "74:Mute", "75:Popup", "76:Ringer",
+ "77:Ringer CS", "78:TX Delay", "79:Units",
+ "80:Beacon Info", "81:Beacon Status Txt",
+ "82:Beacon TX Set", "83:DIGI Path", "84:DIGI Path 1",
+ "85:DIGI Path 2", "86:DIGI Path 3", "87:DIGI Path 4",
+ "88:DIGI Path Full 1", "89: DIGI Path Full 2")
+ options.extend(more)
+ more = ("90:APRS Call Sign", "91:Msg Group", "92:Msg Reply",
+ "93:My Position Set", "94:My Position", "95:My Symbol",
+ "96:Position Comment", "97:Smart Beaconing",
+ "98:Sort Filter", "99:Voice alert", "100:Station List",
+ "101:Msg List", "102:Beacon TX Select", "103:Beacon TX")
+ options.extend(more)
+ more = ("104:SD Card Backup", "105:SD Card Mem Info",
+ "106:SD Card Format", "107:Bluetooth", "108:Voice Memory",
+ "109:FVS Rec", "110:Track Select", "111:Play",
+ "112:Stop", "113:Clear", "114:Voice Guide",
+ "115:USB Camera")
+ options.extend(more)
+ more = ("116:This->Other", "117:Other->This", "118:Call Sign",
+ "119:Mem Chn Reset", "120:APRS Reset", "121:Config Set",
+ "122:Config Recall", "123:SW Version",
+ "124:Factory Reset")
+ options.extend(more)
+ rx = RadioSettingValueList(options, options[_mic.fkp1])
+ rs = RadioSetting("micset.fkp1", "Keypad Slot 1", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fkp1", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fkp2])
+ rs = RadioSetting("micset.fkp2", "Keypad Slot 2", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fkp2", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fkp3])
+ rs = RadioSetting("micset.fkp3", "Keypad Slot 3", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fkp3", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fkp4])
+ rs = RadioSetting("micset.fkp4", "Keypad Slot 4", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fkp4", options)
+ fmenu.append(rs)
+
+ rx = RadioSettingValueList(options, options[_mic.fhm1])
+ rs = RadioSetting("micset.fhm1", "Home Slot 1", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fhm1", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fhm2])
+ rs = RadioSetting("micset.fhm2", "Home Slot 2", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fhm2", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fhm3])
+ rs = RadioSetting("micset.fhm3", "Home Slot 3", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fhm3", options)
+ fmenu.append(rs)
+ rx = RadioSettingValueList(options, options[_mic.fhm4])
+ rs = RadioSetting("micset.fhm4", "Home Slot 4", rx)
+ rs.set_apply_callback(self._setQA, _mic, "fhm4", options)
+ fmenu.append(rs)
+ else: # FTM-6000
+ _shr = self._memobj.share1
+ for mnu in range(0, 35):
+ x1 = mnu * 2 # _shr index of first byte
+ vx = _shr[x1].shbyt
+ bx = False
+ if vx != 0xff:
+ bx = True
+ sx = "share1/%d.shbyt" % mnu
+ rx = RadioSettingValueBoolean(bx)
+ rs = RadioSetting(sx, menu_items[mnu], rx)
+ fmenu.append(rs)
+ rs.set_apply_callback(self._setfunc, _shr, "shbyt", mnu)
+ # End fmenu settings
+
+ # Begin microphone settings
+ options = ["Min", "Low", "Normal", "High", "Max"]
+ rx = RadioSettingValueList(options, options[_mic.micgain])
+ rs = RadioSetting("micset.micgain", "Microphone Gain", rx)
+ mic.append(rs)
+
+ options = ["ARTS", "SCAN On", "HOME Recall", "Repeater Duplex",
+ "Repeater Reverse", "Transmit Power", "Squelch Off",
+ "T-Call", "Dual Watch", "Weather Channel"]
+ if self.FTM200:
+ options = ["Off", "Band Scope", "Scan", "Home", "Rpt Shift",
+ "Reverse", "TX Power", "SQL Off", "T-Call", "Voice",
+ "D_X", "WX", "Stn_List", "Msg List", "Reply",
+ "M-Edit"]
+
+ else: # FTM200- P1 is fixed as GM, immutable
+ vx = _mic.micp1 - 0xdd
+ if vx == 10:
+ vx = 9 # stupid index jump
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("micset.micp1", "Microphone Key P1", rx)
+ rs.set_apply_callback(self._micpkey, _mic, "micp1", options)
+ mic.append(rs)
+
+ vx = _mic.micp2 - 0xdd
+ if vx == 10:
+ vx = 9
+ if self.FTM200:
+ vx = _mic.micp2 - 0xdc
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("micset.micp2", "Microphone Key P2", rx)
+ rs.set_apply_callback(self._micpkey, _mic, "micp2", options)
+ mic.append(rs)
+
+ vx = _mic.micp3 - 0xdd
+ if vx == 10:
+ vx = 9
+ if self.FTM200:
+ vx = _mic.micp3 - 0xdc
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("micset.micp3", "Microphone Key P3", rx)
+ rs.set_apply_callback(self._micpkey, _mic, "micp3", options)
+ mic.append(rs)
+
+ vx = _mic.micp4 - 0xdd
+ if vx == 10:
+ vx = 9
+ if self.FTM200:
+ vx = _mic.micp4 - 0xdc
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("micset.micp4", "Microphone Key P4", rx)
+ rs.set_apply_callback(self._micpkey, _mic, "micp4", options)
+ mic.append(rs)
+ # End mic settings
+
+ # Begin DTMF settings
+ options = ["Manual", "Auto"]
+ rx = RadioSettingValueList(options, options[_setx.dtmf_auto])
+ rs = RadioSetting("setts.dtmf_auto", "DTMF Transmit Mode", rx)
+ dtmf.append(rs)
+
+ for kx in range(0, 9):
+ sx = self._unpack_str(_dtm[kx].codes)
+ rx = RadioSettingValueString(0, 16, sx, False)
+ # NOTE the / to indicate indexed array
+ vx = kx + 1
+ rs = RadioSetting("dtmfcode/%d.codes" % kx,
+ "DTMF Code %d" % vx, rx)
+ rs.set_apply_callback(self._pack_chars, _dtm, "codes", kx)
+ dtmf.append(rs)
+ # End of dtmf settings
+
+ # Begin Scan settings
+ if self.FTM200:
+ options = ["Busy", "Hold", "1 Second", "3 Seconds", "5 Seconds"]
+ rx = RadioSettingValueList(options, options[_setx.scnrsm2a])
+ rs = RadioSetting("setts.scnrsm2a", "A: Scan Resume Mode", rx)
+ scan.append(rs)
+ rx = RadioSettingValueList(options, options[_setx.scnrsm2b])
+ rs = RadioSetting("setts.scnrsm2b", "B: Scan Resume Mode", rx)
+ scan.append(rs)
+
+ options = ["0.5 seconds", "1 sec", "2 sec", "3 sec", "5 sec",
+ "7 sec", "10 secs"]
+ rx = RadioSettingValueList(options, options[_setx.scndria])
+ rs = RadioSetting("setts.scndria", "A: Dual Receive Interval",
+ rx)
+ scan.append(rs)
+ rx = RadioSettingValueList(options, options[_setx.scndrib])
+ rs = RadioSetting("setts.scndrib", "B: Dual Receive Interval",
+ rx)
+ scan.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.dwrvt])
+ rs = RadioSetting("setts.dwrvt", "A: Priority Revert", rx)
+ scan.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.dwrvtb])
+ rs = RadioSetting("setts.dwrvtb", "B: Priority Revert", rx)
+ scan.append(rs)
+
+ options = ["Off", "Priority Scan", "A-B Dual Receive"]
+ # unbelievable 2- byte configuration
+ v0 = _setx.scndrm1 # at 17f62 bit 8
+ v1 = _setx.scndrm2 # at 17f6b bit 1
+ vx = 0
+ if v0 == 1:
+ vx = 1
+ elif v1 == 1:
+ vx = 2
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("setts.scndrm1", "Scan Dual Receive Mode", rx)
+ rs.set_apply_callback(self._scndrm, _setx)
+ scan.append(rs)
+
+ else:
+ options = ["Busy", "Hold", "1 Second", "3 Seconds", "5 Seconds"]
+ rx = RadioSettingValueList(options, options[_setx.scnrsm])
+ rs = RadioSetting("setts.scnrsm", "Scan Resume Mode", rx)
+ scan.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.dwrvt])
+ rs = RadioSetting("setts.dwrvt", "Dual Watch Revert", rx)
+ scan.append(rs)
+ # End of Scan settings
+
+ # Begin Data settings
+ if self.FTM200:
+ options = ["4700 bps", "9600 bps", "19200 bps", "38400 bps",
+ "57600 bps"]
+ rx = RadioSettingValueList(options, options[_wierd.comspd])
+ rs = RadioSetting("wierd.comspd", "COM Port Speed", rx)
+ dat.append(rs)
+
+ options = ["Off", "GPS Out", "Packet", "Waypoint"]
+ rx = RadioSettingValueList(options, options[_wierd.comout])
+ rs = RadioSetting("wierd.comout", "COM Port Output", rx)
+ dat.append(rs)
+
+ options = ["NMEA9", "NMEA8", "NMEA7", "NMEA6"]
+ rx = RadioSettingValueList(options, options[_wierd.comwpf])
+ rs = RadioSetting("wierd.comwpf", "COM Port Waypoint Format", rx)
+ dat.append(rs)
+
+ options = ["All", "Mobile", "Frequency", "Object/Item",
+ "Digipeater", "VoIP", "Weather", "Yaesu",
+ "Call Ringer", "Rng Ringer"]
+ rx = RadioSettingValueList(options, options[_wierd.comflt])
+ rs = RadioSetting("wierd.comflt", "COM Port Waypoint Filter", rx)
+ dat.append(rs)
+
+ options = ["Main Band", "Sub Band", "A-Band Fix", "B-Band Fix"]
+ rx = RadioSettingValueList(options, options[_wierd.dbsela])
+ rs = RadioSetting("wierd.dbsela", "Data Band Select: APRS", rx)
+ dat.append(rs)
+ rx = RadioSettingValueList(options, options[_wierd.dbseld])
+ rs = RadioSetting("wierd.dbseld", "Data Band Select: Data", rx)
+ dat.append(rs)
+
+ options = ["1200 bps", "9600 bps"]
+ rx = RadioSettingValueList(options, options[_wierd.dspda])
+ rs = RadioSetting("wierd.dspda", "Data Speed: APRS", rx)
+ dat.append(rs)
+ rx = RadioSettingValueList(options, options[_wierd.pktspd])
+ rs = RadioSetting("wierd.pktspd", "Data Speed: Data", rx)
+ dat.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.datsql])
+ rs = RadioSetting("wierd.datsql", "Data Squelch", rx)
+ dat.append(rs)
+ # End of Data Settings
+
+ # Begin Options settings
+ if self.FTM200:
+ options = ["FREE 5 min", "LAST 30 sec"]
+ rx = RadioSettingValueList(options, options[_setx.fvsrec])
+ rs = RadioSetting("setts.fvsrec", "FVS-2: Play/Record", rx)
+ opts.append(rs)
+
+ options = ["Off", "Manual", "Auto"]
+ rx = RadioSettingValueList(options, options[_setx.fvsanc])
+ rs = RadioSetting("setts.fvsanc", "FVS-2: Announce Mode", rx)
+ opts.append(rs)
+
+ options = ["English", "Japanese"]
+ rx = RadioSettingValueList(options, options[_setx.fvslan])
+ rs = RadioSetting("setts.fvslan", "FVS-2: Language", rx)
+ opts.append(rs)
+
+ options = ["Low", "Mid", "High"]
+ rx = RadioSettingValueList(options, options[_setx.fvsvol])
+ rs = RadioSetting("setts.fvsvol", "FVS-2: Volume", rx)
+ opts.append(rs)
+
+ # Yaesu split the FTM-200 BEEP 0/1/2 into 2 byte locations!!
+ # and assigned this bit to the upper Beep of the FTM-6000!!
+ rx = RadioSettingValueList(onoff, onoff[_setx.fvsrxm])
+ rs = RadioSetting("setts.fvsrxm", "FVS-2: RX Mute", rx)
+ opts.append(rs)
+
+ # Common Options - BLUETOOTH
+ rx = RadioSettingValueList(offon, offon[_setx.bton])
+ rs = RadioSetting("setts.bton", "Bluetooth: Enabled", rx)
+ opts.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.btsave])
+ rs = RadioSetting("setts.btsave", "Bluetooth: Save", rx)
+ opts.append(rs)
+
+ options = ["Auto", "Fix"]
+ rx = RadioSettingValueList(options, options[_setx.btaud])
+ rs = RadioSetting("setts.btaud", "Bluetooth: Audio", rx)
+ opts.append(rs)
+
+ if self.FTM200:
+ options = ["320 x 240", "160 x 120"]
+ rx = RadioSettingValueList(options, options[_mic.usbcamsz])
+ rs = RadioSetting("micset.usbcamsz", "USB Camera: Image Size", rx)
+ opts.append(rs)
+
+ options = ["Low", "Normal", "High"]
+ rx = RadioSettingValueList(options, options[_mic.usbcamql])
+ rs = RadioSetting("micset.usbcamql", "USB Camera: Image Quality",
+ rx)
+ opts.append(rs)
+
+ # End of Options settings
+
+ # Begin Other settings
+
+ if self.FTM200:
+ options = ["A", "B", "A+B"]
+ rx = RadioSettingValueList(options, options[_setx.audrec - 1])
+ rs = RadioSetting("setts.audrec", "Audio Recording Band", rx)
+ rs.set_apply_callback(self._adjlist, _setx, "audrec", options,
+ 1)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.audmic])
+ rs = RadioSetting("setts.audmic", "Microphone", rx)
+ other.append(rs)
+
+ options = ["Off", "Low", "High"]
+ rx = RadioSettingValueList(options, options[_setx.voxsen])
+ rs = RadioSetting("setts.voxsen", "VOX Sensitivity", rx)
+ other.append(rs)
+ options = ["0.5 seconds", "1 sec", "1.5 sec", "2.0 sec",
+ "2.5sec", "3 sec"]
+ rx = RadioSettingValueList(options, options[_setx.voxdly])
+ rs = RadioSetting("setts.voxdly", "VOX Delay", rx)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.bskpa1])
+ rs = RadioSetting("setts.bskpa1", "A: Air Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpa2])
+ rs = RadioSetting("setts.bskpa2", "A: VHF Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpa4])
+ rs = RadioSetting("setts.bskpa4", "A: UHF Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpa3])
+ rs = RadioSetting("setts.bskpa3", "A: Other Band Enabled", rx)
+ rs.set_apply_callback(self._skp_other, _setx, "A", options)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.bskpb1])
+ rs = RadioSetting("setts.bskpb1", "B: Air Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpb2])
+ rs = RadioSetting("setts.bskpb2", "B: VHF Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpb4])
+ rs = RadioSetting("setts.bskpb4", "B: UHF Band Enabled", rx)
+ other.append(rs)
+ rx = RadioSettingValueList(offon, offon[_setx.bskpb3])
+ rs = RadioSetting("setts.bskpb3", "B: Other Band Enabled", rx)
+ rs.set_apply_callback(self._skp_other, _setx, "B", options)
+ other.append(rs)
+
+ else:
+ options = ["1200 bps", "9600 bps"]
+ rx = RadioSettingValueList(options, options[_wierd.pktspd])
+ rs = RadioSetting("wierd.pktspd", "Data Port Packet Baud Rate",
+ rx)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.airb])
+ rs = RadioSetting("setts.airb", "Air Band Enabled", rx)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.vhfb])
+ rs = RadioSetting("setts.vhfb", "VHF Band Enabled", rx)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.uhfb])
+ rs = RadioSetting("setts.uhfb", "UHF Band Enabled", rx)
+ other.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.otrb])
+ rs = RadioSetting("setts.otrb", "Other Band Enabled", rx)
+ other.append(rs)
+ # End of Other Settings
+
+ # Start FTM200 Unique settings
+ if self.FTM200:
+ _wrx = self._memobj.wrxmsg
+ xmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop"
+ xmap += "qrstuvwxyz!\"#$%&\\()*+,-./:;<=>?@[']^_`{|}~****** "
+ # NOTE: Unknown characters at index 94-99; replaced with *
+ for ix in range(1, 11):
+ atr = "msg%02d" % ix
+ sx = getattr(_wrx, atr)
+ tx = ""
+ for cx in sx:
+ if cx == 0xff:
+ break
+ tx += xmap[cx]
+ tx = tx.strip()
+ rx = RadioSettingValueString(0, 128, tx, False)
+ atr = "wrxmsg.msg%02d" % ix
+ rs = RadioSetting(atr, "Message M%02d" % ix, rx)
+ rs.set_apply_callback(self._xmsg, _wrx, ix, xmap)
+ wires.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.wrxrng])
+ rs = RadioSetting("setts.wrxrng", "Range Ringer", rx)
+ wires.append(rs)
+
+ options = ["Auto", "TX FM Fixed", "TX DN Fixed"]
+ rx = RadioSettingValueList(options, options[_setx.wrxams])
+ rs = RadioSetting("setts.wrxams", "AMS TX Mode", rx)
+ wires.append(rs)
+
+ options = ["Off", "2 seconds", "4 sec", "6 sec", "8 sec",
+ "10 sec", "20 sec", "30 sec", "60 sec", "Continuous"]
+ rx = RadioSettingValueList(options, options[_mic.wrxpop])
+ rs = RadioSetting("micset.wrxpop", "Digital Popup", rx)
+ wires.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.wrxloc])
+ rs = RadioSetting("setts.wrxloc", "Location Service", rx)
+ wires.append(rs)
+
+ rx = RadioSettingValueList(onoff, onoff[_setx.wrxstb])
+ rs = RadioSetting("setts.wrxstb", "Standby Beep", rx)
+ wires.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_setx.wrxdvw])
+ rs = RadioSetting("setts.wrxdvw", "Digital VW Enable", rx)
+ wires.append(rs)
+
+ options = ["Manual", "Preset"]
+ rx = RadioSettingValueList(options, options[_setx.wrxrwf])
+ rs = RadioSetting("setts.wrxrwf", "RPT/WIRES Frequency Mode", rx)
+ wires.append(rs)
+
+ options = ["VHF Preset", "UHF Preset"]
+ rx = RadioSettingValueList(options, options[_mic.wrxbsel])
+ rs = RadioSetting("micset.wrxbsel", "RPT/WIRES Preset Band Select",
+ rx)
+ wires.append(rs)
+
+ vx = self._freqdcode(_mic.wrxvfrq) / 1000000
+ rx = RadioSettingValueFloat(144.0, 149.0, vx, 0.001, 3)
+ rs = RadioSetting("micset.wrxvfrq", "RPT/WIRES VHF Preset Freq",
+ rx)
+ rs.set_apply_callback(self._wrxfrq, _mic, "wrxvfrq")
+ wires.append(rs)
+
+ vx = self._freqdcode(_mic.wrxufrq) / 1000000
+ rx = RadioSettingValueFloat(420.0, 460.0, vx, 0.001, 3)
+ rs = RadioSetting("micset.wrxufrq", "RPT/WIRES UHF Preset Freq",
+ rx)
+ rs.set_apply_callback(self._wrxfrq, _mic, "wrxufrq")
+ wires.append(rs)
+
+ options = ["History", "Activity"]
+ rx = RadioSettingValueList(options, options[_setx.wrxsrch])
+ rs = RadioSetting("setts.wrxsrch", "RPT/WIRES Search Setup", rx)
+ wires.append(rs)
+
+ options = ["Auto"]
+ for vx in range(1, 100):
+ options.append("%02d" % vx)
+ rx = RadioSettingValueList(options, options[_mic.wrxdid])
+ rs = RadioSetting("micset.wrxdid", "RPT/WIRES Radio Digital ID",
+ rx)
+ wires.append(rs)
+
+ _wrxc = self._memobj.wrxcat
+ for ix in range(1, 6):
+ atr = "c%d" % ix
+ sx = getattr(_wrxc, atr)
+ tx = ""
+ for cx in sx:
+ if cx == 0xca:
+ break
+ tx += xmap[cx]
+ tx = tx.strip()
+ rx = RadioSettingValueString(0, 16, tx, False)
+ atr = "wrxcat.c%d" % ix
+ rs = RadioSetting(atr, "Category Text C%d" % ix, rx)
+ rs.set_apply_callback(self._xmsg, _wrxc, ix, xmap, 1)
+ wires.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprson])
+ rs = RadioSetting("wierd.aprson", "APRS Enabled", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprmut])
+ rs = RadioSetting("wierd.aprmut", "Mute", rx)
+ aprscom.append(rs)
+
+ options = ["100ms", "150ms", "200ms", "250ms", "300ms", "400ms",
+ "500ms", "750ms", "1000ms"]
+ rx = RadioSettingValueList(options, options[_wierd.aprtxd])
+ rs = RadioSetting("wierd.aprtxd", "TX Delay", rx)
+ aprscom.append(rs)
+
+ options = ["Off", "1 digit", "2 digits", "3 digits", "4 digits"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbamb])
+ rs = RadioSetting("wierd.aprbamb", "Beacon Ambiguity", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprspdc])
+ rs = RadioSetting("wierd.aprspdc", "Beacon Speed/Course", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprsalt])
+ rs = RadioSetting("wierd.aprsalt", "Beacon Altitude", rx)
+ aprscom.append(rs)
+
+ options = ["Off", "On", "Smart"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbaut])
+ rs = RadioSetting("wierd.aprbaut", "Beacon TX Mode", rx)
+ aprscom.append(rs)
+
+ options = ["30 sec", "1 minute", "2 mins", "3 mins", "5 mins",
+ "10 mins", "15 mins", "20 mins", "30 mins", "60 mins"]
+ rx = RadioSettingValueList(options, options[_wierd.aprstxi])
+ rs = RadioSetting("wierd.aprstxi", "Beacon TX Interval", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprspro])
+ rs = RadioSetting("wierd.aprspro", "Beacon TX Proportional", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprsdcy])
+ rs = RadioSetting("wierd.aprsdcy", "Beacon TX Decay", rx)
+ aprscom.append(rs)
+
+ # Lets try using integer values instead of list; just for kicks
+ rx = RadioSettingValueInteger(1, 99, _wierd.aprslow)
+ rs = RadioSetting("wierd.aprslow", "TX Low Speed (mph)", rx)
+ aprscom.append(rs)
+
+ rx = RadioSettingValueInteger(5, 180, _wierd.aprsrate)
+ rs = RadioSetting("wierd.aprsrate", "TX Rate Limit (secs)", rx)
+ aprscom.append(rs)
+
+ sx = self._b2s(_wierd.aprcsgn, 0xca)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ rs = RadioSetting("wierd.aprcsgn", "APRS Callsign", rx)
+ rs.set_apply_callback(self._s2b, _wierd, "aprcsgn", 6, 0xca)
+ aprscom.append(rs)
+
+ options = [""]
+ for i in range(1, 16):
+ options.append("-%d" % i)
+ vx = _wierd.aprcsfx
+ if vx == 0xca:
+ vx = 0
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("wierd.aprcsfx", "Call Sign SSID ", rx)
+ rs.set_apply_callback(self._aprsfx, _wierd, "aprcsfx", 0xca)
+ aprscom.append(rs)
+
+ options = ["Off Duty", "En Route", "In Service", "Returning",
+ "Committed", "Special", "Priority", "Sustom 0",
+ "Custom 1", "Custom 2", "Custom 3", "Custom 4",
+ "Custom 5", "Custom 6", "EMERGENCY!"]
+ rx = RadioSettingValueList(options, options[_wierd.aprcmt])
+ rs = RadioSetting("wierd.aprcmt", "Position Comment", rx)
+ aprscom.append(rs)
+
+ options = ["Off", "3 secs", " 5 secs", "10 secs", "HOLD"]
+ vx = _wierd.aprpopb # hex encoded decimal plus ff= hold
+ v1 = 0
+ if vx == 3:
+ v1 = 1
+ if vx == 5:
+ v1 = 2
+ if vx == 10:
+ v1 = 3
+ if vx == 0xff:
+ v1 = 4
+ rx = RadioSettingValueList(options, options[v1])
+ rs = RadioSetting("wierd.aprpopb", "Beacon Popup Display Time",
+ rx)
+ rs.set_apply_callback(self._aprspop, _wierd, "aprpopb")
+ aprscom.append(rs)
+
+ vx = _wierd.aprpopm
+ v1 = 0
+ if vx == 3:
+ v1 = 1
+ if vx == 5:
+ v1 = 2
+ if vx == 10:
+ v1 = 3
+ if vx == 0xff:
+ v1 = 4
+ rx = RadioSettingValueList(options, options[v1])
+ rs = RadioSetting("wierd.aprpopm",
+ "Message Popup Display Time", rx)
+ rs.set_apply_callback(self._aprspop, _wierd, "aprpopm")
+ aprscom.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprmpkt])
+ rs = RadioSetting("wierd.aprmpkt", "My Packet", rx)
+ aprscom.append(rs)
+
+ options = ["Time", "Call Sign", "Distance"]
+ rx = RadioSettingValueList(options, options[_wierd.aprsfs])
+ rs = RadioSetting("wierd.aprsfs", "Sort Filter: Sort By", rx)
+ aprscom.append(rs)
+
+ options = ["All", "Mobile", "Frequency", "Object/Item",
+ "Digipeater", "VoIP", "Weather", "Yaesu",
+ "Other Packet", " Call Ringer", "Range Ringer",
+ "1200 bps", "9600 bps"]
+ rx = RadioSettingValueList(options, options[_wierd.aprsff])
+ rs = RadioSetting("wierd.aprsff", "Sort Filter: Filter Type", rx)
+ aprscom.append(rs)
+
+ options = ["Normal", "Tone-SQL", "DCS", "Rx-TSQL", "Rx-DCS"]
+ rx = RadioSettingValueList(options, options[_wierd.aprvlrt])
+ rs = RadioSetting("wierd.aprvlrt", "Voice Alert: Mode", rx)
+ aprscom.append(rs)
+
+ options = []
+ for vx in chirp_common.TONES:
+ options.append("%.1f" % vx)
+ rx = RadioSettingValueList(options, options[_wierd.aprtsql])
+ rs = RadioSetting("wierd.aprtsql", "Voice Alert: TSQL Tone", rx)
+ aprscom.append(rs)
+
+ options = []
+ for vx in chirp_common.DTCS_CODES:
+ options.append("%03d" % vx)
+ rx = RadioSettingValueList(options, options[_wierd.aprvdcs])
+ rs = RadioSetting("wierd.aprvdcs", "Voice Alert: DCS Code", rx)
+ aprscom.append(rs)
+
+ options = ["GPS", "Manual"]
+ rx = RadioSettingValueList(options, options[_wierd.aprmpos])
+ rs = RadioSetting("wierd.aprmpos", "My Position Mode", rx)
+ aprscom.append(rs)
+
+ optns = ["North", "South"]
+ rx = RadioSettingValueList(optns, optns[_wierd.aprltns])
+ rs = RadioSetting("wierd.aprltns",
+ "My Position Manual Lat N/S", rx)
+ aprscom.append(rs)
+
+ v0 = int("%X" % _wierd.aprlad) # Hex coded Decimal
+ rx = RadioSettingValueInteger(0, 90, v0)
+ rs = RadioSetting("wierd.aprlad",
+ "My Position Manual Lat Degrees", rx)
+ rs.set_apply_callback(self._enchcd, _wierd, "aprlad")
+ aprscom.append(rs)
+
+ v0 = int("%X" % _wierd.aprlam) # Whole Minutes, HCD
+ v1 = _wierd.aprlas
+ v1 = v1 / 1000
+ vx = v0 + v1
+ rx = RadioSettingValueFloat(0, 59.99, vx, 0.01, 2)
+ rs = RadioSetting("wierd.aprlas",
+ "My Position Manual Lat Minutes", rx)
+ rs.set_apply_callback(self._mdot, _wierd, "aprlam", "aprlas")
+ aprscom.append(rs)
+
+ optns = ["East", "West"]
+ rx = RadioSettingValueList(optns, optns[_wierd.aprlgew])
+ rs = RadioSetting("wierd.aprlgew",
+ "My Position Manual Long N/S", rx)
+ aprscom.append(rs)
+
+ v0 = int("%X" % _wierd.aprlgd)
+ rx = RadioSettingValueInteger(0, 180, v0)
+ rs = RadioSetting("wierd.aprlgd",
+ "My Position Manual Long Degrees", rx)
+ rs.set_apply_callback(self._enchcd, _wierd, "aprlgd")
+ aprscom.append(rs)
+
+ v0 = int("%X" % _wierd.aprlgm)
+ v1 = _wierd.aprlgs
+ v1 = v1 / 1000
+ vx = v0 + v1
+ rx = RadioSettingValueFloat(0, 59.99, vx, 0.01, 2)
+ rs = RadioSetting("wierd.aprlgs",
+ "My Position Manual Long Minutes", rx)
+ rs.set_apply_callback(self._mdot, _wierd, "aprlgm", "aprlgs")
+ aprscom.append(rs)
+
+ # APRS Digipeater Settings
+
+ options = ["Off", "Wide 1-1", "Wide 1-1, 1-2", "Path 1",
+ "Path 2", "Path 3", "Path 4", "Full 1", "Full2"]
+ rx = RadioSettingValueList(options, options[_wierd.aprdig])
+ rs = RadioSetting("wierd.aprdig", "Digipeater Route Selection",
+ rx)
+ aprsdgp.append(rs)
+
+ # Using shared memory 'share1'; extracting Digipath data
+ _shr = self._memobj.share1
+ optsfx = [""] # List for path suffix
+ for i in range(1, 16):
+ optsfx.append("-%d" % i)
+ py = 0 # route index 0-7 for 'paths'
+ pz = 0 # index to _shr array for dummy memobj
+ for px in range(0, 4): # Path 1 - 4
+ sx, sfx = self._getdgpath(py)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ tx = "Path-%d First Route Callsign" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpname, py)
+ aprsdgp.append(rs)
+ pz += 1
+ rx = RadioSettingValueList(optsfx, optsfx[sfx])
+ tx = "Path-%d First Route SSID" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpsfx, py)
+ aprsdgp.append(rs)
+ py += 1
+ pz += 1
+ sx, sfx = self._getdgpath(py)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ tx = "Path-%d Second Route Callsign" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpname, py)
+ aprsdgp.append(rs)
+ pz += 1
+ rx = RadioSettingValueList(optsfx, optsfx[sfx])
+ tx = "Path-%d Second Route SSID" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpsfx, py)
+ aprsdgp.append(rs)
+ py += 1
+ pz += 1
+ # Now 8 routes for 'Full1'
+ for px in range(0, 8):
+ sx, sfx = self._getdgpath(py)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ tx = "Full-1 Route %d Callsign" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpname, py)
+ aprsdgp.append(rs)
+ pz += 1
+ rx = RadioSettingValueList(optsfx, optsfx[sfx])
+ tx = "Full-1 Route %d SSID" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpsfx, py)
+ aprsdgp.append(rs)
+ py += 1
+ pz += 1
+ # and 8 more for 'Full2'
+ for px in range(0, 8):
+ sx, sfx = self._getdgpath(py)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ tx = "Full-2 Route %d Callsign" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpname, py)
+ aprsdgp.append(rs)
+ pz += 1
+ rx = RadioSettingValueList(optsfx, optsfx[sfx])
+ tx = "Full-2 Route %d SSID" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpsfx, py)
+ aprsdgp.append(rs)
+ py += 1
+ pz += 1
+
+ # --- APRS Messages =====
+
+ _apmsg = self._memobj.aprsmsg
+ for vx in range(1, 7):
+ tx = "grp%d" % vx
+ bx = getattr(_apmsg, tx)
+ sx = self._b2s(bx, 0xca)
+ qx = "aprsmsg.grp%d" % vx
+ rx = RadioSettingValueString(0, 9, sx, False)
+ rs = RadioSetting(qx, "Message Group %d Text" % vx, rx)
+ rs.set_apply_callback(self._s2b, _apmsg, tx, 9, 0xca)
+ aprsmsg.append(rs)
+
+ for vx in range(1, 4):
+ tx = "blt%d" % vx
+ bx = getattr(_apmsg, tx)
+ sx = self._b2s(bx, 0xca)
+ qx = "aprsmsg.blt%d" % vx
+ rx = RadioSettingValueString(0, 9, sx, False)
+ rs = RadioSetting(qx, "Message Bulletin %d Text" % vx, rx)
+ rs.set_apply_callback(self._s2b, _apmsg, tx, 9, 0xca)
+ aprsmsg.append(rs)
+
+ _aptxt = self._memobj.aprstxt
+ for vx in range(1, 9):
+ tx = "txt%d" % vx
+ bx = getattr(_aptxt, tx)
+ sx = self._b2s(bx, 0xca)
+ qx = "aprstxt.txt%d" % vx
+ rx = RadioSettingValueString(0, 16, sx, False)
+ rs = RadioSetting(qx, "Message %d Text" % vx, rx)
+ rs.set_apply_callback(self._s2b, _aptxt, tx, 16, 0xca)
+ aprsmsg.append(rs)
+
+ options = []
+ for i in range(1, 9):
+ options.append("%d" % i)
+ rx = RadioSettingValueList(options, options[_wierd.aprstxn])
+ rs = RadioSetting("wierd.aprstxn", "Message # Selected", rx)
+ aprsmsg.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprrply])
+ rs = RadioSetting("wierd.aprrply", "Message Reply Enabled", rx)
+ aprsmsg.append(rs)
+
+ sx = self._b2s(_wierd.aprrcsn, 0x2a)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ rs = RadioSetting("wierd.aprrcsn", "Message Reply Callsign", rx)
+ rs.set_apply_callback(self._s2b, _wierd, "aprrcsn", 6, 0x2a)
+ aprsmsg.append(rs)
+
+ options = [" "]
+ for i in range(1, 16):
+ options.append("-%d" % i)
+ options.append("-**") # index 16
+ vx = _wierd.aprrcfx
+ if vx == 0x2a:
+ vx = 16
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("wierd.aprrcfx", "Reply Call Sign SSID", rx)
+ rs.set_apply_callback(self._aprsfx, _wierd, "aprrcfx", 0x2a)
+ aprsmsg.append(rs)
+
+ sx = self._b2s(_wierd.aprrtxt, 0xca)
+ rx = RadioSettingValueString(0, 64, sx, False)
+ rs = RadioSetting("wierd.aprrtxt", "Message Reply Text", rx)
+ rs.set_apply_callback(self._s2b, _wierd, "aprrtxt", 64, 0xca)
+ aprsmsg.append(rs)
+
+ options = ["/#", "/&", "/'", "/-", "/.", "/0", "/:", "/;", "/<",
+ "/=", "/>", "/C", "/E", "/I", "/K", "/O", "/P", "/R",
+ "/T", "/U", "/V", "/W", "/X", "Y", "/[", "/\\", "/^",
+ "/_", "/a", "/b", "/f", "/g", "/j", "/k", "/m", "/r",
+ "/s", "/u", "/v", "/y", "\\#", "\\&", "\\-", "\\.",
+ "\\0", "E0", "IO", "W0", "\\;", "\\>", "\\A", "\\K",
+ "\\W", "\\Y", "KY", "YY", "\\^", "\\_", "\\m", "\\n",
+ "\\s", "\\u", "\\v", "\\x"]
+ for i in range(1, 4):
+ tx = "aprsym%d" % i
+ bx = getattr(_wierd, tx)
+ sx = self._b2s(bx, 0) # 2-byte char string
+ qx = "wierd.aprsym%d" % i
+ rx = RadioSettingValueList(options, sx)
+ rs = RadioSetting(qx, "My Symbol #%d" % i, rx)
+ rs.set_apply_callback(self._s2b, _wierd, tx, 2, 0)
+ aprsmsg.append(rs)
+
+ options = ["/"]
+ for i in range(48, 58):
+ options.append(chr(i)) # numerals
+ for i in range(65, 91):
+ options.append(chr(i)) # Cap letters
+ options.append("\\")
+ sx = chr(_wierd.aprsym4a)
+ rx = RadioSettingValueList(options, sx)
+ rs = RadioSetting("wierd.aprsym4a", "My Symbol #4 First Value",
+ rx)
+ rs.set_apply_callback(self._c2u8, _wierd, "aprsym4a")
+ aprsmsg.append(rs)
+
+ options = []
+ for i in range(33, 127):
+ options.append(chr(i)) # full ascii
+ sx = chr(_wierd.aprsym4b)
+ rx = RadioSettingValueList(options, sx)
+ rs = RadioSetting("wierd.aprsym4b", "My Symbol #4 Second Value",
+ rx)
+ rs.set_apply_callback(self._c2u8, _wierd, "aprsym4b")
+ aprsmsg.append(rs)
+
+ options = ["#1", "#2", "#3", "#4"]
+ rx = RadioSettingValueList(options, options[_wierd.aprsym])
+ rs = RadioSetting("wierd.aprsym", "My Symbol Selected", rx)
+ aprsmsg.append(rs)
+
+ # --- APRS Beacon Settings -----
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfme])
+ rs = RadioSetting("wierd.aprbfme", "Filter: MIC-E", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfpo])
+ rs = RadioSetting("wierd.aprbfpo", "Filter: Position", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfwx])
+ rs = RadioSetting("wierd.aprbfwx", "Filter: Weather", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfob])
+ rs = RadioSetting("wierd.aprbfob", "Filter: Object", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfit])
+ rs = RadioSetting("wierd.aprbfit", "Filter: Item", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfst])
+ rs = RadioSetting("wierd.aprbfst", "Filter: Status", rx)
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfot])
+ rs = RadioSetting("wierd.aprbfot", "Filter: Other", rx)
+ bcnfltr.append(rs)
+
+ options = ["Off", "1", "10", "100", "1000", "3000"]
+ v0 = int(_wierd.aprbfrl) # u16 int: 0,1,10,100,1000,3000
+ if v0 == 0:
+ vx = 0
+ else:
+ vx = options.index(str(v0))
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("wierd.aprbfrl",
+ "Filter: Range Limit (km:miles)", rx)
+ rs.set_apply_callback(self._rnglmt, _wierd, "aprbfrl")
+ bcnfltr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbfan])
+ rs = RadioSetting("wierd.aprbfan", "Filter: AltNet", rx)
+ bcnfltr.append(rs)
+
+ options = ["dd mm ss", "dd mm.mm"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbupo])
+ rs = RadioSetting("wierd.aprbupo", "Units: Position Format", rx)
+ bcnunit.append(rs)
+
+ options = ["kilometers", "miles"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbudi])
+ rs = RadioSetting("wierd.aprbudi", "Units: Distance", rx)
+ bcnunit.append(rs)
+
+ options = ["km/h", "mph", "knots"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbusp])
+ rs = RadioSetting("wierd.aprbusp", "Units: Speed", rx)
+ bcnunit.append(rs)
+
+ options = ["meters", "feet"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbual])
+ rs = RadioSetting("wierd.aprbual", "Units: Altitude", rx)
+ bcnunit.append(rs)
+
+ options = ["hPa", "mb", "mmHg", "inHg"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbubo])
+ rs = RadioSetting("wierd.aprbubo", "Units: Barometric Pressure",
+ rx)
+ bcnunit.append(rs)
+
+ options = ["Celsius", "Farenheit"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbutp])
+ rs = RadioSetting("wierd.aprbutp", "Units: Temperature", rx)
+ bcnunit.append(rs)
+
+ options = ["mm", "inch"]
+ rx = RadioSettingValueList(options, options[_wierd.aprburn])
+ rs = RadioSetting("wierd.aprburn", "Units: Rain", rx)
+ bcnunit.append(rs)
+
+ options = ["m/s", "mph", "knots"]
+ rx = RadioSettingValueList(options, options[_wierd.aprbuwd])
+ rs = RadioSetting("wierd.aprbuwd", "Units: Wind Speed", rx)
+ bcnunit.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrtb])
+ rs = RadioSetting("wierd.aprbrtb", "Ringer: TX Beacon", rx)
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrtm])
+ rs = RadioSetting("wierd.aprbrtm", "Ringer: TX Message", rx)
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrrb])
+ rs = RadioSetting("wierd.aprbrrb", "Ringer: RX Beacon", rx)
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrrm])
+ rs = RadioSetting("wierd.aprbrrm", "Ringer: RX Message", rx)
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrmp])
+ rs = RadioSetting("wierd.aprbrmp", "Ringer: My Packet", rx)
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrcr])
+ rs = RadioSetting("wierd.aprbrcr", "Ringer: Call Ringer", rx)
+ bcnrngr.append(rs)
+
+ options = ["Off", "1", "5", "10", "50", "100"]
+ v0 = int(_wierd.aprbrrr) # u8 int: 0,1,5,10,50,100
+ if v0 == 0:
+ vx = 0
+ else:
+ vx = options.index(str(v0))
+ rx = RadioSettingValueList(options, options[vx])
+ rs = RadioSetting("wierd.aprbrrr", "Ringer: Range Ringer", rx)
+ rs.set_apply_callback(self._rnglmt, _wierd, "aprbrrr")
+ bcnrngr.append(rs)
+
+ rx = RadioSettingValueList(offon, offon[_wierd.aprbrmv])
+ rs = RadioSetting("wierd.aprbrmv", "Ringer: Message Voice", rx)
+ bcnrngr.append(rs)
+
+ # --- APRS Ringer Call Signs and SSID Suffix ---
+ py = 0x18 # Share1 cs index
+ pz = 0x30 # dummy share1 index
+ for px in range(0, 8): # Calls 1-8
+ sx, sfx = self._getdgpath(py)
+ rx = RadioSettingValueString(0, 6, sx, False)
+ tx = "Ringer Callsign %d" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpname, py)
+ bcnrngr.append(rs)
+ pz += 1
+ rx = RadioSettingValueList(optsfx, optsfx[sfx])
+ tx = "Ringer Callsign %d SSID" % (px + 1)
+ rs = RadioSetting("share1/%d.shbyte" % pz, tx, rx)
+ rs.set_apply_callback(self._putdgpsfx, py)
+ bcnrngr.append(rs)
+ py += 1
+ pz += 1
+
+ # --- Beacon Status Text ----
+ _bstat = self._memobj.bcntxt
+ options = ["Off", "Text 1", "Text 2", "Text 3", "Text 4",
+ "Text 5"]
+ rx = RadioSettingValueList(options, options[_wierd.bcnstat])
+ rs = RadioSetting("wierd.bcnstat", "APRS Beacon Text Select", rx)
+ bcnstat.append(rs)
+
+ options = ["1/1", "1/2", "1/3", "1/4", "1/5", "1/6", "1/7", "1/8",
+ "1/2(Freq)", "1/3(Freq)", "1/4(Freq)", "1/5(Freq)",
+ "1/6(Freq)", "1/7(Freq)", "1/8(Freq)"]
+ rx = RadioSettingValueList(options, options[_wierd.bcntxra])
+ rs = RadioSetting("wierd.bcntxra", "Beacon Text Rate", rx)
+ bcnstat.append(rs)
+
+ options = ["None", "Frequency", "Freq & SQL & Shift"]
+
+ for i in range(0, 5):
+ vx = i + 1
+ sx = self._b2s(_bstat[i].msg, 0xca)
+ rx = RadioSettingValueString(0, 60, sx)
+ rs = RadioSetting("bcntxt/%d.msg" % i,
+ "Status Text %d" % vx, rx)
+ rs.set_apply_callback(self._s2b, _bstat[i], "msg", 60, 0xca)
+ bcnstat.append(rs)
+
+ rx = RadioSettingValueList(options, options[_bstat[i].mode])
+ rs = RadioSetting("bcntxt/%d.mode" % i,
+ "Status Text %d Mode" % vx, rx)
+ bcnstat.append(rs)
+
+ # --- Smart Beaconing
+
+ options = ["Off", "Type 1", "Type 2", "Type 3"]
+ rx = RadioSettingValueList(options, options[_wierd.bcnsmart])
+ rs = RadioSetting("wierd.bcnsmart", "Smart Beaconing Status", rx)
+ bcnsmrt.append(rs)
+
+ labls = ["Low Speed (2-30)", "High Speed (3-90)",
+ "Slow Rate (1-100 min)", "Fast Rate (10-180 sec)",
+ "Turn Angle (5-90 deg)", "Turn Slope (1-255)",
+ "Turn Time (5-180 sec)"]
+ minvals = [2, 3, 1, 10, 5, 1, 5]
+ maxvals = [30, 90, 100, 180, 90, 255, 180]
+ for v0 in range(1, 4): # Type
+ sx = "Type %d" % v0
+ for v1 in range(0, 7): # Parameters
+ v2 = getattr(_wierd, "typ%dval%d" % (v0, v1))
+ rx = RadioSettingValueInteger(minvals[v1], maxvals[v1], v2)
+ rs = RadioSetting("wierd.typ%dval%d" % (v0, v1),
+ "%s %s" % (sx, labls[v1]), rx)
+ bcnsmrt.append(rs)
+ # End If FTM-200 Unique settings
+ return group # End get_settings()
+
+ def set_settings(self, settings):
+ # _setx = self._memobj.setts
+ for element in settings:
+ if not isinstance(element, RadioSetting):
+ self.set_settings(element)
+ continue
+ else:
+ try:
+ name = element.get_name()
+ if "." in name:
+ bits = name.split(".")
+ obj = self._memobj
+ for bit in bits[: -1]:
+ if "/" in bit:
+ bit, index = bit.split("/", 1)
+ index = int(index)
+ obj = getattr(obj, bit)[index]
+ else:
+ obj = getattr(obj, bit)
+ setting = bits[-1]
+ else:
+ # obj = _setx
+ obj = self._memobj
+ setting = element.get_name()
+
+ if element.has_apply_callback():
+ LOG.debug("Using apply callback")
+ element.run_apply_callback()
+ elif element.value.get_mutable():
+ LOG.debug("Setting %s = %s" % (setting, element.value))
+ setattr(obj, setting, element.value)
+ except Exception:
+ LOG.debug(element.get_name())
+ raise
+
+
+@directory.register
+class FTM200radio(FTM6000Radio):
+ """Yaesu FTM-200"""
+ FTM200 = True
+ MODEL = "FTM-200"
+ NAME_LEN = 16 # Valid name length
+ CHARSET = list(chirp_common.CHARSET_ASCII)
+ MODES = ["AM", "FM", "NFM", "DN"]
+ NEEDS_COMPAT_SERIAL = False
+ TESTME = False
diff --git a/tests/Python3_Driver_Testing.md b/tests/Python3_Driver_Testing.md
index bf3abf263..76a65f367 100644
--- a/tests/Python3_Driver_Testing.md
+++ b/tests/Python3_Driver_Testing.md
@@ -441,8 +441,10 @@
| Yaesu_FT2D_R | [Implied by Yaesu_FT3D_R](#user-content-Yaesu_FT3D_R) | 30-Dec-2022 | Yes | 0.02% |
| Yaesu_FT2D_Rv2 | [Implied by Yaesu_FT3D_R](#user-content-Yaesu_FT3D_R) | 30-Dec-2022 | Yes | |
| Yaesu_FT3D_R | [@kk7ds](https://github.com/kk7ds) | 30-Dec-2022 | Yes | 0.03% |
+| Yaesu_FTM-200 | [@Old-Phart](https://github.com/Old-Phart) | 16-Apr-2024 | Yes | |
| Yaesu_FTM-3200D_R | | | Yes | 0.02% |
| Yaesu_FTM-350 | [@kk7ds](https://github.com/kk7ds) | 4-Dec-2022 | Yes | 0.02% |
+| Yaesu_FTM-6000 | [@Old-Phart](https://github.com/Old-Phart) | 16-Apr-2024 | Yes | |
| Yaesu_FTM-7250D_R | | | Yes | 0.04% |
| Yaesu_VX-170 | | | Yes | 0.03% |
| Yaesu_VX-2 | [@thomasrussellmurphy](https://github.com/thomasrussellmurphy) | 20-Jan-2023 | Yes | 0.04% |
@@ -457,11 +459,11 @@
| Zastone_ZT-X6 | [Implied by Retevis_RT22](#user-content-Retevis_RT22) | 9-Dec-2022 | Yes | 0.11% |
## Stats
-**Drivers:** 454
+**Drivers:** 456
-**Tested:** 87% (398/56) (93% of usage stats)
+**Tested:** 87% (400/56) (93% of usage stats)
-**Byte clean:** 91% (415/39)
+**Byte clean:** 91% (417/39)
## Meaning of this testing
diff --git a/tests/images/Yaesu_FTM-200.img b/tests/images/Yaesu_FTM-200.img
new file mode 100644
index 000000000..d52457dc4
Binary files /dev/null and b/tests/images/Yaesu_FTM-200.img differ
diff --git a/tests/images/Yaesu_FTM-6000.img b/tests/images/Yaesu_FTM-6000.img
new file mode 100644
index 000000000..318e7d694
Binary files /dev/null and b/tests/images/Yaesu_FTM-6000.img differ
diff --git a/tests/py3_driver_testers.txt b/tests/py3_driver_testers.txt
index e2fbfe20f..f02a2950d 100644
--- a/tests/py3_driver_testers.txt
+++ b/tests/py3_driver_testers.txt
@@ -395,7 +395,9 @@ Yaesu_FT-818ND_US,+Yaesu_FT-817,14-Feb-2019
Yaesu_FT-857_897,+Yaesu_FT-817,14-Feb-2019
Yaesu_FT-857_897_US,+Yaesu_FT-817,14-Feb-2019
Yaesu_FT-90,#10386,22-Feb-2023
+Yaesu_FTM-200,@Old-Phart,16-Apr-2024
Yaesu_FTM-350,@kk7ds,4-Dec-2022
+Yaesu_FTM-6000,@Old-Phart,16-Apr-2024
Yaesu_VX-2,@thomasrussellmurphy,20-Jan-2023
Yaesu_VX-3,@kk7ds,15-Feb-2019
Yaesu_VX-5,*yaesu_clone,12-Dec-2022