Project

General

Profile

Bug #10909 » ft450d.py

William Curlew, 10/24/2023 03:24 PM

 
# Copyright 2018 by Rick DeWitt (aa0rd@yahoo.com) V2.0
# PY3 Compliant release.
# Thanks to Filippi Marco <iz3gme.marco@gmail.com> 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 <http://www.gnu.org/licenses/>.

"""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 &lt;] and [MODE &gt;] keys"
" while\n"
" turning the radio on (\"CLONE MODE\" will appear on the\n"
" display).\n"
"4. <b>After clicking OK</b> 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 &lt;] and [MODE &gt;] 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
(7-7/8)