# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com) V2.0 # PY3 Compliant release. # Thanks to Filippi Marco for Yaesu processes # # 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 . """FT450D 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, \ RadioSettings import time import struct import logging LOG = logging.getLogger(__name__) CMD_ACK = b'\x06' EX_MODES = ["USER-L", "USER-U", "LSB+CW", "USB+CW", "RTTY-L", "RTTY-U", "N/A"] T_STEPS = sorted(list(chirp_common.TUNING_STEPS)) T_STEPS.remove(30.0) T_STEPS.remove(100.0) T_STEPS.remove(125.0) T_STEPS.remove(200.0) @directory.register class FT450DRadio(yaesu_clone.YaesuCloneModeRadio): """Yaesu FT-450D""" BAUD_RATE = 38400 COM_BITS = 8 # number of data bits COM_PRTY = 'N' # parity checking COM_STOP = 1 # stop bits MODEL = "FT-450D" NEEDS_COMPAT_SERIAL = False DUPLEX = ["", "-", "+"] MODES = ["LSB", "USB", "CW", "AM", "FM", "RTTY-L", "USER-L", "USER-U", "NFM", "CWR"] TMODES = ["", "Tone", "TSQL"] STEPSFM = [5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0] STEPSAM = [2.5, 5.0, 9.0, 10.0, 12.5, 25.0] STEPSSSB = [1.0, 2.5, 5.0] VALID_BANDS = [(100000, 33000000), (33000000, 56000000)] FUNC_LIST = ['MONI', 'N/A', 'PBAK', 'PLAY1', 'PLAY2', 'PLAY3', 'QSPLIT', 'SPOT', 'SQLOFF', 'SWR', 'TXW', 'VCC', 'VOICE2', 'VM1MONI', 'VM1REC', 'VM1TX', 'VM2MONI', 'VM2REC', 'VM2TX', 'DOWN', 'FAST', 'UP', 'DSP', 'IPO/ATT', 'NB', 'AGC', 'MODEDN', 'MODEUP', 'DSP/SEL', 'KEYER', 'CLAR', 'BANDDN', 'BANDUP', 'A=B', 'A/B', 'LOCK', 'TUNE', 'VOICE', 'MW', 'V/M', 'HOME', 'RCL', 'VOX', 'STO', 'STEP', 'SPLIT', 'PMS', 'SCAN', 'MENU', 'DIMMER', 'MTR'] CHARSET = list(chirp_common.CHARSET_ASCII) CHARSET.remove("\\") # WDC MEM_SIZE = 15017 MEM_SIZE = 15015 # block 9 (135 Bytes long) is to be repeated 101 times #WDC _block_lengths = [4, 84, 135, 162, 135, 162, 151, 130, 135, 127, 189, 103] _block_lengths = [2, 84, 135, 162, 135, 162, 151, 130, 135, 127, 189, 103] MEM_FORMAT = """ struct mem_struct { // 27 bytes per channel u8 tag_on_off:2, // @ Byte 0 1=Off, 2=On unk0:2, mode:4; u8 duplex:2, // @ byte 1 att:1, ipo:1, unka1:1, tunerbad:1, // ?? Possible tuner failed unk1b:1, // @@@??? uprband:1; u8 cnturpk:1, // @ Byte 2 Peak (clr), Null (set) cnturmd:3, // Contour filter mode cnturgn:1, // Contour filter gain Low/high mode2:3; // When mode is data(5) u8 ssb_step:2, // @ Byte 3 am_step:3, fm_step:3; u8 tunerok:1, // @ Byte 4 ?? Poss tuned ok cnturon:1, unk4b:1, dnr_on:1 notch:1, unk4c:1, tmode:2; // Tone/Cross/etc as Off/Enc/Enc+Dec u8 unk5a:4, // @ byte 5 dnr_val:4; u8 cw_width:2, // # byte 6, Notch width indexes fm_width:2, am_width:2, sb_width:2; i8 notch_pos; // @ Byte 7 Signed: - 0 + u8 tone; // @ Byte 8 u8 unk9; // @ Byte 9 Always set to 0 u8 unkA; // @ Byte A u8 unkB; // @ Byte B u32 freq; // @ C-F u32 offset; // @ 10-13 u8 name[7]; // @ 14-1A }; # seekto 0x02; struct { u8 set04; // ?Checksum / Clone counter? u8 set05; // Current VFO? u8 set06; u8 fast:1, lock:1, // Inverted: 1 = Off nb:1, agc:5; u8 set08a:3, keyer:1, set08b:2, mtr_mode:2; u8 set09; u8 set0A; u8 set0B:2, clk_sft:1, cont:5; // 1:1 u8 beepvol_sgn:1, // @x0C: set : Link @0x41, clear: fix @ 0x40 set0Ca:3, clar_btn:1, // 0 = Dial, 1= SEL cwstone_sgn:1, // Set: Lnk @ x42, clear: Fixed at 0x43 beepton:2; // Index 0-3 u8 set0Da:1, cw_key:1, set0Db:3, dialstp_mode:1, dialstp:2; u8 set0E:1, keyhold:1, lockmod:1, set0ea:1, amfmdial:1, // 0= Enabled. 1 = Disabled cwpitch:3; // 0-based index u8 sql_rfg:1 set0F:2, cwweigt:5; // Index 1:2.5=0 -> 1:4.5=20 u8 cw_dly; // @x10 ms = val * 10 u8 set11; u8 cwspeed; // val 4-60 is wpm, *5 is cpm u8 vox_gain; // val 1:1 u8 set14:2, emergen:1, vox_dly:5; // ms = val * 100 u8 set15a:1, stby_beep:1 set15b:1 mem_grp:1, apo:4; u8 tot; // Byte x16, 1:1 u8 micscan:1, set17:5, micgain:2; u8 cwpaddl:1, // @x18 0=Key, 1=Mic set18:7; u8 set19; u8 set1A; u8 set1B; u8 set1C; u8 dig_vox; // 1:1 u8 set1E; i16 d_disp; // @ x1F,x20 signed 16bit u8 pnl_cs; // 0-based index u8 pm_up; u8 pm_fst; u8 pm_dwn; u8 set25; u8 set26; u8 set27; u8 set28; u8 beacon_time; // 1:1 u8 set2A; u8 cat_rts:1, // @x2b: Enable=0, Disable=1 peakhold:1, set2B:4, cat_tot:2; // Index 0-3 u8 set2CA:2, rtyrpol:1, rtytpol:1 rty_sft:2, rty_ton:1, set2CC:1; u8 dig_vox; // 1:1 u8 ext_mnu:1, m_tune:1, set2E:2, scn_res:4; u8 cw_auto:1, // Off=0, On=1 cwtrain:2, // Index set2F:1, cw_qsk:2, // Index cw_bfo:2; // Index u8 mic_eq; // @x30 1:1 u8 set31:5, catrate:3; // Index 0-4 u8 set32; u8 dimmer:4, set33:4; u8 set34; u8 set35; u8 set36; u8 set37; u8 set38a:1, rfpower:7; // 1:1 u8 set39a:2, tuner:3, // Index 0-4 seldial:3; // Index 0-5 u8 set3A; u8 set3B; u8 set3C; i8 qspl_f; // Signed u8 set3E; u8 set3F; u8 beepvol_fix; // 1:1 i8 beepvol_lnk; // SIGNED 2's compl byte u8 cwstone_fix; i8 cwstone_lnk; // signed byte u8 set44:2, mym_data:1, // My Mode: Data, set = OFF mym_fm:1, mym_am:1, mym_cw:1, mym_usb:1, mym_lsb:1; u8 myb_24:1, // My Band: 24 MHz set = OFF myb_21:1, myb_18:1, myb_14:1, myb_10:1, myb_7:1, myb_3_5:1, myb_1_8:1; u8 set46:6, myb_28:1, myb_50:1; u8 set47; u8 set48; u8 set49; u8 set4A; u8 set4B; u8 set4C; u8 set4D; u8 set4E; u8 set4F; u8 set50; u8 set51; u8 set52; u8 set53; u8 set54; u8 set55; u8 set56a:3, split:1, set56b:4; u8 set57; } settings; # seekto 0x56; struct mem_struct vfoa[11]; // The current cfgs for each vfo 'band' struct mem_struct vfob[11]; struct mem_struct home[2]; // The 2 Home cfgs (HF and 6m) struct mem_struct qmb; // The Quick Memory Bank STO/RCL struct mem_struct mtqmb; // Last QMB-MemTune cfg (not displayed) struct mem_struct mtune; // Last MemTune cfg (not displayed) # seekto 0x341; // chan status u8 visible[63]; // 1 bit per channel u8 pmsvisible; // @ 0x380 # seekto 0x381; u8 filled[63]; u8 pmsfilled; // @ 0x3c0 # seekto 0x3C1; struct mem_struct memory[500]; struct mem_struct pms[4]; // Programmed Scan limits @ x387D # seekto 0x3904; struct { char t1[40]; // CW Beacon Text char t2[40]; char t3[40]; } beacontext; // to 0x397E. ? 7 more bytes of stuff in block # seekto 0x3983; struct mem_struct m60[5]; // to 0x3A0B # seekto 0x03a43; struct mem_struct current; """ _CALLSIGN_CHARSET = [chr(x) for x in list(range(ord("0"), ord("9") + 1)) + list(range(ord("A"), ord("Z") + 1)) + [ord(" ")]] _CALLSIGN_CHARSET_REV = dict(zip(_CALLSIGN_CHARSET, range(0, len(_CALLSIGN_CHARSET)))) # WARNING Indecis are hard wired in get/set_memory code !!! # Channels print in + increasing index order (PMS first) SPECIAL_MEMORIES = { "VFOa-1.8M": -27, "VFOa-3.5M": -26, "VFOa-7M": -25, "VFOa-10M": -24, "VFOa-14M": -23, "VFOa-18M": -22, "VFOa-21M": -21, "VFOa-24M": -20, "VFOa-28M": -19, "VFOa-50M": -18, "VFOa-HF": -17, "VFOb-1.8M": -16, "VFOb-3.5M": -15, "VFOb-7M": -14, "VFOb-10M": -13, "VFOb-14M": -12, "VFOb-18M": -11, "VFOb-21M": - 10, "VFOb-24M": -9, "VFOb-28M": -8, "VFOb-50M": -7, "VFOb-HF": -6, "HOME-HF": -5, "HOME-50M": -4, "QMB": -3, "QMB-MTune": -2, "Mem-Tune": -1, } FIRST_VFOB_INDEX = -6 LAST_VFOB_INDEX = -16 FIRST_VFOA_INDEX = -17 LAST_VFOA_INDEX = -27 SPECIAL_PMS = { "PMS1-L": -36, "PMS1-U": -35, "PMS2-L": -34, "PMS2-U": -33, } LAST_PMS_INDEX = -36 SPECIAL_MEMORIES.update(SPECIAL_PMS) SPECIAL_60M = { "60m-Ch1": -32, "60m-Ch2": -31, "60m-Ch3": -30, "60m-Ch4": -29, "60m-Ch5": -28, } LAST_60M_INDEX = -32 SPECIAL_MEMORIES.update(SPECIAL_60M) SPECIAL_MEMORIES_REV = dict(zip(SPECIAL_MEMORIES.values(), SPECIAL_MEMORIES.keys())) @classmethod def get_prompts(cls): rp = chirp_common.RadioPrompts() rp.info = _( "The FT-450 radio driver loads the 'Special Channels' tab\n" "with the PMS scanning range memories (group 11), 60meter\n" "channels (group 12), the QMB (STO/RCL) memory, the HF and\n" "50m HOME memories and all the A and B VFO memories.\n" "There are VFO memories for the last frequency dialed in\n" "each band. The last mem-tune config is also stored.\n" "These Special Channels allow limited field editing.\n" "This driver also populates the 'Other' tab in the channel\n" "memory Properties window. This tab contains values for\n" "those channel memory settings that don't fall under the\n" "standard Chirp display columns.\n") rp.pre_download = _( "1. Turn radio off.\n" "2. Connect cable to ACC jack.\n" "3. Press and hold in the [MODE <] and [MODE >] keys" " while\n" " turning the radio on (\"CLONE MODE\" will appear on the\n" " display).\n" "4. After clicking OK here, press the [C.S.] key to\n" " send image.\n") rp.pre_upload = _( "1. Turn radio off.\n" "2. Connect cable to ACC jack.\n" "3. Press and hold in the [MODE <] and [MODE >] keys" " while\n" " turning the radio on (\"CLONE MODE\" will appear on the\n" " display).\n" "4. Click OK here.\n" " (\"Receiving\" will appear on the LCD).\n") return rp def _read(self, block, blocknum): # be very patient at first block if blocknum == 0: attempts = 60 else: attempts = 5 for _i in range(0, attempts): data = self.pipe.read(block + 2) # Blocknum, data,checksum if data: break time.sleep(0.5) if len(data) == block + 2 and data[0] == blocknum: checksum = yaesu_clone.YaesuChecksum(1, block) if checksum.get_existing(data) != \ checksum.get_calculated(data): raise Exception("Checksum Failed [%02X<>%02X] block %02X" % (checksum.get_existing(data), checksum.get_calculated(data), blocknum)) # Remove the block number and checksum data = data[1:block + 1] else: # Use this info to decode a new Yaesu model raise Exception("Unable to read block %i expected %i got %i" % (blocknum, block + 2, 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 start = time.time() data = b"" blocks = 0 status = chirp_common.Status() status.msg = _("Cloning from radio") nblocks = len(self._block_lengths) + 100 # Block 8 repeats status.max = nblocks for block in self._block_lengths: if blocks == 8: # repeated read of block 8 same size (chan memory area) repeat = 101 else: repeat = 1 for _i in range(0, repeat): data += self._read(block, blocks) self.pipe.write(CMD_ACK) blocks += 1 status.cur = blocks self.status_fn(status) data += self.MODEL.encode() return memmap.MemoryMapBytes(data) 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 delay = 0.5 start = time.time() blocks = 0 pos = 0 status = chirp_common.Status() status.msg = _("Cloning to radio") status.max = len(self._block_lengths) + 100 for block in self._block_lengths: if blocks == 8: repeat = 101 else: repeat = 1 for _i in range(0, repeat): time.sleep(0.01) checksum = yaesu_clone.YaesuChecksum(pos, pos + block - 1) LOG.debug("Sending block %s" % hex(blocks)) self.pipe.write(struct.pack('B', blocks)) blkdat = self.get_mmap()[pos:pos + block] LOG.debug("Sending %d bytes:\n%s" % (len(blkdat), util.hexprint(blkdat))) self.pipe.write(blkdat) xs = checksum.get_calculated(self.get_mmap()) LOG.debug("Sending checksum %s" % hex(xs)) self.pipe.write(struct.pack('B', xs)) buf = self.pipe.read(1) if not buf or buf[:1] != CMD_ACK: time.sleep(delay) buf = self.pipe.read(1) if not buf or buf[:1] != CMD_ACK: raise Exception(_("Radio did not ack block %i") % blocks) pos += block blocks += 1 status.cur = blocks self.status_fn(status) 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_bank = False rf.has_dtcs = False rf.valid_modes = [x for x in self.MODES if x in chirp_common.MODES] rf.valid_tmodes = list(self.TMODES) rf.valid_duplexes = list(self.DUPLEX) rf.valid_tuning_steps = list(T_STEPS) rf.valid_bands = self.VALID_BANDS rf.valid_power_levels = [] rf.valid_characters = "".join(self.CHARSET) rf.valid_name_length = 7 rf.valid_skips = [] rf.valid_special_chans = sorted(self.SPECIAL_MEMORIES.keys()) rf.memory_bounds = (1, 500) rf.has_ctone = True rf.has_settings = True rf.has_cross = True return rf def get_raw_memory(self, number): return repr(self._memobj.memory[number - 1]) def _get_tmode(self, mem, _mem): mem.tmode = self.TMODES[_mem.tmode] mem.rtone = chirp_common.TONES[_mem.tone] mem.ctone = mem.rtone def _set_duplex(self, mem, _mem): _mem.duplex = self.DUPLEX.index(mem.duplex) def get_memory(self, number): if isinstance(number, str): return self._get_special(number) elif number < 0: # I can't stop delete operation from losing extd_number but # I know how to get it back return self._get_special(self.SPECIAL_MEMORIES_REV[number]) else: return self._get_normal(number) def set_memory(self, memory): if memory.number < 0: return self._set_special(memory) else: return self._set_normal(memory) def _get_special(self, number): mem = chirp_common.Memory() mem.number = self.SPECIAL_MEMORIES[number] mem.extd_number = number if mem.number in range(self.FIRST_VFOA_INDEX, self.LAST_VFOA_INDEX - 1, -1): _mem = self._memobj.vfoa[-self.LAST_VFOA_INDEX + mem.number] immutable = ["number", "extd_number", "name", "power"] elif mem.number in range(self.FIRST_VFOB_INDEX, self.LAST_VFOB_INDEX - 1, -1): _mem = self._memobj.vfob[-self.LAST_VFOB_INDEX + mem.number] immutable = ["number", "extd_number", "name", "power"] elif mem.number in range(-4, -6, -1): # 2 Home Chans _mem = self._memobj.home[5 + mem.number] immutable = ["number", "extd_number", "name", "power"] elif mem.number == -3: _mem = self._memobj.qmb immutable = ["number", "extd_number", "name", "power"] elif mem.number == -2: _mem = self._memobj.mtqmb immutable = ["number", "extd_number", "name", "power"] elif mem.number == -1: _mem = self._memobj.mtune immutable = ["number", "extd_number", "name", "power"] elif mem.number in self.SPECIAL_PMS.values(): # bitindex = (-self.LAST_PMS_INDEX) + mem.number # used = (self._memobj.pmsvisible >> bitindex) & 0x01 # valid = (self._memobj.pmsfilled >> bitindex) & 0x01 # if not used: # mem.empty = True # if not valid: # mem.empty = True # return mem # ft450 only has 1 bit to expose PMS, and does not set filled indicator bitindex = 0 used = (self._memobj.pmsvisible >> bitindex) & 0x01 valid = used if not used: mem.empty = True if not valid: mem.empty = True return mem mx = (-self.LAST_PMS_INDEX) + mem.number _mem = self._memobj.pms[mx] mx = mx + 1 immutable = ["number", "rtone", "ctone", "extd_number", "tmode", "cross_mode", "power", "duplex", "offset"] elif mem.number in self.SPECIAL_60M.values(): mx = (-self.LAST_60M_INDEX) + mem.number _mem = self._memobj.m60[mx] mx = mx + 1 immutable = ["number", "rtone", "ctone", "extd_number", "tmode", "cross_mode", "frequency", "power", "duplex", "offset"] else: raise Exception("Sorry, you can't edit that special" " memory channel %i." % mem.number) mem = self._get_memory(mem, _mem) mem.immutable = immutable return mem def _set_special(self, mem): if mem.empty and mem.number not in self.SPECIAL_PMS.values(): # can't delete special memories! raise errors.RadioError("Sorry, special memory can't be deleted") cur_mem = self._get_special(self.SPECIAL_MEMORIES_REV[mem.number]) if mem.number in range(self.FIRST_VFOA_INDEX, self.LAST_VFOA_INDEX - 1, -1): _mem = self._memobj.vfoa[-self.LAST_VFOA_INDEX + mem.number] elif mem.number in range(self.FIRST_VFOB_INDEX, self.LAST_VFOB_INDEX - 1, -1): _mem = self._memobj.vfob[-self.LAST_VFOB_INDEX + mem.number] elif mem.number in range(-4, -6, -1): _mem = self._memobj.home[5 + mem.number] elif mem.number == -3: _mem = self._memobj.qmb elif mem.number == -2: _mem = self._memobj.mtqmb elif mem.number == -1: _mem = self._memobj.mtune elif mem.number in self.SPECIAL_PMS.values(): bitindex = (-self.LAST_PMS_INDEX) + mem.number wasused = (self._memobj.pmsvisible >> bitindex) & 0x01 wasvalid = (self._memobj.pmsfilled >> bitindex) & 0x01 if mem.empty: if wasvalid and not wasused: # pylint get confused by &= operator self._memobj.pmsfilled = self._memobj.pmsfilled & \ ~ (1 << bitindex) # pylint get confused by &= operator self._memobj.pmsvisible = self._memobj.pmsvisible & \ ~ (1 << bitindex) return # pylint get confused by |= operator self._memobj.pmsvisible = self._memobj.pmsvisible | 1 << bitindex self._memobj.pmsfilled = self._memobj.pmsfilled | 1 << bitindex _mem = self._memobj.pms[-self.LAST_PMS_INDEX + mem.number] else: raise errors.RadioError("Sorry, you can't edit" " that special memory.") for key in cur_mem.immutable: if key != "extd_number": if cur_mem.__dict__[key] != mem.__dict__[key]: raise errors.RadioError("Editing field `%s' " % key + "is not supported on this channel") self._set_memory(mem, _mem) def _get_normal(self, number): _mem = self._memobj.memory[number - 1] used = (self._memobj.visible[(number - 1) / 8] >> (number - 1) % 8) \ & 0x01 valid = (self._memobj.filled[(number - 1) / 8] >> (number - 1) % 8) \ & 0x01 mem = chirp_common.Memory() mem.number = number if not used: mem.empty = True if not valid or _mem.freq == 0xffffffff: return mem if mem.number == 1: mem.immutable = ['empty'] return self._get_memory(mem, _mem) def _set_normal(self, mem): _mem = self._memobj.memory[mem.number - 1] wasused = (self._memobj.visible[(mem.number - 1) / 8] >> (mem.number - 1) % 8) & 0x01 wasvalid = (self._memobj.filled[(mem.number - 1) / 8] >> (mem.number - 1) % 8) & 0x01 if mem.empty: if mem.number == 1: raise Exception("Sorry, can't delete first memory") if wasvalid and not wasused: self._memobj.filled[(mem.number - 1) // 8] &= \ ~(1 << (mem.number - 1) % 8) _mem.set_raw("\xFF" * (_mem.size() // 8)) # clean up self._memobj.visible[(mem.number - 1) // 8] &= \ ~(1 << (mem.number - 1) % 8) return if not wasvalid: _mem.set_raw("\x00" * (_mem.size() // 8)) # clean up self._memobj.visible[(mem.number - 1) // 8] |= 1 << (mem.number - 1) \ % 8 self._memobj.filled[(mem.number - 1) // 8] |= 1 << (mem.number - 1) \ % 8 self._set_memory(mem, _mem) def _get_memory(self, mem, _mem): mem.freq = int(_mem.freq) mem.offset = int(_mem.offset) mem.duplex = self.DUPLEX[_mem.duplex] # Mode gets tricky with dual (USB+DATA) options vx = _mem.mode if vx == 4: # FM or NFM if _mem.mode2 == 2: vx = 4 # FM else: vx = 8 # NFM if vx == 10: # CWR vx = 9 if vx == 5: # Data/Dual mode if _mem.mode2 == 0: # RTTY-L vx = 5 if _mem.mode2 == 1: # USER-L vx = 6 if _mem.mode2 == 2: # USER-U vx = 7 try: mem.mode = self.MODES[vx] except ValueError: LOG.error('The FT-450 driver is broken for unsupported modes') if mem.mode == "FM" or mem.mode == "NFM": mem.tuning_step = self.STEPSFM[_mem.fm_step] elif mem.mode == "AM": mem.tuning_step = self.STEPSAM[_mem.am_step] elif mem.mode[:2] == "CW": mem.tuning_step = self.STEPSSSB[_mem.ssb_step] else: try: mem.tuning_step = self.STEPSSSB[_mem.ssb_step] except IndexError: pass self._get_tmode(mem, _mem) if _mem.tag_on_off == 2: for i in _mem.name: if i == 0xFF: break if chr(i) in self.CHARSET: mem.name += chr(i) else: # radio has some graphical chars that are not supported # we replace those with a * LOG.info("Replacing char %x with *" % i) mem.name += "*" mem.name = mem.name.rstrip() else: mem.name = "" mem.extra = RadioSettingGroup("extra", "Extra") rs = RadioSetting("ipo", "IPO", RadioSettingValueBoolean(bool(_mem.ipo))) rs.set_doc("Bypass preamp") mem.extra.append(rs) rs = RadioSetting("att", "ATT", RadioSettingValueBoolean(bool(_mem.att))) rs.set_doc("10dB front end attenuator") mem.extra.append(rs) rs = RadioSetting("cnturon", "Contour Filter", RadioSettingValueBoolean(_mem.cnturon)) rs.set_doc("Contour filter on/off") mem.extra.append(rs) options = ["Peak", "Null"] rs = RadioSetting("cnturpk", "Contour Filter Mode", RadioSettingValueList(options, options[_mem.cnturpk])) mem.extra.append(rs) options = ["Low", "High"] rs = RadioSetting("cnturgn", "Contour Filter Gain", RadioSettingValueList(options, options[_mem.cnturgn])) rs.set_doc("Filter gain/attenuation") mem.extra.append(rs) options = ["-2", "-1", "Center", "+1", "+2"] rs = RadioSetting("cnturmd", "Contour Filter Notch", RadioSettingValueList(options, options[_mem.cnturmd])) rs.set_doc("Filter notch offset") mem.extra.append(rs) rs = RadioSetting("notch", "Notch Filter", RadioSettingValueBoolean(_mem.notch)) rs.set_doc("IF bandpass filter") mem.extra.append(rs) vx = 1 options = ["<-", "Center", "+>"] if _mem.notch_pos < 0: vx = 0 if _mem.notch_pos > 0: vx = 2 rs = RadioSetting("notch_pos", "Notch Position", RadioSettingValueList(options, options[vx])) rs.set_doc("IF bandpass filter shift") mem.extra.append(rs) vx = 0 if mem.mode[1:] == "SB": options = ["1.8 kHz", "2.4 kHz", "3.0 kHz"] vx = _mem.sb_width stx = "sb_width" elif mem.mode[:1] == "CW": options = ["300 Hz", "500 kHz", "2.4 kHz"] vx = _mem.cw_width stx = "cw_width" elif mem.mode[:4] == "USER" or mem.mode[:4] == "RTTY": options = ["300 Hz", "2.4 kHz", "3.0 kHz"] vx = _mem.sb_width stx = "sb_width" elif mem.mode == "AM": options = ["3.0 kHz", "6.0 kHz", "9.0 kHz"] vx = _mem.am_width stx = "am_width" else: options = ["2.5 kHz", "5.0 kHz"] vx = _mem.fm_width stx = "fm_width" rs = RadioSetting(stx, "IF Bandpass Filter Width", RadioSettingValueList(options, options[vx])) rs.set_doc("DSP IF bandpass Notch width (Hz)") mem.extra.append(rs) rs = RadioSetting("dnr_on", "DSP Noise Reduction", RadioSettingValueBoolean(bool(_mem.dnr_on))) rs.set_doc("Digital noise processing") mem.extra.append(rs) options = ["Off", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"] rs = RadioSetting("dnr_val", "DSP Noise Reduction Alg", RadioSettingValueList(options, options[_mem.dnr_val])) rs.set_doc("Digital noise reduction algorithm number (1-11)") mem.extra.append(rs) return mem # end get_memory def _set_memory(self, mem, _mem): if len(mem.name) > 0: _mem.tag_on_off = 2 else: _mem.tag_on_off = 1 self._set_duplex(mem, _mem) _mem.mode2 = 0 if mem.mode == "USER-L": _mem.mode = 5 _mem.mode2 = 1 elif mem.mode == "USER-U": _mem.mode = 5 _mem.mode2 = 2 elif mem.mode == "RTTY-L": _mem.mode = 5 _mem.mode2 = 0 elif mem.mode == "CWR": _mem.mode = 10 _mem.mode2 = 0 elif mem.mode == "CW": _mem.mode = 2 _mem.mode2 = 0 elif mem.mode == "NFM": _mem.mode = 4 _mem.mode2 = 1 elif mem.mode == "FM": _mem.mode = 4 _mem.mode2 = 2 else: # LSB, USB, AM _mem.mode = self.MODES.index(mem.mode) _mem.mode2 = 0 try: _mem.ssb_step = self.STEPSSSB.index(mem.tuning_step) except ValueError: pass try: _mem.am_step = self.STEPSAM.index(mem.tuning_step) except ValueError: pass try: _mem.fm_step = self.STEPSFM.index(mem.tuning_step) except ValueError: pass _mem.freq = mem.freq _mem.uprband = 0 if mem.freq >= 33000000: _mem.uprband = 1 _mem.offset = mem.offset _mem.tmode = self.TMODES.index(mem.tmode) _mem.tone = chirp_common.TONES.index(mem.rtone) _mem.tunerok = 0 # Dont know what these two do... _mem.tunerbad = 0 for i in range(0, 7): _mem.name[i] = ord(mem.name.ljust(7)[i]) for setting in mem.extra: if setting.get_name() == "notch_pos": vx = 0 # Override list string with signed value stx = str(setting.value) if stx == "<-": vx = -13 if stx == "+>": vx = 12 setattr(_mem, "notch_pos", vx) elif setting.get_name() == "dnr_val": stx = str(setting.value) # Convert string to int vx = 0 if stx != "Off": vx = int(stx) else: setattr(_mem, "dnr_on", 0) setattr(_mem, setting.get_name(), vx) else: setattr(_mem, setting.get_name(), setting.value) @classmethod def match_model(cls, filedata, filename): """Match the opened/downloaded image to the correct version""" if len(filedata) == cls.MEM_SIZE + 7: # +7 bytes of model name rid = filedata[cls.MEM_SIZE:cls.MEM_SIZE + 7] if rid.startswith(cls.MODEL.encode()): return True else: return False def _invert_me(self, setting, obj, atrb): """Callback: from inverted logic 1-bit booleans""" invb = not setting.value setattr(obj, atrb, invb) return def _chars2str(self, cary, knt): """Convert raw memory char array to a string: NOT a callback.""" stx = "" for char in cary[0:knt]: stx += chr(int(char)) return stx def _my_str2ary(self, setting, obj, atrba, knt): """Callback: convert string to fixed-length char array..""" ary = "" for j in range(0, knt, 1): chx = ord(str(setting.value)[j]) if chx < 32 or chx > 125: # strip non-printing ary += " " else: ary += str(setting.value)[j] setattr(obj, atrba, ary) return def get_settings(self): _settings = self._memobj.settings _beacon = self._memobj.beacontext gen = RadioSettingGroup("gen", "General") cw = RadioSettingGroup("cw", "CW") pnlcfg = RadioSettingGroup("pnlcfg", "Panel buttons") pnlset = RadioSettingGroup("pnlset", "Panel settings") voxdat = RadioSettingGroup("voxdat", "VOX and Data") mic = RadioSettingGroup("mic", "Microphone") mybands = RadioSettingGroup("mybands", "My Bands") mymodes = RadioSettingGroup("mymodes", "My Modes") top = RadioSettings(gen, cw, pnlcfg, pnlset, voxdat, mic, mymodes, mybands) self._do_general_settings(gen) self._do_cw_settings(cw) self._do_panel_buttons(pnlcfg) self._do_panel_settings(pnlset) self._do_vox_settings(voxdat) self._do_mic_settings(mic) self._do_mymodes_settings(mymodes) self._do_mybands_settings(mybands) return top def _do_general_settings(self, tab): _settings = self._memobj.settings rs = RadioSetting("ext_mnu", "Extended menu", RadioSettingValueBoolean(_settings.ext_mnu)) rs.set_doc("Enables access to extended settings in the radio") tab.append(rs) # Issue #8183 bugfix rs = RadioSetting("apo", "APO time (Hrs)", RadioSettingValueInteger(0, 12, _settings.apo)) tab.append(rs) options = ["%i" % i for i in range(0, 21)] options[0] = "Off" rs = RadioSetting("tot", "TX 'TOT' time-out (mins)", RadioSettingValueList(options, options[_settings.tot])) tab.append(rs) bx = not _settings.cat_rts # Convert from Enable=0 rs = RadioSetting("cat_rts", "CAT RTS flow control", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "cat_rts") tab.append(rs) options = ["0", "100ms", "1000ms", "3000ms"] rs = RadioSetting("cat_tot", "CAT Timeout", RadioSettingValueList(options, options[_settings.cat_tot])) tab.append(rs) options = ["4800", "9600", "19200", "38400", "Data"] rs = RadioSetting("catrate", "CAT rate", RadioSettingValueList(options, options[_settings.catrate])) tab.append(rs) rs = RadioSetting("mem_grp", "Mem groups", RadioSettingValueBoolean(_settings.mem_grp)) tab.append(rs) rs = RadioSetting("scn_res", "Resume scan (secs)", RadioSettingValueInteger(0, 10, _settings.scn_res)) tab.append(rs) rs = RadioSetting("clk_sft", "CPU clock shift", RadioSettingValueBoolean(_settings.clk_sft)) tab.append(rs) rs = RadioSetting("split", "TX/RX Frequency Split", RadioSettingValueBoolean(_settings.split)) tab.append(rs) rs = RadioSetting("qspl_f", "Quick-Split freq offset (kHz)", RadioSettingValueInteger(-20, 20, _settings.qspl_f)) tab.append(rs) rs = RadioSetting("emergen", "Alaska Emergency Mem 5167.5 kHz", RadioSettingValueBoolean(_settings.emergen)) tab.append(rs) rs = RadioSetting("stby_beep", "PTT release 'Standby' beep", RadioSettingValueBoolean(_settings.stby_beep)) tab.append(rs) options = ["ATAS", "EXT ATU", "INT ATU", "INTRATU", "F-TRANS"] rs = RadioSetting("tuner", "Antenna Tuner", RadioSettingValueList(options, options[_settings.tuner])) tab.append(rs) rs = RadioSetting("rfpower", "RF power (watts)", RadioSettingValueInteger(5, 100, _settings.rfpower)) tab.append(rs) # End of _do_general_settings def _do_cw_settings(self, cw): # - - - CW - - - _settings = self._memobj.settings _beacon = self._memobj.beacontext rs = RadioSetting("cw_dly", "CW break-in delay (ms * 10)", RadioSettingValueInteger(0, 300, _settings.cw_dly)) cw.append(rs) options = ["%i Hz" % i for i in range(400, 801, 100)] rs = RadioSetting("cwpitch", "CW pitch", RadioSettingValueList(options, options[_settings.cwpitch])) cw.append(rs) rs = RadioSetting("cwspeed", "CW speed (wpm)", RadioSettingValueInteger(4, 60, _settings.cwspeed)) rs.set_doc("Cpm is Wpm * 5") cw.append(rs) options = ["1:%1.1f" % (i / 10) for i in range(25, 46, 1)] rs = RadioSetting("cwweigt", "CW weight", RadioSettingValueList(options, options[_settings.cwweigt])) cw.append(rs) options = ["15ms", "20ms", "25ms", "30ms"] rs = RadioSetting("cw_qsk", "CW delay before TX in QSK mode", RadioSettingValueList(options, options[_settings.cw_qsk])) cw.append(rs) rs = RadioSetting("cwstone_sgn", "CW sidetone volume Linked", RadioSettingValueBoolean(_settings.cwstone_sgn)) rs.set_doc("If set; volume is relative to AF Gain knob.") cw.append(rs) rs = RadioSetting("cwstone_lnk", "CW sidetone linked volume", RadioSettingValueInteger(-50, 50, _settings.cwstone_lnk)) cw.append(rs) rs = RadioSetting("cwstone_fix", "CW sidetone fixed volume", RadioSettingValueInteger(0, 100, _settings.cwstone_fix)) cw.append(rs) options = ["Numeric", "Alpha", "Mixed"] rs = RadioSetting("cwtrain", "CW Training mode", RadioSettingValueList(options, options[_settings.cwtrain])) cw.append(rs) rs = RadioSetting("cw_auto", "CW key jack- auto CW mode", RadioSettingValueBoolean(_settings.cw_auto)) rs.set_doc("Enable for CW mode auto-set when keyer pluuged in.") cw.append(rs) options = ["Normal", "Reverse"] rs = RadioSetting("cw_key", "CW paddle wiring", RadioSettingValueList(options, options[_settings.cw_key])) cw.append(rs) rs = RadioSetting("beacon_time", "CW beacon Tx interval (secs)", RadioSettingValueInteger(0, 255, _settings.beacon_time)) cw.append(rs) tmp = self._chars2str(_beacon.t1, 40) rs = RadioSetting("t1", "CW Beacon Line 1", RadioSettingValueString(0, 40, tmp)) rs.set_apply_callback(self._my_str2ary, _beacon, "t1", 40) cw.append(rs) tmp = self._chars2str(_beacon.t2, 40) rs = RadioSetting("t2", "CW Beacon Line 2", RadioSettingValueString(0, 40, tmp)) rs.set_apply_callback(self._my_str2ary, _beacon, "t2", 40) cw.append(rs) tmp = self._chars2str(_beacon.t3, 40) rs = RadioSetting("t3", "CW Beacon Line 3", RadioSettingValueString(0, 40, tmp)) rs.set_apply_callback(self._my_str2ary, _beacon, "t3", 40) cw.append(rs) # END _do_cw_settings def _do_panel_settings(self, pnlset): # - - - Panel settings _settings = self._memobj.settings bx = not _settings.amfmdial # Convert from Enable=0 rs = RadioSetting("amfmdial", "AM&FM Dial", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "amfmdial") pnlset.append(rs) options = ["440 Hz", "880 Hz", "1760 Hz"] rs = RadioSetting("beepton", "Beep frequency", RadioSettingValueList(options, options[_settings.beepton])) pnlset.append(rs) rs = RadioSetting("beepvol_sgn", "Beep volume Linked", RadioSettingValueBoolean(_settings.beepvol_sgn)) rs.set_doc("If set; volume is relative to AF Gain knob.") pnlset.append(rs) rs = RadioSetting("beepvol_lnk", "Linked beep volume", RadioSettingValueInteger(-50, 50, _settings.beepvol_lnk)) rs.set_doc("Relative to AF-Gain setting.") pnlset.append(rs) rs = RadioSetting("beepvol_fix", "Fixed beep volume", RadioSettingValueInteger(0, 100, _settings.beepvol_fix)) rs.set_doc("When Linked setting is unchecked.") pnlset.append(rs) rs = RadioSetting("cont", "LCD Contrast", RadioSettingValueInteger(1, 24, _settings.cont)) rs.set_doc("This setting does not appear to do anything...") pnlset.append(rs) rs = RadioSetting("dimmer", "LCD Dimmer", RadioSettingValueInteger(1, 8, _settings.dimmer)) pnlset.append(rs) options = ["RF-Gain", "Squelch"] rs = RadioSetting("sql_rfg", "Squelch/RF-Gain", RadioSettingValueList(options, options[_settings.sql_rfg])) pnlset.append(rs) options = ["Frequencies", "Panel", "All"] rs = RadioSetting("lockmod", "Lock Mode", RadioSettingValueList(options, options[_settings.lockmod])) pnlset.append(rs) options = ["Dial", "SEL"] rs = RadioSetting("clar_btn", "CLAR button control", RadioSettingValueList(options, options[_settings.clar_btn])) pnlset.append(rs) if _settings.dialstp_mode == 0: # AM/FM options = ["SSB/CW:1Hz", "SSB/CW:10Hz", "SSB/CW:20Hz"] else: options = ["AM/FM:100Hz", "AM/FM:200Hz"] rs = RadioSetting("dialstp", "Dial tuning step", RadioSettingValueList(options, options[_settings.dialstp])) pnlset.append(rs) options = ["0.5secs", "1.0secs", "1.5secs", "2.0secs"] rs = RadioSetting("keyhold", "Buttons hold-to-activate time", RadioSettingValueList(options, options[_settings.keyhold])) pnlset.append(rs) rs = RadioSetting("m_tune", "Memory tune", RadioSettingValueBoolean(_settings.m_tune)) pnlset.append(rs) rs = RadioSetting("peakhold", "S-Meter display hold (1sec)", RadioSettingValueBoolean(_settings.peakhold)) pnlset.append(rs) options = ["CW Sidetone", "CW Speed", "100 kHz step", "1 MHz Step", "Mic Gain", "RF Power"] rs = RadioSetting("seldial", "SEL dial 2nd function (push)", RadioSettingValueList(options, options[_settings.seldial])) pnlset.append(rs) # End _do_panel_settings def _do_panel_buttons(self, pnlcfg): # - - - Current Panel Config _settings = self._memobj.settings rs = RadioSetting("pnl_cs", "C.S. Function", RadioSettingValueList(self.FUNC_LIST, self.FUNC_LIST[_settings.pnl_cs])) pnlcfg.append(rs) rs = RadioSetting("nb", "Noise blanker", RadioSettingValueBoolean(_settings.nb)) pnlcfg.append(rs) options = ["Auto", "Fast", "Slow", "Auto/Fast", "Auto/Slow", "?5?"] rs = RadioSetting("agc", "AGC", RadioSettingValueList(options, options[_settings.agc])) pnlcfg.append(rs) rs = RadioSetting("keyer", "Keyer", RadioSettingValueBoolean(_settings.keyer)) pnlcfg.append(rs) rs = RadioSetting("fast", "Fast step", RadioSettingValueBoolean(_settings.fast)) pnlcfg.append(rs) rs = RadioSetting("lock", "Lock (per Lock Mode)", RadioSettingValueBoolean(_settings.lock)) pnlcfg.append(rs) options = ["PO", "ALC", "SWR"] rs = RadioSetting("mtr_mode", "S-Meter mode", RadioSettingValueList(options, options[_settings.mtr_mode])) pnlcfg.append(rs) # End _do_panel_Buttons def _do_vox_settings(self, voxdat): # - - VOX and DATA Settings _settings = self._memobj.settings rs = RadioSetting("vox_dly", "VOX delay (x 100 ms)", RadioSettingValueInteger(1, 30, _settings.vox_dly)) voxdat.append(rs) rs = RadioSetting("vox_gain", "VOX Gain", RadioSettingValueInteger(0, 100, _settings.vox_gain)) voxdat.append(rs) rs = RadioSetting("dig_vox", "Digital VOX Gain", RadioSettingValueInteger(0, 100, _settings.dig_vox)) voxdat.append(rs) rs = RadioSetting("d_disp", "User-L/U freq offset (Hz)", RadioSettingValueInteger(-3000, 30000, _settings.d_disp, 10)) voxdat.append(rs) options = ["170 Hz", "200 Hz", "425 Hz", "850 Hz"] rs = RadioSetting("rty_sft", "RTTY FSK Freq Shift", RadioSettingValueList(options, options[_settings.rty_sft])) voxdat.append(rs) options = ["1275 Hz", "2125 Hz"] rs = RadioSetting("rty_ton", "RTTY FSK Mark tone", RadioSettingValueList(options, options[_settings.rty_ton])) voxdat.append(rs) options = ["Normal", "Reverse"] rs = RadioSetting("rtyrpol", "RTTY Mark/Space RX polarity", RadioSettingValueList(options, options[_settings.rtyrpol])) voxdat.append(rs) rs = RadioSetting("rtytpol", "RTTY Mark/Space TX polarity", RadioSettingValueList(options, options[_settings.rtytpol])) voxdat.append(rs) # End _do_vox_settings def _do_mic_settings(self, mic): # - - MIC Settings _settings = self._memobj.settings rs = RadioSetting("mic_eq", "Mic Equalizer", RadioSettingValueInteger(0, 9, _settings.mic_eq)) mic.append(rs) options = ["Low", "Normal", "High"] rs = RadioSetting("micgain", "Mic Gain", RadioSettingValueList(options, options[_settings.micgain])) mic.append(rs) rs = RadioSetting("micscan", "Mic scan enabled", RadioSettingValueBoolean(_settings.micscan)) rs.set_doc("Enables channel scanning via mic up/down buttons.") mic.append(rs) rs = RadioSetting("pm_dwn", "Mic Down button function", RadioSettingValueList(self.FUNC_LIST, self.FUNC_LIST[_settings.pm_dwn])) mic.append(rs) rs = RadioSetting("pm_fst", "Mic Fast button function", RadioSettingValueList(self.FUNC_LIST, self.FUNC_LIST[_settings.pm_fst])) mic.append(rs) rs = RadioSetting("pm_up", "Mic Up button function", RadioSettingValueList(self.FUNC_LIST, self.FUNC_LIST[_settings.pm_up])) mic.append(rs) # End _do_mic_settings def _do_mymodes_settings(self, mymodes): # - - MYMODES _settings = self._memobj.settings # Inverted Logic requires callback bx = not _settings.mym_lsb rs = RadioSetting("mym_lsb", "LSB", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_lsb") mymodes.append(rs) bx = not _settings.mym_usb rs = RadioSetting("mym_usb", "USB", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_usb") mymodes.append(rs) bx = not _settings.mym_cw rs = RadioSetting("mym_cw", "CW", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_cw") mymodes.append(rs) bx = not _settings.mym_am rs = RadioSetting("mym_am", "AM", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_am") mymodes.append(rs) bx = not _settings.mym_fm rs = RadioSetting("mym_fm", "FM", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_fm") mymodes.append(rs) bx = not _settings.mym_data rs = RadioSetting("mym_data", "DATA", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "mym_data") mymodes.append(rs) # End _do_mymodes_settings def _do_mybands_settings(self, mybands): # - - MYBANDS Settings _settings = self._memobj.settings # Inverted Logic requires callback bx = not _settings.myb_1_8 rs = RadioSetting("myb_1_8", "1.8 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_1_8") mybands.append(rs) bx = not _settings.myb_3_5 rs = RadioSetting("myb_3_5", "3.5 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_3_5") mybands.append(rs) bx = not _settings.myb_7 rs = RadioSetting("myb_7", "7 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_7") mybands.append(rs) bx = not _settings.myb_10 rs = RadioSetting("myb_10", "10 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_10") mybands.append(rs) bx = not _settings.myb_14 rs = RadioSetting("myb_14", "14 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_14") mybands.append(rs) bx = not _settings.myb_18 rs = RadioSetting("myb_18", "18 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_18") mybands.append(rs) bx = not _settings.myb_21 rs = RadioSetting("myb_21", "21 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_21") mybands.append(rs) bx = not _settings.myb_24 rs = RadioSetting("myb_24", "24 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_24") mybands.append(rs) bx = not _settings.myb_28 rs = RadioSetting("myb_28", "28 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_28") mybands.append(rs) bx = not _settings.myb_50 rs = RadioSetting("myb_50", "50 MHz", RadioSettingValueBoolean(bx)) rs.set_apply_callback(self._invert_me, _settings, "myb_50") mybands.append(rs) # End _do_mybands_settings def set_settings(self, settings): _settings = self._memobj.settings _mem = self._memobj 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 = _settings 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