# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com> # 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, \ RadioSettingValueFloat, RadioSettings import time import logging from textwrap import dedent LOG = logging.getLogger(__name__) CMD_ACK = 0x06 # TBD: Enable some form of generated UI field, for the memory tags # That field wiould not be stored in img file, but generated in get_memory MEM_GRP_LBL = False # To ignore Comment channel-tags for now EX_MODES = ["USER-L", "USER-U", "LSB+CW", "USB+CW", "RTTY-L", "RTTY-U", "N/A"] for i in EX_MODES: chirp_common.MODES.append(i) 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" 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("\\") MEM_SIZE = 15017 # block 9 (135 Bytes long) is to be repeated 101 times _block_lengths = [4, 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 0x04; 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: 24Mhz 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 0x58; 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 0x343; // chan status u8 visible[63]; // 1 bit per channel u8 pmsvisible; // @ 0x382 #seekto 0x383; u8 filled[63]; u8 pmsfilled; // @ 0x3c2 #seekto 0x3C3; struct mem_struct memory[500]; struct mem_struct pms[4]; // Programed Scan limits @ x387F #seekto 0x3906; struct { char t1[40]; // CW Beacon Text char t2[40]; char t3[40]; } beacontext; // to 0x397E #seekto 0x3985; struct mem_struct m60[5]; // to 0x3A0B #seekto 0x03a45; struct mem_struct current; """ _CALLSIGN_CHARSET = [chr(x) for x in range(ord("0"), ord("9") + 1) + 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 = _(dedent(""" The FT-450 radio driver loads the 'Special Channels' tab with the PMS scanning range memories (group 11), 60meter channels (group 12), the QMB (STO/RCL) memory, the HF and 50m HOME memories and all the A and B VFO memories. There are VFO memories for the last frequency dialed in each band. The last mem-tune config is also stored. These Special Channels allow limited field editting. This driver also populates the 'Other' tab in the channel memory Properties window. This tab contains values for those channel memory settings that don't fall under the standard Chirp display columns. """)) rp.pre_download = _(dedent("""\ 1. Turn radio off. 2. Connect cable to ACC jack. 3. Press and hold in the [MODE <] and [MODE >] keys while turning the radio on ("CLONE MODE" will appear on the display). 4. After clicking OK here, press the [C.S.] key to send image.""")) rp.pre_upload = _(dedent("""\ 1. Turn radio off. 2. Connect cable to ACC jack. 3. Press and hold in the [MODE <] and [MODE >] keys while turning the radio on ("CLONE MODE" will appear on the display). 4. Click OK here. ("Receiving" will appear on the LCD).""")) 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] == chr(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 = "" 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(chr(CMD_ACK)) blocks += 1 status.cur = blocks self.status_fn(status) data += self.MODEL # Append ID return memmap.MemoryMap(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) self.pipe.write(chr(blocks)) self.pipe.write(self.get_mmap()[pos:pos + block]) self.pipe.write(chr(checksum.get_calculated(self.get_mmap()))) buf = self.pipe.read(1) if not buf or buf[0] != chr(CMD_ACK): time.sleep(delay) buf = self.pipe.read(1) if not buf or buf[0] != chr(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, 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, 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 if MEM_GRP_LBL: rf.has_comment = True # Used for Mem-Grp number rf.valid_modes = list(set(self.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 loosing 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 mx = (-self.LAST_PMS_INDEX) + mem.number _mem = self._memobj.pms[mx] mx = mx + 1 if MEM_GRP_LBL: mem.comment = "M-11-%02i" % mx 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 if MEM_GRP_LBL: mem.comment = "M-12-%02i" % mx 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 Exception("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 Exception("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_GRP_LBL: mgrp = int((number - 1) / 50) mem.comment = "M-%02i-%02i" % (mgrp + 1, number - (mgrp * 50)) 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 mem.mode = self.MODES[vx] 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.8kHz", "2.4kHz", "3.0kHz"] vx = _mem.sb_width stx = "sb_width" elif mem.mode[:1] == "CW": options = ["300Hz", "500 kHz", "2.4kHz"] vx = _mem.cw_width stx = "cw_width" elif mem.mode[:4] == "USER" or mem.mode[:4] == "RTTY": options = ["300Hz", "2.4kHz", "3.0kHz"] vx = _mem.sb_width stx = "sb_width" elif mem.mode == "AM": options = ["3.0kHz", "6.0kHz", "9.0 kHz"] vx = _mem.am_width stx = "am_width" else: options = ["2.5kHz", "5.0kHz"] 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 # Overide 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): 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[:knt]: stx += chr(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) rs = RadioSetting("apo", "APO time (Hrs)", RadioSettingValueInteger(1, 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.5KHz", 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 = ["440Hz", "880Hz", "1760Hz"] 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", "100KHz step", "1MHz 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 = ["170Hz", "200Hz", "425Hz", "850Hz"] rs = RadioSetting("rty_sft", "RTTY FSK Freq Shift", RadioSettingValueList(options, options[_settings.rty_sft])) voxdat.append(rs) options = ["1275Hz", "2125Hz"] 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, e: LOG.debug(element.get_name()) raise