diff --git a/chirp/drivers/ftm6000.py b/chirp/drivers/ftm6000.py new file mode 100644 index 000000000..9491c4fc0 --- /dev/null +++ b/chirp/drivers/ftm6000.py @@ -0,0 +1,2921 @@ +# 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' + + +class Ftm6000RAlias(chirp_common.Alias): + Vendor = "Yaesu" + MODEL = "FTM-6000R" + + +@directory.register +class FTM6000Radio(yaesu_clone.YaesuCloneModeRadio): + """Yaesu FTM-6000""" + 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 + ALIASES = [Ftm6000RAlias] + 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 + + +class Ftm200dAlias(chirp_common.Alias): + VENDOR = "Yaesu" + MODEL = "FTM-200D" + + +@directory.register +class FTM200radio(FTM6000Radio): + """Yaesu FTM-200""" + FTM200 = True + MODEL = "FTM-200" + NAME_LEN = 16 # Valid name length + ALIASES = [Ftm200dAlias] + 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 68276b95a..d742804b7 100644 --- a/tests/Python3_Driver_Testing.md +++ b/tests/Python3_Driver_Testing.md @@ -5,7 +5,7 @@ | Abbree_AR-518 | [@KC9HI](https://github.com/KC9HI) | 16-Dec-2022 | Yes | 0.24% | | Abbree_AR-63 | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.03% | | Abbree_AR-730 | [@KC9HI](https://github.com/KC9HI) | 17-Aug-2023 | Yes | | -| Abbree_AR-869 | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | 0.01% | +| Abbree_AR-869 | | | Yes | 0.01% | | Abbree_AR-F5 | [@KC9HI](https://github.com/KC9HI) | 09-Aug-2023 | Yes | | | Alinco_DJ-G7EG | [@HB9UF](https://github.com/HB9UF) | 09-Dec-2022 | Yes | 0.44% | | Alinco_DJ-G7T | [Reported working](https://chirp.danplanet.com/issues/10567) | 09-May-2023 | Yes | | @@ -16,7 +16,7 @@ | Alinco_DR135T | [Implied by Alinco_DR235T](#user-content-Alinco_DR235T) | 12-Dec-2022 | Yes | 0.04% | | Alinco_DR235T | [@kk7ds](https://github.com/kk7ds) | 12-Dec-2022 | Yes | 0.02% | | Alinco_DR435T | [Implied by Alinco_DR235T](#user-content-Alinco_DR235T) | 12-Dec-2022 | Yes | 0.02% | -| Alinco_DR735T | [@jacobcalvert](https://github.com/jacobcalvert) | 03-April-2024 | Yes | | +| Alinco_DR735T | | | Yes | | | AnyTone_5888UV | [@kk7ds](https://github.com/kk7ds) | 9-Dec-2022 | Yes | 0.14% | | AnyTone_5888UVIII | [@bschuler42](https://github.com/bschuler42) | 9-Jan-2023 | Yes | 0.04% | | AnyTone_778UV | [Implied by Retevis_RT95](#user-content-Retevis_RT95) | 13-Nov-2022 | Yes | 0.17% | @@ -46,8 +46,8 @@ | BTECH_UV-50X2_G2 | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.00% | | BTECH_UV-50X3 | [@KC9HI](https://github.com/KC9HI) | 10-Dec-2022 | Yes | 0.06% | | BTECH_UV-5X3 | [@KC9HI](https://github.com/KC9HI) | 9-Dec-2022 | Yes | 0.43% | -| Baofeng_5RM | [@vdwel](https://github.com/vdwel) | 7-Dec-2023 | Yes | | -| Baofeng_5RX | [@KC9HI](https://github.com/KC9HI) | 16-Jan-2024 | Yes | | +| Baofeng_5RM | | | Yes | | +| Baofeng_5RX | | | Yes | | | Baofeng_BF-1901 | [@cetinajero](https://github.com/cetinajero) | 3-Jul-2023 | Yes | | | Baofeng_BF-1904 | [@cetinajero](https://github.com/cetinajero) | 3-Jul-2023 | Yes | | | Baofeng_BF-888 | [@kk7ds](https://github.com/kk7ds) | 13-Feb-2019 | Yes | **15.53%** | @@ -56,19 +56,19 @@ | Baofeng_BF-F8HP | [@KC9HI](https://github.com/KC9HI) | 18-Nov-2022 | Yes | **6.33%** | | Baofeng_BF-M4 | [@cetinajero](https://github.com/cetinajero) | 8-May-2023 | Yes | | | Baofeng_BF-T1 | [@KC9HI](https://github.com/KC9HI) | 10-Dec-2022 | Yes | 0.76% | -| Baofeng_BF-T20 | [@KC9HI](https://github.com/KC9HI) | 15-Dec-2023 | Yes | | -| Baofeng_BF-T20FRS | [@KC9HI](https://github.com/KC9HI) | 17-Dec-2023 | Yes | | +| Baofeng_BF-T20 | | | Yes | | +| Baofeng_BF-T20FRS | | | Yes | | | Baofeng_BF-T8 | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.42% | | Baofeng_BF-V8A | [@KC9HI](https://github.com/KC9HI) | 28-Nov-2022 | Yes | 0.03% | | Baofeng_F-11 | [Implied by Baofeng_UV-5R](#user-content-Baofeng_UV-5R) | 18-Nov-2022 | Yes | 0.12% | -| Baofeng_GM-5RH | [@KC9HI](https://github.com/KC9HI) | 21-Mar-2024 | Yes | | +| Baofeng_GM-5RH | | | Yes | | | Baofeng_GT-3WP | [@KC9HI](https://github.com/KC9HI) | 3-Dec-2022 | Yes | 0.43% | | Baofeng_GT-5R | [@KC9HI](https://github.com/KC9HI) | 18-Nov-2022 | Yes | 0.68% | | Baofeng_K5-Plus | | | Yes | | -| Baofeng_UV-13Pro | [@vdwel](https://github.com/vdwel) | 21-Feb-2024 | Yes | | -| Baofeng_UV-17 | [@vdwel](https://github.com/vdwel) | 27-Nov-2023 | Yes | | -| Baofeng_UV-17Pro | [@vdwel](https://github.com/vdwel) | 27-Nov-2023 | Yes | | -| Baofeng_UV-17ProGPS | [@vdwel](https://github.com/vdwel) | 27-Nov-2023 | Yes | | +| Baofeng_UV-13Pro | | | Yes | | +| Baofeng_UV-17 | | | Yes | | +| Baofeng_UV-17Pro | | | Yes | | +| Baofeng_UV-17ProGPS | | | Yes | | | Baofeng_UV-3R | [@yarda](https://github.com/yarda) | 26-Sep-2022 | Yes | 0.45% | | Baofeng_UV-5R | [@lekv](https://github.com/lekv) | 23-Oct-2022 | Yes | **28.81%** | | Baofeng_UV-6 | [Implied by Baofeng_UV-5R](#user-content-Baofeng_UV-5R) | 18-Nov-2022 | Yes | 0.46% | @@ -86,7 +86,7 @@ | Baojie_BJ-9900 | | | Yes | 0.02% | | Baojie_BJ-UV55 | | | Yes | 0.03% | | Boblov_X3Plus | | | | 0.02% | -| Boristone_8RS | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | 0.00% | +| Boristone_8RS | | | Yes | 0.00% | | CRT_Micron_UV | [Implied by Retevis_RT95](#user-content-Retevis_RT95) | 13-Nov-2022 | Yes | 0.08% | | CRT_Micron_UV_V2 | [Implied by Retevis_RT95](#user-content-Retevis_RT95) | 13-Nov-2022 | Yes | 0.10% | | Cignus_XTR-5 | [Implied by Radioddity_GS-5B](#user-content-Radioddity_GS-5B) | 17-Mar-2023 | Yes | | @@ -102,7 +102,7 @@ | Feidaxin_FD-460A | | | | 0.01% | | Feidaxin_FD-460UH | | | | 0.01% | | Generic_CSV | [@kk7ds](https://github.com/kk7ds) | 4-Dec-2022 | | | -| HamGeek_HG-590 | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | 0.00% | +| HamGeek_HG-590 | | | Yes | 0.00% | | Hiroyasu_HI-8811 | [@KC9HI](https://github.com/KC9HI) | 4-Jun-2023 | Yes | | | HobbyPCB_RS-UV3 | [@kk7ds](https://github.com/kk7ds) | 9-Dec-2022 | Yes | 0.02% | | Icom_IC-208H | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/icf.py) | 12-Dec-2022 | Yes | 0.05% | @@ -144,11 +144,11 @@ | Icom_ID-880H | [@kk7ds](https://github.com/kk7ds) | 22-Oct-2022 | Yes | 0.02% | | Intek_HR-2040 | [Implied by AnyTone_5888UV](#user-content-AnyTone_5888UV) | 9-Dec-2022 | Yes | 0.02% | | Intek_KT-980HP | [Implied by Baofeng_BF-F8HP](#user-content-Baofeng_BF-F8HP) | 18-Nov-2022 | Yes | 0.04% | -| JJCC_JC-8629 | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | | +| JJCC_JC-8629 | | | Yes | | | Jetstream_JT220M | [Implied by Alinco_DR235T](#user-content-Alinco_DR235T) | 12-Dec-2022 | Yes | 0.01% | | Jetstream_JT270M | [Implied by Jetstream_JT270M](#user-content-Jetstream_JT270M) | 13-Dec-2022 | Yes | 0.01% | | Jetstream_JT270MH | [@kk7ds](https://github.com/kk7ds) | 13-Dec-2022 | Yes | 0.01% | -| Jianpai_8800_Plus | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | 0.01% | +| Jianpai_8800_Plus | | | Yes | 0.01% | | KYD_IP-620 | | | | 0.02% | | KYD_NC-630A | | | | 0.02% | | Kenwood_HMK | [Implied by Generic_CSV](#user-content-Generic_CSV) | 4-Dec-2022 | | 0.00% | @@ -158,7 +158,7 @@ | Kenwood_TH-D72_live_mode | [@kk7ds](https://github.com/kk7ds) | 21-Oct-2022 | Yes | 0.00% | | Kenwood_TH-D74_clone_mode | [@kk7ds](https://github.com/kk7ds) | 30-Nov-2022 | Yes | 0.00% | | Kenwood_TH-D74_live_mode | [@kk7ds](https://github.com/kk7ds) | 20-Oct-2022 | Yes | 0.00% | -| Kenwood_TH-D75 | [Reported working](https://chirp.danplanet.com/issues/11122) | 114-Feb-2024 | Yes | | +| Kenwood_TH-D75 | | | Yes | | | Kenwood_TH-D7G | [Probably works](https://github.com/kk7ds/chirp/blob/py3/chirp/drivers/kenwood_live.py) | 12-Dec-2022 | Yes | 0.00% | | Kenwood_TH-F6 | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.03% | | Kenwood_TH-F7 | [@sv1](https://github.com/sv1) | 19-Oct-2022 | Yes | 0.03% | @@ -235,13 +235,13 @@ | LUITON_LT-580_VHF | [@KC9HI](https://github.com/KC9HI) | 8-Dec-2022 | Yes | 0.01% | | LUITON_LT-588UV | [@KC9HI](https://github.com/KC9HI) | 11-Nov-2022 | Yes | 0.01% | | LUITON_LT-725UV | [@KC9HI](https://github.com/KC9HI) | 11-Dec-2022 | Yes | 0.02% | -| Lanchonlh_HG-UV98 | [Reported working](https://chirp.danplanet.com/issues/11179) | 23-Feb-2024 | Yes | 0.00% | +| Lanchonlh_HG-UV98 | | | Yes | 0.00% | | Leixen_VV-898 | [Implied by Jetstream_JT270MH](#user-content-Jetstream_JT270MH) | 13-Dec-2022 | Yes | 0.16% | | Leixen_VV-898E | | | Yes | 0.02% | -| Leixen_VV-898E_Dual_Bank | [Implied by Leixen_VV-898S_Dual_Bank](#user-content-Leixen_VV-898S_Dual_Bank) | 10-Feb-2024 | Yes | | +| Leixen_VV-898E_Dual_Bank | | | Yes | | | Leixen_VV-898S | [@READ10](https://github.com/READ10) | 19-Dec-2022 | Yes | 0.08% | -| Leixen_VV-898S_Dual_Bank | [Reported working](https://chirp.danplanet.com/issues/4069) | 10-Feb-2024 | Yes | | -| MMLradio_JC-8629 | [@KC9HI](https://github.com/KC9HI) | 11-Nov-2023 | Yes | 0.01% | +| Leixen_VV-898S_Dual_Bank | | | Yes | | +| MMLradio_JC-8629 | | | Yes | 0.01% | | MTC_UV-5R-3 | [Implied by BTECH_UV-5X3](#user-content-BTECH_UV-5X3) | 9-Dec-2022 | Yes | 0.02% | | Maverick_RA-100 | [@cetinajero](https://github.com/cetinajero) | 7-Jul-2023 | Yes | | | Maverick_RA-425 | [@cetinajero](https://github.com/cetinajero) | 7-Jul-2023 | Yes | | @@ -264,7 +264,7 @@ | QYT_KT980PLUS | [Implied by BTECH_UV-25X2](#user-content-BTECH_UV-25X2) | 11-Nov-2022 | Yes | 0.09% | | Quansheng_TG-UV2+ | [Reported working](https://chirp.danplanet.com/issues/10398) | 26-Feb-2023 | Yes | 0.05% | | Quansheng_UV-K5 | [@sq5bpf](https://github.com/sq5bpf) | 29-May-2023 | Yes | | -| Quansheng_UV-K5_egzumer | [@egzumer](https://github.com/egzumer) | 23-Jan-2024 | Yes | | +| Quansheng_UV-K5_egzumer | | | Yes | | | RT_Systems_CSV | | | | | | Radioddity_DB25-G | [@KC9HI](https://github.com/KC9HI) | 11-Nov-2022 | Yes | 0.17% | | Radioddity_GA-2S | [@KC9HI](https://github.com/KC9HI) | 4-Dec-2022 | Yes | 0.19% | @@ -277,14 +277,14 @@ | Radtel_RT-470 | [@KC9HI](https://github.com/KC9HI) | 2-Apr-2023 | Yes | 0.00% | | Radtel_RT-470L | [@KC9HI](https://github.com/KC9HI) | 5-May-2023 | Yes | | | Radtel_RT-470X | [@KC9HI](https://github.com/KC9HI) | 10-Aug-2023 | Yes | | -| Radtel_RT-490 | [@KC9HI](https://github.com/KC9HI) | 11-Nov-2023 | Yes | 0.08% | +| Radtel_RT-490 | | | Yes | 0.08% | | Radtel_T18 | [@KC9HI](https://github.com/KC9HI) | 28-Nov-2022 | Yes | 0.07% | | Retevis_H777S | [@KC9HI](https://github.com/KC9HI) | 22-Dec-2022 | Yes | | | Retevis_H777_Plus | [@KC9HI](https://github.com/KC9HI) | 4-Dec-2022 | Yes | 0.06% | | Retevis_RA685 | [@KC9HI](https://github.com/KC9HI) | 19-Nov-2022 | Yes | 0.25% | | Retevis_RA79 | [@KC9HI](https://github.com/KC9HI) | 27-Oct-2023 | Yes | | | Retevis_RA85 | [@KC9HI](https://github.com/KC9HI) | 19-Nov-2022 | Yes | 0.05% | -| Retevis_RA89 | [@KC9HI](https://github.com/KC9HI) | 23-Nov-2023 | Yes | | +| Retevis_RA89 | | | Yes | | | Retevis_RB15 | [@KC9HI](https://github.com/KC9HI) | 18-Dec-2022 | Yes | 0.01% | | Retevis_RB17 | [@KC9HI](https://github.com/KC9HI) | 28-Nov-2022 | Yes | 0.02% | | Retevis_RB17A | [@KC9HI](https://github.com/KC9HI) | 30-Nov-2022 | Yes | 0.01% | @@ -353,12 +353,12 @@ | Ruyage_UV58Plus | [@KC9HI](https://github.com/KC9HI) | 18-Mar-2023 | Yes | | | Sainsonic_AP510 | | | | 0.00% | | SenhaiX_8800 | [Implied by Radioddity_GS-5B](#user-content-Radioddity_GS-5B) | 17-Mar-2023 | Yes | | -| Socotran_FB-8629 | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | 0.00% | -| Socotran_JC-8629 | [Implied by Radtel_RT-490](#user-content-Radtel_RT-490) | 11-Nov-2023 | Yes | | +| Socotran_FB-8629 | | | Yes | 0.00% | +| Socotran_JC-8629 | | | Yes | | | TDXone_TD-Q8A | [@KC9HI](https://github.com/KC9HI) | 18-Dec-2022 | | 0.02% | -| TIDRADIO_TD-H3 | [@songjunbiao](https://github.com/songjunbiao) | 1-Dec-2023 | Yes | | -| TIDRADIO_TD-H3-GMRS | [@songjunbiao](https://github.com/songjunbiao) | 1-Dec-2023 | Yes | | -| TIDRADIO_TD-H3-HAM | [@songjunbiao](https://github.com/songjunbiao) | 1-Dec-2023 | Yes | | +| TIDRADIO_TD-H3 | | | Yes | | +| TIDRADIO_TD-H3-GMRS | | | Yes | | +| TIDRADIO_TD-H3-HAM | | | Yes | | | TIDRADIO_TD-H6 | [@kk7ds](https://github.com/kk7ds) | 21-Oct-2022 | Yes | 0.43% | | TIDRADIO_TD-H8 | [@Sandmann34](https://github.com/Sandmann34) | 7-Feb-2023 | Yes | | | TIDRADIO_TD-H8-GMRS | [@Sandmann34](https://github.com/Sandmann34) | 7-Feb-2023 | Yes | | @@ -380,7 +380,7 @@ | TYT_TH9000_220 | [Implied by Retevis_RT9000D_220-260](#user-content-Retevis_RT9000D_220-260) | 8-Dec-2022 | Yes | 0.04% | | TYT_TH9000_440 | [Implied by Retevis_RT9000D_400-490](#user-content-Retevis_RT9000D_400-490) | 8-Dec-2022 | Yes | 0.04% | | Talkpod_A36plus | [@KC9HI](https://github.com/KC9HI) | 12-Jun-2023 | Yes | | -| Talkpod_A36plus_8w | [@KC9HI](https://github.com/KC9HI) | 16-Feb-2024 | Yes | | +| Talkpod_A36plus_8w | | | Yes | | | Vertex_Standard_FTL-1011 | | | | 0.02% | | Vertex_Standard_FTL-2011 | | | | 0.05% | | Vertex_Standard_FTL-7011 | | | | 0.03% | @@ -440,8 +440,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% | @@ -456,11 +458,11 @@ | Zastone_ZT-X6 | [Implied by Retevis_RT22](#user-content-Retevis_RT22) | 9-Dec-2022 | Yes | 0.11% | ## Stats -**Drivers:** 453 +**Drivers:** 455 -**Tested:** 87% (398/55) (93% of usage stats) +**Tested:** 81% (371/84) (93% of usage stats) -**Byte clean:** 91% (414/39) +**Byte clean:** 91% (416/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..4f41c0e36 100644 --- a/tests/py3_driver_testers.txt +++ b/tests/py3_driver_testers.txt @@ -11,7 +11,6 @@ Abbree_AR-518,@KC9HI,16-Dec-2022 Abbree_AR-63,@KC9HI,30-Nov-2022 Abbree_AR-730,@KC9HI,17-Aug-2023 -Abbree_AR-869,+Radtel_RT-490,11-Nov-2023 Abbree_AR-F5,@KC9HI,09-Aug-2023 Alinco_DJ175,+Alinco_DR235T,12-Dec-2022 Alinco_DJ596,+Alinco_DR235T,12-Dec-2022 @@ -22,7 +21,6 @@ Alinco_DR06T,+Alinco_DR235T,12-Dec-2022 Alinco_DR135T,+Alinco_DR235T,12-Dec-2022 Alinco_DR235T,@kk7ds,12-Dec-2022 Alinco_DR435T,+Alinco_DR235T,12-Dec-2022 -Alinco_DR735T,@jacobcalvert,03-April-2024 Anysecu_UV-A37,@KC9HI,5-Jun-2023 Anysecu_WP-9900,@KC9HI,11-Nov-2022 Anysecu_AC-580,+Radioddity_GS-5B,17-Mar-2023 @@ -52,8 +50,6 @@ BTECH_UV-50X2,@KC9HI,11-Nov-2022 BTECH_UV-50X2_G2,@KC9HI,30-Nov-2022 BTECH_UV-50X3,@KC9HI,10-Dec-2022 BTECH_UV-5X3,@KC9HI,9-Dec-2022 -Baofeng_5RM,@vdwel,7-Dec-2023 -Baofeng_5RX,@KC9HI,16-Jan-2024 Baofeng_BF-1901,@cetinajero,3-Jul-2023 Baofeng_BF-1904,@cetinajero,3-Jul-2023 Baofeng_BF-888,@kk7ds,13-Feb-2019 @@ -62,12 +58,9 @@ Baofeng_BF-A58S,@KC9HI,3-Dec-2022 Baofeng_BF-F8HP,@KC9HI,18-Nov-2022 Baofeng_BF-M4,@cetinajero,8-May-2023 Baofeng_BF-T1,@KC9HI,10-Dec-2022 -Baofeng_BF-T20,@KC9HI,15-Dec-2023 -Baofeng_BF-T20FRS,@KC9HI,17-Dec-2023 Baofeng_BF-T8,@KC9HI,30-Nov-2022 Baofeng_BF-V8A,@KC9HI,28-Nov-2022 Baofeng_F-11,+Baofeng_UV-5R,18-Nov-2022 -Baofeng_GM-5RH,@KC9HI,21-Mar-2024 Baofeng_GT-3WP,@KC9HI,3-Dec-2022 Baofeng_GT-5R,@KC9HI,18-Nov-2022 Baofeng_UV-3R,@yarda,26-Sep-2022 @@ -79,22 +72,16 @@ Baofeng_UV-82HP,@KC9HI,18-Nov-2022 Baofeng_UV-82WP,@KC9HI,3-Dec-2022 Baofeng_UV-9G,@KC9HI,3-Dec-2022 Baofeng_UV-9R,@KC9HI,3-Dec-2022 -Baofeng_UV-13Pro,@vdwel,21-Feb-2024 -Baofeng_UV-17,@vdwel,27-Nov-2023 -Baofeng_UV-17Pro,@vdwel,27-Nov-2023 -Baofeng_UV-17ProGPS,@vdwel,27-Nov-2023 Baofeng_UV-B5,@KC9HI,18-Nov-2022 Baofeng_UV-S9X3,@KC9HI,22-Aug-2023 Baofeng_W31E,@KC9HI,14-Aug-2023 Baojie_BJ-218,+LUITON_LT-725UV,16-Feb-2023 Baojie_BJ-318,@KC9HI,2-Jan-2023 -Boristone_8RS,+Radtel_RT-490,11-Nov-2023 Cignus_XTR-5,+Radioddity_GS-5B,17-Mar-2023 CRT_Micron_UV,+Retevis_RT95,13-Nov-2022 CRT_Micron_UV_V2,+Retevis_RT95,13-Nov-2022 Explorer_QRZ-1,+TYT_TH-UV88,5-Dec-2022 Generic_CSV,@kk7ds,4-Dec-2022 -HamGeek_HG-590,+Radtel_RT-490,11-Nov-2023 Hiroyasu_HI-8811,@KC9HI,4-Jun-2023 HobbyPCB_RS-UV3,@kk7ds,9-Dec-2022 Icom_IC-208H,*icf,12-Dec-2022 @@ -138,8 +125,6 @@ Intek_HR-2040,+AnyTone_5888UV,9-Dec-2022 Jetstream_JT220M,+Alinco_DR235T,12-Dec-2022 Jetstream_JT270M,+Jetstream_JT270M,13-Dec-2022 Jetstream_JT270MH,@kk7ds,13-Dec-2022 -Jianpai_8800_Plus,+Radtel_RT-490,11-Nov-2023 -JJCC_JC-8629,+Radtel_RT-490,11-Nov-2023 Kenwood_HMK,+Generic_CSV,4-Dec-2022 Kenwood_TH-D7,*kenwood_live,12-Dec-2022 Kenwood_TH-D7G,*kenwood_live,12-Dec-2022 @@ -147,7 +132,6 @@ Kenwood_TH-D72_clone_mode,@kk7ds,28-Nov-2022 Kenwood_TH-D72_live_mode,@kk7ds,21-Oct-2022 Kenwood_TH-D74_clone_mode,@kk7ds,30-Nov-2022 Kenwood_TH-D74_live_mode,@kk7ds,20-Oct-2022 -Kenwood_TH-D75,#11122,114-Feb-2024 Kenwood_TH-F6,@KC9HI,30-Nov-2022 Kenwood_TH-F7,@sv1,19-Oct-2022 Kenwood_TH-G71,*kenwood_live,12-Dec-2022 @@ -203,11 +187,8 @@ Kenwood_TK-860,+Kenwood_TK-760,5-Dec-2022 Kenwood_TK-862,+Kenwood_TK-760,5-Dec-2022 Kenwood_TK-868,+Kenwood_TK-760,5-Dec-2022 Kenwood_TS-2000,ZS6CRW,10-Jan-2022 -Lanchonlh_HG-UV98,#11179,23-Feb-2024 Leixen_VV-898,+Jetstream_JT270MH,13-Dec-2022 Leixen_VV-898S,@READ10,19-Dec-2022 -Leixen_VV-898S_Dual_Bank,#4069,10-Feb-2024 -Leixen_VV-898E_Dual_Bank,+Leixen_VV-898S_Dual_Bank,10-Feb-2024 LUITON_LT-316,+Retevis_RT22,9-Dec-2022 LUITON_LT-580_UHF,@KC9HI,8-Dec-2022 LUITON_LT-580_VHF,@KC9HI,8-Dec-2022 @@ -215,7 +196,6 @@ LUITON_LT-588UV,@KC9HI,11-Nov-2022 LUITON_LT-725UV,@KC9HI,11-Dec-2022 Maverick_RA-100,@cetinajero,7-Jul-2023 Maverick_RA-425,@cetinajero,7-Jul-2023 -MMLradio_JC-8629,@KC9HI,11-Nov-2023 MTC_UV-5R-3,+BTECH_UV-5X3,9-Dec-2022 Midland_DBR2500,+Retevis_RT95,13-Nov-2022 Polmar_DB-50M,+AnyTone_5888UV,9-Dec-2022 @@ -225,7 +205,6 @@ Q-MAC_HF-90_v300_or_earlier,@fthain,9-Mar-2023 Q-MAC_HF-90_v301_or_later,@fthain,9-Mar-2023 Quansheng_TG-UV2+,#10398,26-Feb-2023 Quansheng_UV-K5,@sq5bpf,29-May-2023 -Quansheng_UV-K5_egzumer,@egzumer,23-Jan-2024 QYT_KT-8R,@KC9HI,11-Nov-2022 QYT_KT-UV980,+WACCOM_MINI-8900,11-Nov-2022 QYT_KT-WP12,+Anysecu_WP-9900,11-Nov-2022 @@ -246,14 +225,12 @@ Radioddity_UV-82X3,@KC9HI,18-Nov-2022 Radtel_RT-470,@KC9HI,2-Apr-2023 Radtel_RT-470L,@KC9HI,5-May-2023 Radtel_RT-470X,@KC9HI,10-Aug-2023 -Radtel_RT-490,@KC9HI,11-Nov-2023 Radtel_T18,@KC9HI,28-Nov-2022 Retevis_H777S,@KC9HI,22-Dec-2022 Retevis_H777_Plus,@KC9HI,4-Dec-2022 Retevis_RA685,@KC9HI,19-Nov-2022 Retevis_RA79,@KC9HI,27-Oct-2023 Retevis_RA85,@KC9HI,19-Nov-2022 -Retevis_RA89,@KC9HI,23-Nov-2023 Retevis_RB15,@KC9HI,18-Dec-2022 Retevis_RB17,@KC9HI,28-Nov-2022 Retevis_RB17A,@KC9HI,30-Nov-2022 @@ -320,10 +297,7 @@ Retevis_RT9000D_400-490,@KC9HI,8-Dec-2022 Retevis_RT9000D_66-88,@KC9HI,8-Dec-2022 Ruyage_UV58Plus,@KC9HI,18-Mar-2023 SenhaiX_8800,+Radioddity_GS-5B,17-Mar-2023 -Socotran_FB-8629,+Radtel_RT-490,11-Nov-2023 -Socotran_JC-8629,+Radtel_RT-490,11-Nov-2023 Talkpod_A36plus,@KC9HI,12-Jun-2023 -Talkpod_A36plus_8w,@KC9HI,16-Feb-2024 TDXone_TD-Q8A,@KC9HI,18-Dec-2022 TIDRADIO_TD-H6,@kk7ds,21-Oct-2022 TIDRADIO_TD-H8,@Sandmann34,7-Feb-2023 @@ -331,9 +305,6 @@ TIDRADIO_TD-H8-GMRS,@Sandmann34,7-Feb-2023 TIDRADIO_TD-H8-HAM,@Sandmann34,7-Feb-2023 TID_TD-M8,+Retevis_RT22,9-Dec-2022 TID_TD-UV68,@Sandmann34,7-Feb-2023 -TIDRADIO_TD-H3,@songjunbiao,1-Dec-2023 -TIDRADIO_TD-H3-GMRS,@songjunbiao,1-Dec-2023 -TIDRADIO_TD-H3-HAM,@songjunbiao,1-Dec-2023 TYT_TH-UVF1,@kk7ds,13-Dec-2022 TYT_TH-UVF8D,@MELERIX,2-Feb-2023 TYT_TH-UV8000,@KC9HI,6-Jan-2023 @@ -395,7 +366,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