[chirp_devel] New FT-405D Driver

Rick DeWitt
Mon Jun 4 10:54:37 PDT 2018


Attached is the patch file for the new Yaesu FT-450D driver.
Please let me know what fails if it doesn't pass the style check, I 
scrubbed it as well as I can with Windows and Notepad++ and I am trying 
to keep notes of what fails.
Ditto for the patch file format.
Thanks!

-- 
Rick DeWitt
AA0RD
Sequim, Washington, USA
360-681-3494

-------------- next part --------------
# HG changeset patch
# Parent  0cbedd969bad9e0635e58d138266add5edbf2779
New Yaesu FT-450D Driver

Submitting new driver in support of issues #0951, #3427 and #3621
user: Rick DeWitt <aa0rd at yahoo.com>
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 at yahoo.com>
+# Adapted from FT817 driver by Filippi Marco <iz3gme.marco at gmail.com>
+#
+# 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, \
+    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)
+
+ at 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 &lt;] and [MODE &gt;] keys while
+                 turning the radio on ("CLONE MODE" will appear on the
+                 display).
+            4. <b>After clicking OK</b>, 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 &lt;] and [MODE &gt;] 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


More information about the chirp_devel mailing list