# HG changeset patch # Parent 0cbedd969bad9e0635e58d138266add5edbf2779 New Yaesu FT-450D Driver Submitting new driver in support of issues #0951, #3427 and #3621 user: Rick DeWitt branch 'default' added chirp/drivers/ft450d.py diff -r 0cbedd969bad -r c2f84de00484 chirp/drivers/ft450d.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/drivers/ft450d.py Mon Jun 04 10:39:30 2018 -0700 @@ -0,0 +1,1481 @@ +# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com> +# Adapted from FT817 driver by Filippi Marco +# +# 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, + chirp_common.ExperimentalRadio): + """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 comment; // @ 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.experimental = ("The FT-450D driver supports 'Special Channels' " + "and the 'Other' tab of memory properties.") + 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, 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. + ("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) + 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)) + # Chew away the block number and the checksum + data = data[1:block + 1] + else: + raise Exception("Unable to read block %i expected %i got %i" + % (blocknum, block + 2, len(data))) + +# LOG.warning("Block %i: %i bytes." % ( blocknum,len(data))) #RJD@@@ + 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 + status.max = nblocks + for block in self._block_lengths: + if blocks == 8: + # repeated read of 101 block same size (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) + + status.msg = _("Clone completed, checking for spurious bytes") + self.status_fn(status) + moredata = self.pipe.read(2) + if moredata: + raise Exception( + _("Radio sent data after the last awaited block.")) + data += self.MODEL # Append ID + LOG.info("Clone completed in %i seconds" % (time.time() - start)) + + 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) + LOG.debug("Block %i - will send from %i to %i byte " % + (blocks, pos, pos + block)) + LOG.debug(util.hexprint(chr(blocks))) + LOG.debug(util.hexprint(self.get_mmap()[pos:pos + block])) + LOG.debug(util.hexprint(chr(checksum.get_calculated( + self.get_mmap())))) + 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): + LOG.debug(util.hexprint(buf)) + raise Exception(_("Radio did not ack block %i") % blocks) + pos += block + blocks += 1 + status.cur = blocks + self.status_fn(status) + + LOG.info("Clone completed in %i seconds" % (time.time() - start)) + + 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.has_nostep_tuning = True + rf.valid_modes = list(set(self.MODES)) + rf.valid_tmodes = list(self.TMODES) + rf.valid_duplexes = list(self.DUPLEX) +# rf.valid_tuning_steps = list(self.STEPSFM) + 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", "comment"] + 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", "comment"] + elif mem.number in range(-4, -6, -1): # 2 Home Chans + _mem = self._memobj.home[5 + mem.number] + immutable = ["number", "extd_number", + "name", "power", "comment"] + elif mem.number == -3: + _mem = self._memobj.qmb + immutable = ["number", "extd_number", + "name", "power", "comment"] + elif mem.number == -2: + _mem = self._memobj.mtqmb + immutable = ["number", "extd_number", + "name", "power", "comment"] + elif mem.number == -1: + _mem = self._memobj.mtune + immutable = ["number", "extd_number", + "name", "power", "comment"] + 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", "comment"] + 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", "comment"] + else: + raise Exception("Sorry, special memory index %i " % mem.number + + "unknown you hit a bug!!") + + 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]) + + # TODO add frequency range check for vfo and home memories + 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, special memory index %i " % mem.number + + "unknown you hit a bug!!") + + 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: + # as Dan says "yaesus are not good about that :(" + # if you upload an empty image you can brick your radio + 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 + _mem.comment = 0 # Gotta have an object + + 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) +# LOG.warning("Chn: %i, Dnr_val set: %s, Value: %i" +# % (mem.number, stx, vx)) + 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 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) + + def invert_me(setting, obj, atrb): + """Callback: from inverted logic 1-bit booleans""" + invb = not setting.value + setattr(obj, atrb, invb) + return + +# - - - Gen --- + rs = RadioSetting("ext_mnu", "Extended menu", + RadioSettingValueBoolean(_settings.ext_mnu)) + rs.set_doc("Enables access to extended settings in the radio") + gen.append(rs) + + rs = RadioSetting("apo", "APO time (Hrs)", + RadioSettingValueInteger(1, 12, _settings.apo)) + gen.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])) + gen.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(invert_me, _settings, "cat_rts") + gen.append(rs) + + options = ["0", "100ms", "1000ms", "3000ms"] + rs = RadioSetting("cat_tot", "CAT Timeout", + RadioSettingValueList(options, + options[_settings.cat_tot])) + gen.append(rs) + + options = ["4800", "9600", "19200", "38400", "Data"] + rs = RadioSetting("catrate", "CAT rate", + RadioSettingValueList(options, + options[_settings.catrate])) + gen.append(rs) + + rs = RadioSetting("mem_grp", "Mem groups", + RadioSettingValueBoolean(_settings.mem_grp)) + gen.append(rs) + + rs = RadioSetting("scn_res", "Resume scan (secs)", + RadioSettingValueInteger(0, 10, _settings.scn_res)) + gen.append(rs) + + rs = RadioSetting("clk_sft", "CPU clock shift", + RadioSettingValueBoolean(_settings.clk_sft)) + gen.append(rs) + + rs = RadioSetting("split", "TX/RX Frequency Split", + RadioSettingValueBoolean(_settings.split)) + gen.append(rs) + + rs = RadioSetting("qspl_f", "Quick-Split freq offset (KHz)", + RadioSettingValueInteger(-20, 20, _settings.qspl_f)) + gen.append(rs) + + rs = RadioSetting("emergen", "Alaska Emergency Mem 5167.5KHz", + RadioSettingValueBoolean(_settings.emergen)) + gen.append(rs) + + rs = RadioSetting("stby_beep", "PTT release 'Standby' beep", + RadioSettingValueBoolean(_settings.stby_beep)) + gen.append(rs) + + options = ["ATAS", "EXT ATU", "INT ATU", "INTRATU", "F-TRANS"] + rs = RadioSetting("tuner", "Antenna Tuner", + RadioSettingValueList(options, + options[_settings.tuner])) + gen.append(rs) + + rs = RadioSetting("rfpower", "RF power (watts)", + RadioSettingValueInteger(5, 100, _settings.rfpower)) + gen.append(rs) + +# - - - CW - - - + 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) + + def chars2str(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(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 + + rs = RadioSetting("beacon_time", "CW beacon Tx interval (secs)", + RadioSettingValueInteger(0, 255, + _settings.beacon_time)) + cw.append(rs) + + tmp = chars2str(_beacon.t1, 40) + rs=RadioSetting("t1", "CW Beacon Line 1", + RadioSettingValueString(0, 40, tmp)) + rs.set_apply_callback(my_str2ary, _beacon, "t1", 40) + cw.append(rs) + + tmp = chars2str(_beacon.t2, 40) + rs=RadioSetting("t2", "CW Beacon Line 2", + RadioSettingValueString(0, 40, tmp)) + rs.set_apply_callback(my_str2ary, _beacon, "t2", 40) + cw.append(rs) + + tmp = chars2str(_beacon.t3, 40) + rs=RadioSetting("t3", "CW Beacon Line 3", + RadioSettingValueString(0, 40, tmp)) + rs.set_apply_callback(my_str2ary, _beacon, "t3", 40) + cw.append(rs) + +# - - - Panel settings & config - - - + bx = not _settings.amfmdial # Convert from Enable=0 + rs = RadioSetting("amfmdial", "AM&FM Dial", + RadioSettingValueBoolean(bx)) + rs.set_apply_callback(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) + + 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) + + options = ["Frequencies", "Panel", "All"] + rs = RadioSetting("lockmod", "Lock Mode", + RadioSettingValueList(options, + options[_settings.lockmod])) + pnlset.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) + + 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) + + +# - - VOX and DATA - - - + 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) + +# - - MIC - - - - + 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) + +# - - MYMODES - - - Inverted Logic, requires callback + bx = not _settings.mym_lsb + rs = RadioSetting("mym_lsb", "LSB", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_lsb") + mymodes.append(rs) + + bx = not _settings.mym_usb + rs = RadioSetting("mym_usb", "USB", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_usb") + mymodes.append(rs) + + bx = not _settings.mym_cw + rs = RadioSetting("mym_cw", "CW", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_cw") + mymodes.append(rs) + + bx = not _settings.mym_am + rs = RadioSetting("mym_am", "AM", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_am") + mymodes.append(rs) + + bx = not _settings.mym_fm + rs = RadioSetting("mym_fm", "FM", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_fm") + mymodes.append(rs) + + bx = not _settings.mym_data + rs = RadioSetting("mym_data", "DATA", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(invert_me, _settings, "mym_data") + mymodes.append(rs) + +# - - MYBANDS - - - Inverted Logic, requires callback + bx = not _settings.myb_1_8 + rs = RadioSetting("myb_1_8", "1.8 MHz", RadioSettingValueBoolean(bx)) + rs.set_apply_callback(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(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(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(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(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(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(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(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(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(invert_me, _settings, "myb_50") + mybands.append(rs) + + return top + + 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 \ No newline at end of file