# HG changeset patch
# User Jens Jensen <kd4tjx@yahoo.com>
# Date 1377497847 18000
# Node ID 7c95e9d39dfc07ea1e3192834d8a339e100347f6
# Parent  86910885e998d559dfdd6ba4c5fb0bf520fdee31
initial support for Yaesu FT-90R, revised #1087

diff -r 86910885e998 -r 7c95e9d39dfc chirp/ft90.py
--- /dev/null    Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/ft90.py    Mon Aug 26 01:17:27 2013 -0500
@@ -0,0 +1,379 @@
+# Copyright 2011 Dan Smith <dsmith@danplanet.com>
+# Copyright 2013 Jens Jensen <kd4tjx@yahoo.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/>.
+
+from chirp import chirp_common, bitwise, memmap, directory, errors, util, yaesu_clone
+import time, os, traceback
+
+CHIRP_DEBUG=True
+CMD_ACK = chr(0x06)
+
+@directory.register
+class FT90Radio(yaesu_clone.YaesuCloneModeRadio):
+    VENDOR = "Yaesu"
+    MODEL = "FT-90"
+    ID = "\x8E\xF6"
+   
+    STEPS = [5, 10, 12.5, 15, 20, 25, 50]
+    MODES = ["AM", "FM", "Auto"]
+    TMODES = ["", "Tone", "TSQL", "", "DTCS"] # idx 3 (Bell) not supported yet
+
+    TONES = list(chirp_common.TONES)
+    for tone in [ 165.5, 171.3, 177.3 ]:
+        TONES.remove(tone)       
+    POWER_LEVELS = ["Hi", "Mid1", "Mid2", "Low"]
+    DUPLEX = ["", "-", "+", "split"]
+       
+    _memsize = 4063
+    # block 03 (200 Bytes long) repeats 18 times; channel memories
+    _block_lengths = [ 2, 232, 24, 200, 205]
+   
+    mem_format = """
+    #seekto 0x22;   
+    struct {
+        u8    dtmf_active;
+        u8    dtmf1_len;
+        u8    dtmf2_len;
+        u8    dtmf3_len;
+        u8    dtmf4_len;
+        u8    dtmf5_len;
+        u8    dtmf6_len;
+        u8    dtmf7_len;
+        u8    dtmf8_len;
+        bbcd dtmf1[8];
+        bbcd dtmf2[8];
+        bbcd dtmf3[8];
+        bbcd dtmf4[8];           
+        bbcd dtmf5[8];
+        bbcd dtmf6[8];
+        bbcd dtmf7[8];   
+        bbcd dtmf8[8];
+        char cwid[7];
+        u8     unk1;
+        u8     unk2:2,
+            beep_dis:1,
+            unk3:1,
+            rfsqlvl:4;
+        u8    cwid_en:1,
+            unk4:3,
+            txnarrow:1,
+            dtmfspeed:1,
+            pttlock:2;
+        u8    dtmftxdelay:3,
+            fancontrol:2,
+            unk5:3;
+        u8    dimmer:3,
+            unk6:1,
+            lcdcontrast:4;
+        u8    tot;
+        u8    unk8:1,
+            ars:1,
+            lock:1,
+            txpwrsave:1,
+            apo:4;
+        u8    key_rt;
+        u8    key_lt;
+        u8    key_p1;
+        u8    key_p2;
+        u8    key_acc;
+        char    demomsg1[32];
+        char    demomsg2[32];
+       
+    } settings;
+   
+    struct mem_struct {
+      u8 mode:2,
+         isUhf1:1,
+         unknown1:2,
+         step:3;
+      u8 artsmode:2,
+         unknown2:1,
+         isUhf2:1
+         power:2,
+         shift:2;
+      u8 skip:1,
+         showname:1,
+         unknown3:1,
+         isUhfHi:1,
+         unknown4:1,
+         tmode:3;
+      u32 rxfreq;
+      u32 txfreqoffset;
+      u8 UseDefaultName:1,
+         ars:1,
+         tone:6;
+      u8 packetmode:1,
+         unknown5:1,
+         dcstone:6;
+      char name[7];
+    };
+
+    #seekto 0x86;
+    struct mem_struct vfo_v;
+    struct mem_struct call_v;
+    struct mem_struct vfo_u;
+    struct mem_struct call_u;
+   
+    #seekto 0x102;
+    struct mem_struct memory[180];
+
+    #seekto 0xf12;
+    struct mem_struct pms_1L;
+    struct mem_struct pms_1U;
+    struct mem_struct pms_2L;   
+    struct mem_struct pms_2U;
+    """
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == cls._memsize
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_ctone = False
+        rf.has_bank = False
+        rf.has_dtcs_polarity = False
+        rf.has_dtcs = True
+        rf.valid_modes = self.MODES
+        rf.valid_tmodes = self.TMODES
+        rf.valid_duplexes = self.DUPLEX
+        rf.valid_tuning_steps = self.STEPS
+        rf.valid_power_levels = self.POWER_LEVELS
+        rf.valid_name_length = 7
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_skips = ["", "S"]
+        rf.memory_bounds = (1, 180)
+        rf.valid_bands = [(100000000, 230000000),
+            (300000000, 530000000), (810000000, 999975000)]
+
+        return rf
+
+    def _read(self, blocksize, blocknum):
+        data = self.pipe.read(blocksize+2)
+       
+        # chew echo'd ack
+        self.pipe.write(CMD_ACK)
+        time.sleep(0.02)
+        self.pipe.read(1) # chew echoed ACK from 1-wire serial
+       
+        if len(data) == blocksize+2 and data[0] == chr(blocknum):
+            checksum = yaesu_clone.YaesuChecksum(1, blocksize)
+            if checksum.get_existing(data) != checksum.get_calculated(data):
+                raise Exception("Checksum Failed [%02X<>%02X] block %02X, data len: %i" %
+                                    (checksum.get_existing(data),
+                                    checksum.get_calculated(data), blocknum, len(data) ))
+            data = data[1:blocksize+1] # Chew blocknum and checksum
+           
+        else:
+            raise Exception("Unable to read blocknum %02X expected blocksize %i got %i." %
+                                (blocknum, blocksize+2, len(data)))
+
+        return data       
+   
+    def _clone_in(self):
+        # Be very patient with the radio
+        self.pipe.setTimeout(4)
+        start = time.time()
+   
+        data = ""
+        blocknum = 0
+        status = chirp_common.Status()
+        status.msg = "Cloning from radio.\nPut radio into clone mode then\npress SET to send"
+        self.status_fn(status)
+        status.max = len(self._block_lengths) + 18
+        for blocksize in self._block_lengths:
+            if blocksize == 200:
+                # repeated read of 200 block same size (memory area)
+                repeat = 18
+            else:
+                repeat = 1
+            for _i in range(0, repeat):
+                data += self._read(blocksize, blocknum)
+
+                blocknum += 1
+                status.cur = blocknum
+                self.status_fn(status)
+               
+        status.msg = "Clone completed."
+        self.status_fn(status)
+
+        print "Clone completed in %i seconds, blocks read: %i" % (time.time() - start, blocknum)
+   
+        return memmap.MemoryMap(data)
+   
+    def _clone_out(self):
+        delay = 0.2
+        start = time.time()
+   
+        blocknum = 0
+        pos = 0
+        status = chirp_common.Status()
+        status.msg = "Cloning to radio.\nPut radio into clone mode and press DISP/SS\n to start receive within 3 secs..."
+        self.status_fn(status)
+        # radio likes to have port open
+        self.pipe.open()
+        time.sleep(3)
+        status.max = len(self._block_lengths) + 18
+
+
+        for blocksize in self._block_lengths:
+            if blocksize == 200:
+                # repeat channel blocks
+                repeat = 18
+            else:
+                repeat = 1
+            for _i in range(0, repeat):
+                time.sleep(0.1)
+                checksum = yaesu_clone.YaesuChecksum(pos, pos+blocksize-1)
+                blocknumbyte = chr(blocknum)
+                payloadbytes = self.get_mmap()[pos:pos+blocksize]
+                checksumbyte = chr(checksum.get_calculated(self.get_mmap()))
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:
+                    print "Block %i - will send from %i to %i byte " % \
+                        (blocknum, pos, pos + blocksize)
+                    print util.hexprint(blocknumbyte)
+                    print util.hexprint(payloadbytes)
+                    print util.hexprint(checksumbyte)
+                # send wrapped bytes
+                self.pipe.write(blocknumbyte)
+                self.pipe.write(payloadbytes)
+                self.pipe.write(checksumbyte)
+                tmp = self.pipe.read(blocksize+2)  #chew echo
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:               
+                    print "bytes echoed: "
+                    print util.hexprint(tmp)
+                # radio is slow to write/ack:
+                time.sleep(0.9)
+                buf = self.pipe.read(1)
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:               
+                    print "ack recd:"
+                    print util.hexprint(buf)
+                if buf != CMD_ACK:
+                    raise Exception("Radio did not ack block %i" % blocknum)
+                pos += blocksize
+                blocknum += 1
+                status.cur = blocknum
+                self.status_fn(status)
+   
+        print "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:
+            trace = traceback.format_exc()
+            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
+        self.process_mmap()
+
+    def sync_out(self):
+        try:
+            self._clone_out()
+        except errors.RadioError:
+            raise
+        except Exception, e:
+            trace = traceback.format_exc()
+            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(self.mem_format, self._mmap)
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number-1]
+
+        mem = chirp_common.Memory()
+        mem.number = number
+        mem.freq = _mem.rxfreq * 10     
+        mem.offset = _mem.txfreqoffset * 10
+        if not _mem.tmode < len(self.TMODES):
+            _mem.tmode = 0
+        mem.tmode = self.TMODES[_mem.tmode]
+        mem.rtone = self.TONES[_mem.tone]
+        mem.dtcs = chirp_common.DTCS_CODES[_mem.dcstone]
+        mem.mode = self.MODES[_mem.mode]
+
+        '''
+        # ars mode note yet working...
+        # ARS mode:
+        if _mem.ars and _mem.shift == 0:
+            mem.duplex = self.DUPLEX[4]
+        else:
+            mem.duplex = self.DUPLEX[_mem.shift]
+        '''
+
+        mem.duplex = self.DUPLEX[_mem.shift]
+        mem.power = self.POWER_LEVELS[_mem.power]
+        # radio has a known bug with 5khz step and squelch
+        if _mem.step == 0:
+            _mem.step = 2
+        mem.tuning_step = self.STEPS[_mem.step]
+        mem.skip = _mem.skip and "S" or ""
+        mem.name = _mem.name
+        return mem
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number-1])
+   
+    def set_memory(self, mem):
+
+        _mem = self._memobj.memory[mem.number - 1]
+        _mem.skip = mem.skip == "S"
+        # radio has a known bug with 5khz step and dead squelch
+        if not mem.tuning_step or mem.tuning_step == self.STEPS[0]:
+            _mem.step = 2
+        else:
+            _mem.step = self.STEPS.index(mem.tuning_step)
+        _mem.rxfreq = mem.freq / 10
+        # vfo will unlock if not in right band?
+        if mem.freq > 300000000:
+            # uhf
+            _mem.isUhf1 = 1
+            _mem.isUhf2 = 1
+            if mem.freq > 810000000:
+                # uhf hiband
+                _mem.isUhfHi = 1
+            else:
+                _mem.isUhfHi = 0
+        else:
+            # vhf
+            _mem.isUhf1 = 0
+            _mem.isUhf2 = 0
+            _mem.isUhfHi = 0
+        _mem.txfreqoffset = mem.offset / 10
+        _mem.tone = self.TONES.index(mem.rtone)
+        _mem.tmode = self.TMODES.index(mem.tmode)
+        _mem.mode = self.MODES.index(mem.mode)
+        '''
+        # ars not yet working
+        # ARS mode:
+        if mem.duplex == 4:
+            _mem.shift = 0
+            _mem.ars = 1
+        else:
+            _mem.shift = self.DUPLEX.index(mem.duplex)
+        '''
+        _mem.shift = self.DUPLEX.index(mem.duplex)   
+        _mem.dcstone = chirp_common.DTCS_CODES.index(mem.dtcs)
+        if self.get_features().has_tuning_step:
+            _mem.step = self.STEPS.index(mem.tuning_step)
+        _mem.shift = self.DUPLEX.index(mem.duplex)
+        if mem.power:
+            _mem.power = self.POWER_LEVELS.index(mem.power)
+        else:
+            _mem.power = 3  # default to low power
+        _mem.name = mem.name.ljust(7)
+
+
diff -r 86910885e998 -r 7c95e9d39dfc ft90.py
--- /dev/null    Thu Jan 01 00:00:00 1970 +0000
+++ b/ft90.py    Mon Aug 26 01:17:27 2013 -0500
@@ -0,0 +1,379 @@
+# Copyright 2011 Dan Smith <dsmith@danplanet.com>
+# Copyright 2013 Jens Jensen <kd4tjx@yahoo.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/>.
+
+from chirp import chirp_common, bitwise, memmap, directory, errors, util, yaesu_clone
+import time, os, traceback
+
+CHIRP_DEBUG=True
+CMD_ACK = chr(0x06)
+
+@directory.register
+class FT90Radio(yaesu_clone.YaesuCloneModeRadio):
+    VENDOR = "Yaesu"
+    MODEL = "FT-90"
+    ID = "\x8E\xF6"
+   
+    STEPS = [5, 10, 12.5, 15, 20, 25, 50]
+    MODES = ["AM", "FM", "Auto"]
+    TMODES = ["", "Tone", "TSQL", "", "DTCS"] # idx 3 (Bell) not supported yet
+
+    TONES = list(chirp_common.TONES)
+    for tone in [ 165.5, 171.3, 177.3 ]:
+        TONES.remove(tone)       
+    POWER_LEVELS = ["Hi", "Mid1", "Mid2", "Low"]
+    DUPLEX = ["", "-", "+", "split"]
+       
+    _memsize = 4063
+    # block 03 (200 Bytes long) repeats 18 times; channel memories
+    _block_lengths = [ 2, 232, 24, 200, 205]
+   
+    mem_format = """
+    #seekto 0x22;   
+    struct {
+        u8    dtmf_active;
+        u8    dtmf1_len;
+        u8    dtmf2_len;
+        u8    dtmf3_len;
+        u8    dtmf4_len;
+        u8    dtmf5_len;
+        u8    dtmf6_len;
+        u8    dtmf7_len;
+        u8    dtmf8_len;
+        bbcd dtmf1[8];
+        bbcd dtmf2[8];
+        bbcd dtmf3[8];
+        bbcd dtmf4[8];           
+        bbcd dtmf5[8];
+        bbcd dtmf6[8];
+        bbcd dtmf7[8];   
+        bbcd dtmf8[8];
+        char cwid[7];
+        u8     unk1;
+        u8     unk2:2,
+            beep_dis:1,
+            unk3:1,
+            rfsqlvl:4;
+        u8    cwid_en:1,
+            unk4:3,
+            txnarrow:1,
+            dtmfspeed:1,
+            pttlock:2;
+        u8    dtmftxdelay:3,
+            fancontrol:2,
+            unk5:3;
+        u8    dimmer:3,
+            unk6:1,
+            lcdcontrast:4;
+        u8    tot;
+        u8    unk8:1,
+            ars:1,
+            lock:1,
+            txpwrsave:1,
+            apo:4;
+        u8    key_rt;
+        u8    key_lt;
+        u8    key_p1;
+        u8    key_p2;
+        u8    key_acc;
+        char    demomsg1[32];
+        char    demomsg2[32];
+       
+    } settings;
+   
+    struct mem_struct {
+      u8 mode:2,
+         isUhf1:1,
+         unknown1:2,
+         step:3;
+      u8 artsmode:2,
+         unknown2:1,
+         isUhf2:1
+         power:2,
+         shift:2;
+      u8 skip:1,
+         showname:1,
+         unknown3:1,
+         isUhfHi:1,
+         unknown4:1,
+         tmode:3;
+      u32 rxfreq;
+      u32 txfreqoffset;
+      u8 UseDefaultName:1,
+         ars:1,
+         tone:6;
+      u8 packetmode:1,
+         unknown5:1,
+         dcstone:6;
+      char name[7];
+    };
+
+    #seekto 0x86;
+    struct mem_struct vfo_v;
+    struct mem_struct call_v;
+    struct mem_struct vfo_u;
+    struct mem_struct call_u;
+   
+    #seekto 0x102;
+    struct mem_struct memory[180];
+
+    #seekto 0xf12;
+    struct mem_struct pms_1L;
+    struct mem_struct pms_1U;
+    struct mem_struct pms_2L;   
+    struct mem_struct pms_2U;
+    """
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == cls._memsize
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_ctone = False
+        rf.has_bank = False
+        rf.has_dtcs_polarity = False
+        rf.has_dtcs = True
+        rf.valid_modes = self.MODES
+        rf.valid_tmodes = self.TMODES
+        rf.valid_duplexes = self.DUPLEX
+        rf.valid_tuning_steps = self.STEPS
+        rf.valid_power_levels = self.POWER_LEVELS
+        rf.valid_name_length = 7
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_skips = ["", "S"]
+        rf.memory_bounds = (1, 180)
+        rf.valid_bands = [(100000000, 230000000),
+            (300000000, 530000000), (810000000, 999975000)]
+
+        return rf
+
+    def _read(self, blocksize, blocknum):
+        data = self.pipe.read(blocksize+2)
+       
+        # chew echo'd ack
+        self.pipe.write(CMD_ACK)
+        time.sleep(0.02)
+        self.pipe.read(1) # chew echoed ACK from 1-wire serial
+       
+        if len(data) == blocksize+2 and data[0] == chr(blocknum):
+            checksum = yaesu_clone.YaesuChecksum(1, blocksize)
+            if checksum.get_existing(data) != checksum.get_calculated(data):
+                raise Exception("Checksum Failed [%02X<>%02X] block %02X, data len: %i" %
+                                    (checksum.get_existing(data),
+                                    checksum.get_calculated(data), blocknum, len(data) ))
+            data = data[1:blocksize+1] # Chew blocknum and checksum
+           
+        else:
+            raise Exception("Unable to read blocknum %02X expected blocksize %i got %i." %
+                                (blocknum, blocksize+2, len(data)))
+
+        return data       
+   
+    def _clone_in(self):
+        # Be very patient with the radio
+        self.pipe.setTimeout(4)
+        start = time.time()
+   
+        data = ""
+        blocknum = 0
+        status = chirp_common.Status()
+        status.msg = "Cloning from radio.\nPut radio into clone mode then\npress SET to send"
+        self.status_fn(status)
+        status.max = len(self._block_lengths) + 18
+        for blocksize in self._block_lengths:
+            if blocksize == 200:
+                # repeated read of 200 block same size (memory area)
+                repeat = 18
+            else:
+                repeat = 1
+            for _i in range(0, repeat):
+                data += self._read(blocksize, blocknum)
+
+                blocknum += 1
+                status.cur = blocknum
+                self.status_fn(status)
+               
+        status.msg = "Clone completed."
+        self.status_fn(status)
+
+        print "Clone completed in %i seconds, blocks read: %i" % (time.time() - start, blocknum)
+   
+        return memmap.MemoryMap(data)
+   
+    def _clone_out(self):
+        delay = 0.2
+        start = time.time()
+   
+        blocknum = 0
+        pos = 0
+        status = chirp_common.Status()
+        status.msg = "Cloning to radio.\nPut radio into clone mode and press DISP/SS\n to start receive within 3 secs..."
+        self.status_fn(status)
+        # radio likes to have port open
+        self.pipe.open()
+        time.sleep(3)
+        status.max = len(self._block_lengths) + 18
+
+
+        for blocksize in self._block_lengths:
+            if blocksize == 200:
+                # repeat channel blocks
+                repeat = 18
+            else:
+                repeat = 1
+            for _i in range(0, repeat):
+                time.sleep(0.1)
+                checksum = yaesu_clone.YaesuChecksum(pos, pos+blocksize-1)
+                blocknumbyte = chr(blocknum)
+                payloadbytes = self.get_mmap()[pos:pos+blocksize]
+                checksumbyte = chr(checksum.get_calculated(self.get_mmap()))
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:
+                    print "Block %i - will send from %i to %i byte " % \
+                        (blocknum, pos, pos + blocksize)
+                    print util.hexprint(blocknumbyte)
+                    print util.hexprint(payloadbytes)
+                    print util.hexprint(checksumbyte)
+                # send wrapped bytes
+                self.pipe.write(blocknumbyte)
+                self.pipe.write(payloadbytes)
+                self.pipe.write(checksumbyte)
+                tmp = self.pipe.read(blocksize+2)  #chew echo
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:               
+                    print "bytes echoed: "
+                    print util.hexprint(tmp)
+                # radio is slow to write/ack:
+                time.sleep(0.9)
+                buf = self.pipe.read(1)
+                if os.getenv("CHIRP_DEBUG") or CHIRP_DEBUG:               
+                    print "ack recd:"
+                    print util.hexprint(buf)
+                if buf != CMD_ACK:
+                    raise Exception("Radio did not ack block %i" % blocknum)
+                pos += blocksize
+                blocknum += 1
+                status.cur = blocknum
+                self.status_fn(status)
+   
+        print "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:
+            trace = traceback.format_exc()
+            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
+        self.process_mmap()
+
+    def sync_out(self):
+        try:
+            self._clone_out()
+        except errors.RadioError:
+            raise
+        except Exception, e:
+            trace = traceback.format_exc()
+            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(self.mem_format, self._mmap)
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number-1]
+
+        mem = chirp_common.Memory()
+        mem.number = number
+        mem.freq = _mem.rxfreq * 10     
+        mem.offset = _mem.txfreqoffset * 10
+        if not _mem.tmode < len(self.TMODES):
+            _mem.tmode = 0
+        mem.tmode = self.TMODES[_mem.tmode]
+        mem.rtone = self.TONES[_mem.tone]
+        mem.dtcs = chirp_common.DTCS_CODES[_mem.dcstone]
+        mem.mode = self.MODES[_mem.mode]
+
+        '''
+        # ars mode note yet working...
+        # ARS mode:
+        if _mem.ars and _mem.shift == 0:
+            mem.duplex = self.DUPLEX[4]
+        else:
+            mem.duplex = self.DUPLEX[_mem.shift]
+        '''
+
+        mem.duplex = self.DUPLEX[_mem.shift]
+        mem.power = self.POWER_LEVELS[_mem.power]
+        # radio has a known bug with 5khz step and squelch
+        if _mem.step == 0:
+            _mem.step = 2
+        mem.tuning_step = self.STEPS[_mem.step]
+        mem.skip = _mem.skip and "S" or ""
+        mem.name = _mem.name
+        return mem
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number-1])
+   
+    def set_memory(self, mem):
+
+        _mem = self._memobj.memory[mem.number - 1]
+        _mem.skip = mem.skip == "S"
+        # radio has a known bug with 5khz step and dead squelch
+        if not mem.tuning_step or mem.tuning_step == self.STEPS[0]:
+            _mem.step = 2
+        else:
+            _mem.step = self.STEPS.index(mem.tuning_step)
+        _mem.rxfreq = mem.freq / 10
+        # vfo will unlock if not in right band?
+        if mem.freq > 300000000:
+            # uhf
+            _mem.isUhf1 = 1
+            _mem.isUhf2 = 1
+            if mem.freq > 810000000:
+                # uhf hiband
+                _mem.isUhfHi = 1
+            else:
+                _mem.isUhfHi = 0
+        else:
+            # vhf
+            _mem.isUhf1 = 0
+            _mem.isUhf2 = 0
+            _mem.isUhfHi = 0
+        _mem.txfreqoffset = mem.offset / 10
+        _mem.tone = self.TONES.index(mem.rtone)
+        _mem.tmode = self.TMODES.index(mem.tmode)
+        _mem.mode = self.MODES.index(mem.mode)
+        '''
+        # ars not yet working
+        # ARS mode:
+        if mem.duplex == 4:
+            _mem.shift = 0
+            _mem.ars = 1
+        else:
+            _mem.shift = self.DUPLEX.index(mem.duplex)
+        '''
+        _mem.shift = self.DUPLEX.index(mem.duplex)   
+        _mem.dcstone = chirp_common.DTCS_CODES.index(mem.dtcs)
+        if self.get_features().has_tuning_step:
+            _mem.step = self.STEPS.index(mem.tuning_step)
+        _mem.shift = self.DUPLEX.index(mem.duplex)
+        if mem.power:
+            _mem.power = self.POWER_LEVELS.index(mem.power)
+        else:
+            _mem.power = 3  # default to low power
+        _mem.name = mem.name.ljust(7)
+
+