[hamradio-commits] [chirp] 01/06: Imported Upstream version 20160402

Iain R. Learmonth irl at moszumanska.debian.org
Mon Apr 4 19:03:02 UTC 2016


This is an automated email from the git hooks/post-receive script.

irl pushed a commit to branch master
in repository chirp.

commit 4d45d6099769e8b066547196128fce26e76f7f6b
Author: Iain R. Learmonth <irl at debian.org>
Date:   Mon Apr 4 18:47:25 2016 +0100

    Imported Upstream version 20160402
---
 PKG-INFO                                  |    2 +-
 chirp/__init__.py                         |    2 +-
 chirp/chirp_common.py                     |    3 +-
 chirp/drivers/anytone.py                  |    2 +-
 chirp/drivers/anytone_ht.py               |    2 +-
 chirp/drivers/btech.py                    | 1000 +++++++++++++++++++++++++++++
 chirp/drivers/fd268.py                    |    6 +-
 chirp/drivers/ft2800.py                   |    2 +-
 chirp/drivers/ft2900.py                   |  607 ++++++++++++++++-
 chirp/drivers/ft50.py                     |  623 +++++++++++++++++-
 chirp/drivers/ft50_ll.py                  |  319 ---------
 chirp/drivers/ft60.py                     |   28 +
 chirp/drivers/ft817.py                    |    7 +-
 chirp/drivers/ft90.py                     |    2 +-
 chirp/drivers/ftm350.py                   |    4 +-
 chirp/drivers/ic9x.py                     |    2 +-
 chirp/drivers/icomciv.py                  |   78 ++-
 chirp/drivers/kenwood_live.py             |    2 +-
 chirp/drivers/kguv8d.py                   |  197 ++++--
 chirp/drivers/puxing.py                   |    2 +-
 chirp/drivers/th9000.py                   |    2 +-
 chirp/drivers/th9800.py                   |    2 +-
 chirp/drivers/th_uv3r.py                  |    2 +-
 chirp/drivers/th_uv3r25.py                |    2 +-
 chirp/drivers/th_uvf8d.py                 |    4 +-
 chirp/drivers/thd72.py                    |  217 ++++++-
 chirp/drivers/thuv1f.py                   |    2 +-
 chirp/drivers/{tk760.py => tk270.py}      |  597 +++++++++--------
 chirp/drivers/tk760.py                    |  115 ++--
 chirp/drivers/tk760g.py                   |  233 +++++--
 chirp/drivers/tk8102.py                   |    4 +-
 chirp/drivers/ts2000.py                   |    2 +-
 chirp/drivers/uv5r.py                     |   31 +-
 chirp/drivers/uvb5.py                     |    2 +-
 chirp/drivers/vxa700.py                   |    4 +-
 chirp/ui/mainapp.py                       |   19 +-
 chirpw                                    |    1 +
 locale/es_ES/LC_MESSAGES/CHIRP.mo         |  Bin 0 -> 15564 bytes
 stock_configs/NOAA Weather Alert.csv      |   19 +-
 stock_configs/US CA Railroad Channels.csv |  187 ++++++
 40 files changed, 3501 insertions(+), 834 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 599b43a..87068e2 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: chirp
-Version: daily-20160110
+Version: daily-20160402
 Summary: UNKNOWN
 Home-page: UNKNOWN
 Author: UNKNOWN
diff --git a/chirp/__init__.py b/chirp/__init__.py
index 9121671..181425a 100644
--- a/chirp/__init__.py
+++ b/chirp/__init__.py
@@ -17,7 +17,7 @@ import os
 import sys
 from glob import glob
 
-CHIRP_VERSION="daily-20160110"
+CHIRP_VERSION="daily-20160402"
 
 module_dir = os.path.dirname(sys.modules["chirp"].__file__)
 __all__ = []
diff --git a/chirp/chirp_common.py b/chirp/chirp_common.py
index 038ec01..46e9453 100644
--- a/chirp/chirp_common.py
+++ b/chirp/chirp_common.py
@@ -68,7 +68,8 @@ CROSS_MODES = [
 ]
 
 MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY",
-         "DIG", "PKT", "NCW", "NCWR", "CWR", "P25", "Auto"]
+         "DIG", "PKT", "NCW", "NCWR", "CWR", "P25", "Auto", "RTTYR",
+         "FSK", "FSKR"]
 
 TONE_MODES = [
     "",
diff --git a/chirp/drivers/anytone.py b/chirp/drivers/anytone.py
index b80099c..e99a25d 100644
--- a/chirp/drivers/anytone.py
+++ b/chirp/drivers/anytone.py
@@ -200,7 +200,7 @@ valid_model = ['QX588UV', 'HR-2040', 'DB-50M\x00', 'DB-750X']
 
 
 def _ident(radio):
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     _echo_write(radio, "PROGRAM")
     response = radio.pipe.read(3)
     if response != "QX\x06":
diff --git a/chirp/drivers/anytone_ht.py b/chirp/drivers/anytone_ht.py
index 5029620..faa9bb8 100644
--- a/chirp/drivers/anytone_ht.py
+++ b/chirp/drivers/anytone_ht.py
@@ -256,7 +256,7 @@ valid_model = ['TERMN8R', 'OBLTR8R']
 
 
 def _ident(radio):
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     _echo_write(radio, "PROGRAM")
     response = radio.pipe.read(3)
     if response != "QX\x06":
diff --git a/chirp/drivers/btech.py b/chirp/drivers/btech.py
new file mode 100644
index 0000000..67ed1f3
--- /dev/null
+++ b/chirp/drivers/btech.py
@@ -0,0 +1,1000 @@
+# Copyright 2016:
+# * Pavel Milanes CO7WT, <pavelmc at gmail.com>
+# * Jim Unroe KC9HI, <rock.unroe 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 2 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/>.
+
+import time
+import struct
+import logging
+
+LOG = logging.getLogger(__name__)
+
+from chirp import chirp_common, directory, memmap
+from chirp import bitwise, errors, util
+from chirp.settings import RadioSettingGroup, RadioSetting, \
+    RadioSettingValueBoolean, RadioSettingValueList, \
+    RadioSettingValueString, RadioSettingValueInteger, \
+    RadioSettings
+from textwrap import dedent
+
+MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+  lbcd rxfreq[4];
+  lbcd txfreq[4];
+  ul16 rxtone;
+  ul16 txtone;
+  u8 unknown0:4,
+     scode:4;
+  u8 unknown1:2,
+     spmute:1,
+     unknown2:3,
+     optsig:2;
+  u8 unknown3:3,
+     scramble:1,
+     unknown4:3,
+     power:1;
+  u8 unknown5:1,
+     wide:1,
+     unknown6:2,
+     bcl:1,
+     add:1,
+     pttid:2;
+} memory[200];
+
+#seekto 0x1000;
+struct {
+  char name[6];
+  u8 unknown1[10];
+} names[200];
+
+#seekto 0x3C90;
+struct {
+  u8 vhf_low[3];
+  u8 vhf_high[3];
+  u8 uhf_low[3];
+  u8 uhf_high[3];
+} ranges;
+
+// the 2501+220 has a different zone for storing ranges
+
+#seekto 0x3CD0;
+struct {
+  u8 vhf_low[3];
+  u8 vhf_high[3];
+  u8 unknown1[4];
+  u8 unknown2[6];
+  u8 vhf2_low[3];
+  u8 vhf2_high[3];
+  u8 unknown3[4];
+  u8 unknown4[6];
+  u8 uhf_low[3];
+  u8 uhf_high[3];
+} ranges220;
+
+"""
+
+# A note about the memmory in these radios
+#
+# The real memory of these radios extends to 0x4000
+# On read the factory software only uses up to 0x3200
+# On write it just uploads the contents up to 0x3100
+#
+# The mem beyond 0x3200 holds the ID data
+
+MEM_SIZE = 0x4000
+BLOCK_SIZE = 0x40
+TX_BLOCK_SIZE = 0x10
+ACK_CMD = "\x06"
+MODES = ["FM", "NFM"]
+SKIP_VALUES = ["S", ""]
+TONES = chirp_common.TONES
+DTCS = sorted(chirp_common.DTCS_CODES + [645])
+NAME_LENGTH = 6
+PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
+PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
+OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
+
+# This is a general serial timeout for all serial read functions.
+# Practice has show that about 0.7 sec will be enough to cover all radios.
+STIMEOUT = 0.7
+
+# this var controls the verbosity in the debug and by default it's low (False)
+# make it True and you will to get a very verbose debug.log
+debug = False
+
+# Power Levels
+NORMAL_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=25),
+                       chirp_common.PowerLevel("Low", watts=10)]
+UV5001_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=50),
+                       chirp_common.PowerLevel("Low", watts=10)]
+
+# this must be defined globaly
+POWER_LEVELS = None
+
+# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
+VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
+    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
+
+
+##### ID strings #####################################################
+
+# BTECH UV2501 pre-production units
+UV2501pp_fp = "M2C294"
+# BTECH UV2501 pre-production units 2 + and 1st Gen radios
+UV2501pp2_fp = "M29204"
+# B-TECH UV-2501 second generation (2G) radios
+UV2501G2_fp = "BTG214"
+
+
+# B-TECH UV-2501+220 pre-production units
+UV2501_220pp_fp = "M3C281"
+# extra block read for the 2501+220 pre-production units
+UV2501_220pp_id = "      280528"
+# B-TECH UV-2501+220
+UV2501_220_fp = "M3G201"
+# extra block read for the 2501+220
+# the extra block is the same as the pp unit
+
+
+# B-TECH UV-5001 pre-production units + 1st Gen radios
+UV5001pp_fp = "V19204"
+# B-TECH UV-5001 alpha units
+UV5001alpha_fp = "V28204"
+# B-TECH UV-5001 second generation (2G) radios
+# !!!! This is the same as the UV-2501 (2G) Radios !!!!
+UV5001G2_fp = "BTG214"
+# B-TECH UV-5001 second generation (2G2)
+UV5001G22_fp = "V2G204"
+
+
+# WACCOM Mini-8900
+MINI8900_fp = "M28854"
+
+
+# QYT KT-UV980 & JetStream JT2705M
+KTUV980_fp = "H28854"
+
+
+# QYT KT8900 & Juentai JT-6188
+KT8900_fp = "M29154"
+# this radio has an extra ID
+KT8900_id = "      303688"
+
+
+#### MAGICS
+# for the Waccom Mini-8900
+MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
+# for the B-TECH UV-2501+220 (including pre production ones)
+MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
+# for the QYT KT8900
+MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
+# magic string for all other models
+MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
+
+
+def _clean_buffer(radio):
+    """Cleaning the read serial buffer, hard timeout to survive an infinite
+    data stream"""
+
+    # touching the serial timeout to optimize the flushing
+    # restored at the end to the default value
+    radio.pipe.setTimeout(0.1)
+    dump = "1"
+    datacount = 0
+
+    try:
+        while len(dump) > 0:
+            dump = radio.pipe.read(100)
+            datacount += len(dump)
+            # hard limit to survive a infinite serial data stream
+            # 5 times bigger than a normal rx block (69 bytes)
+            if datacount > 345:
+                seriale = "Please check your serial port selection."
+                raise errors.RadioError(seriale)
+
+        # restore the default serial timeout
+        radio.pipe.setTimeout(STIMEOUT)
+
+    except Exception:
+        raise errors.RadioError("Unknown error cleaning the serial buffer")
+
+
+def _rawrecv(radio, amount):
+    """Raw read from the radio device, less intensive way"""
+
+    data = ""
+
+    try:
+        data = radio.pipe.read(amount)
+
+        # DEBUG
+        if debug is True:
+            LOG.debug("<== (%d) bytes:\n\n%s" %
+                      (len(data), util.hexprint(data)))
+
+        # fail if no data is received
+        if len(data) == 0:
+            raise errors.RadioError("No data received from radio")
+
+        # notice on the logs if short
+        if len(data) < amount:
+            LOG.warn("Short reading %d bytes from the %d requested." %
+                     (len(data), amount))
+
+    except:
+        raise errors.RadioError("Error reading data from radio")
+
+    return data
+
+
+def _send(radio, data):
+    """Send data to the radio device"""
+
+    try:
+        for byte in data:
+            radio.pipe.write(byte)
+
+        # DEBUG
+        if debug is True:
+            LOG.debug("==> (%d) bytes:\n\n%s" %
+                      (len(data), util.hexprint(data)))
+    except:
+        raise errors.RadioError("Error sending data to radio")
+
+
+def _make_frame(cmd, addr, length, data=""):
+    """Pack the info in the headder format"""
+    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
+    # add the data if set
+    if len(data) != 0:
+        frame += data
+
+    return frame
+
+
+def _recv(radio, addr):
+    """Get data from the radio all at once to lower syscalls load"""
+
+    # Get the full 69 bytes at a time to reduce load
+    # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
+
+    # get the whole block
+    block = _rawrecv(radio, BLOCK_SIZE + 5)
+
+    # basic check
+    if len(block) < (BLOCK_SIZE + 5):
+        raise errors.RadioError("Short read of the block 0x%04x" % addr)
+
+    # checking for the ack
+    if block[0] != ACK_CMD:
+        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
+
+    # header validation
+    c, a, l = struct.unpack(">BHB", block[1:5])
+    if a != addr or l != BLOCK_SIZE or c != ord("X"):
+        LOG.debug("Invalid header for block 0x%04x" % addr)
+        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
+        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
+
+    # return the data
+    return block[5:]
+
+
+def _start_clone_mode(radio, status):
+    """Put the radio in clone mode and get the ident string, 3 tries"""
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    # prep the data to show in the UI
+    status.cur = 0
+    status.msg = "Identifying the radio..."
+    status.max = 3
+    radio.status_fn(status)
+
+    try:
+        for a in range(0, status.max):
+            # Update the UI
+            status.cur = a + 1
+            radio.status_fn(status)
+
+            # send the magic word
+            _send(radio, radio._magic)
+
+            # Now you get a x06 of ACK if all goes well
+            ack = radio.pipe.read(1)
+
+            if ack == "\x06":
+                # DEBUG
+                LOG.info("Magic ACK received")
+                status.cur = status.max
+                radio.status_fn(status)
+
+            return True
+
+        return False
+
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
+
+
+def _do_ident(radio, status, upload=False):
+    """Put the radio in PROGRAM mode & identify it"""
+    #  set the serial discipline
+    radio.pipe.setBaudrate(9600)
+    radio.pipe.setParity("N")
+
+    # open the radio into program mode
+    if _start_clone_mode(radio, status) is False:
+        raise errors.RadioError("Radio didn't entered in the clone mode")
+
+    # Ok, get the ident string
+    ident = _rawrecv(radio, 49)
+
+    # basic check for the ident
+    if len(ident) != 49:
+        raise errors.RadioError("Radio send a short ident block.")
+
+    # check if ident is OK
+    itis = False
+    for fp in radio._fileid:
+        if fp in ident:
+            itis = True
+            break
+
+    if itis is False:
+        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
+        raise errors.RadioError("Radio identification failed.")
+
+    # some radios needs a extra read and check for a code on it, this ones
+    # has the check value in the _id2 var, others simply False
+    if radio._id2 is not False:
+        # query & receive the extra ID
+        _send(radio, _make_frame("S", 0x3DF0, 16))
+        id2 = _rawrecv(radio, 21)
+
+        # WARNING !!!!!!
+        # different radios send a response with a different amount of data
+        # it seems that it's padded with \xff, \x20 and some times with \x00
+        # we just care about the first 16, our magic string is in there
+        if len(id2) < 16:
+            raise errors.RadioError("The extra ID is short, aborting.")
+
+        # ok, the correct string must be in the received data
+        if radio._id2 not in id2:
+            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
+                      (radio.MODEL, util.hexprint(id2)))
+            raise errors.RadioError("The extra ID is wrong, aborting.")
+
+        # this radios need a extra request/answer here on the upload
+        # the amount of data received depends of the radio type
+        #
+        # also the first block of TX must no have the ACK at the beginning
+        # see _upload for this.
+        if upload is True:
+            # send an ACK
+            _send(radio, ACK_CMD)
+
+            # the amount of data depend on the radio, so far we have two radios
+            # reading two bytes with an ACK at the end and just ONE with just
+            # one byte (QYT KT8900)
+            # the JT-6188 appears a clone of the last, but reads TWO bytes.
+            #
+            # we will read two bytes with a custom timeout to not penalize the
+            # users for this.
+            #
+            # we just check for a response and last byte being a ACK, that is
+            # the common stone for all radios (3 so far)
+            radio.pipe.setTimeout(0.1)
+            ack = _rawrecv(radio, 2)
+
+            # checking
+            if len(ack) == 0 or ack[-1:] != ACK_CMD:
+                raise errors.RadioError("Radio didn't ACK the upload")
+
+            # restore the default serial timeout
+            radio.pipe.setTimeout(STIMEOUT)
+
+    # DEBUG
+    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
+
+    return True
+
+
+def _download(radio):
+    """Get the memory map"""
+
+    # UI progress
+    status = chirp_common.Status()
+
+    # put radio in program mode and identify it
+    _do_ident(radio, status)
+
+    # the models that doesn't have the extra ID have to make a dummy read here
+    if radio._id2 is False:
+        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
+        discard = _rawrecv(radio, BLOCK_SIZE + 5)
+
+        if debug is True:
+            LOG.info("Dummy first block read done, got this:\n\n %s",
+                     util.hexprint(discard))
+
+    # reset the progress bar in the UI
+    status.max = MEM_SIZE / BLOCK_SIZE
+    status.msg = "Cloning from radio..."
+    status.cur = 0
+    radio.status_fn(status)
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    data = ""
+    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
+        # sending the read request
+        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
+
+        # read
+        d = _recv(radio, addr)
+
+        # aggregate the data
+        data += d
+
+        # UI Update
+        status.cur = addr / BLOCK_SIZE
+        status.msg = "Cloning from radio..."
+        radio.status_fn(status)
+
+    return data
+
+
+def _upload(radio):
+    """Upload procedure"""
+
+    # The UPLOAD mem is restricted to lower than 0x3100,
+    # so we will overide that here localy
+    MEM_SIZE = 0x3100
+
+    # UI progress
+    status = chirp_common.Status()
+
+    # put radio in program mode and identify it
+    _do_ident(radio, status, True)
+
+    # get the data to upload to radio
+    data = radio.get_mmap()
+
+    # Reset the UI progress
+    status.max = MEM_SIZE / TX_BLOCK_SIZE
+    status.cur = 0
+    status.msg = "Cloning to radio..."
+    radio.status_fn(status)
+
+    # the radios that doesn't have the extra ID 'may' do a dummy write, I found
+    # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
+    # dummy write is accepted (it actually writes to the mem!) by the radio.
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    # the fun start here
+    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
+        # getting the block of data to send
+        d = data[addr:addr + TX_BLOCK_SIZE]
+
+        # build the frame to send
+        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
+
+        # first block must not send the ACK at the beginning for the
+        # ones that has the extra id, since this have to do a extra step
+        if addr == 0 and radio._id2 is not False:
+            frame = frame[1:]
+
+        # send the frame
+        _send(radio, frame)
+
+        # receiving the response
+        ack = _rawrecv(radio, 1)
+
+        # basic check
+        if len(ack) != 1:
+            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
+
+        if not ack in "\x06\x05":
+            raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
+
+         # UI Update
+        status.cur = addr / TX_BLOCK_SIZE
+        status.msg = "Cloning to radio..."
+        radio.status_fn(status)
+
+
+def model_match(cls, data):
+    """Match the opened/downloaded image to the correct version"""
+    rid = data[0x3f70:0x3f76]
+
+    if rid in cls._fileid:
+        return True
+
+    return False
+
+
+def _decode_ranges(low, high):
+    """Unpack the data in the ranges zones in the memmap and return
+    a tuple with the integer corresponding to the Mhz it means"""
+    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
+    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
+    ilow *= 1000000
+    ihigh *= 1000000
+
+    return (ilow, ihigh)
+
+
+def _split(rf, f1, f2):
+    """Returns False if the two freqs are in the same band (no split)
+    or True otherwise"""
+
+    # determine if the two freqs are in the same band
+    for low, high in rf.valid_bands:
+        if f1 >= low and f1 <= high and \
+                f2 >= low and f2 <= high:
+            # if the two freqs are on the same Band this is not a split
+            return False
+
+    # if you get here is because the freq pairs are split
+    return False
+
+
+class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
+    """BTECH's UV-5001 and alike radios"""
+    VENDOR = "BTECH"
+    MODEL = ""
+    IDENT = ""
+    _vhf_range = (130000000, 180000000)
+    _220_range = (210000000, 231000000)
+    _uhf_range = (400000000, 521000000)
+    _upper = 199
+    _magic = MSTRING
+    _fileid = None
+    _id2 = False
+
+    @classmethod
+    def get_prompts(cls):
+        rp = chirp_common.RadioPrompts()
+        rp.experimental = \
+            ('This driver is experimental.\n'
+             '\n'
+             'Please keep a copy of your memories with the original software '
+             'if you treasure them, this driver is new and may contain'
+             ' bugs.\n'
+             '\n'
+             )
+        rp.pre_download = _(dedent("""\
+            Follow these instructions to download your info:
+
+            1 - Turn off your radio
+            2 - Connect your interface cable
+            3 - Turn on your radio
+            4 - Do the download of your radio data
+
+            """))
+        rp.pre_upload = _(dedent("""\
+            Follow these instructions to upload your info:
+
+            1 - Turn off your radio
+            2 - Connect your interface cable
+            3 - Turn on your radio
+            4 - Do the upload of your radio data
+
+            """))
+        return rp
+
+    def get_features(self):
+        """Get the radio's features"""
+
+        # we will use the following var as global
+        global POWER_LEVELS
+
+        rf = chirp_common.RadioFeatures()
+        rf.has_settings = False
+        rf.has_bank = False
+        rf.has_tuning_step = False
+        rf.can_odd_split = True
+        rf.has_name = True
+        rf.has_offset = True
+        rf.has_mode = True
+        rf.has_dtcs = True
+        rf.has_rx_dtcs = True
+        rf.has_dtcs_polarity = True
+        rf.has_ctone = True
+        rf.has_cross = True
+        rf.valid_modes = MODES
+        rf.valid_characters = VALID_CHARS
+        rf.valid_name_length = NAME_LENGTH
+        rf.valid_duplexes = ["", "-", "+", "split", "off"]
+        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
+        rf.valid_cross_modes = [
+            "Tone->Tone",
+            "DTCS->",
+            "->DTCS",
+            "Tone->DTCS",
+            "DTCS->Tone",
+            "->Tone",
+            "DTCS->DTCS"]
+        rf.valid_skips = SKIP_VALUES
+        rf.valid_dtcs_codes = DTCS
+        rf.memory_bounds = (0, self._upper)
+
+        # power levels
+        if self.MODEL == "UV-5001":
+            POWER_LEVELS = UV5001_POWER_LEVELS  # Higher power (50W)
+        else:
+            POWER_LEVELS = NORMAL_POWER_LEVELS  # Lower power (25W)
+
+        rf.valid_power_levels = POWER_LEVELS
+
+        # bands
+        rf.valid_bands = [self._vhf_range, self._uhf_range]
+
+        # 2501+220
+        if self.MODEL == "UV-2501+220":
+            rf.valid_bands.append(self._220_range)
+
+        return rf
+
+    def sync_in(self):
+        """Download from radio"""
+        data = _download(self)
+        self._mmap = memmap.MemoryMap(data)
+        self.process_mmap()
+
+    def sync_out(self):
+        """Upload to radio"""
+        try:
+            _upload(self)
+        except errors.RadioError:
+            raise
+        except Exception, e:
+            raise errors.RadioError("Error: %s" % e)
+
+    def set_options(self):
+        """This is to read the options from the image and set it in the
+        environment, for now just the limits of the freqs in the VHF/UHF
+        ranges"""
+
+        # setting the correct ranges for each radio type
+        if "+220" in self.MODEL:
+            # the model 2501+220 has a segment in 220
+            # and a different position in the memmap
+            ranges = self._memobj.ranges220
+        else:
+            ranges = self._memobj.ranges
+
+        # the normal dual bands
+        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
+        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
+
+        # DEBUG
+        LOG.info("Radio ranges: VHF %d to %d" % vhf)
+        LOG.info("Radio ranges: UHF %d to %d" % uhf)
+
+        # 220Mhz case
+        if "+220" in self.MODEL:
+            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
+            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
+            self._220_range = vhf2
+
+        # set the class with the real data
+        self._vhf_range = vhf
+        self._uhf_range = uhf
+
+    def process_mmap(self):
+        """Process the mem map into the mem object"""
+
+        # Get it
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+        # load specific parameters from the radio image
+        self.set_options()
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number])
+
+    def _decode_tone(self, val):
+        """Parse the tone data to decode from mem, it returns:
+        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
+        pol = None
+
+        if val in [0, 65535]:
+            return '', None, None
+        elif val > 0x0258:
+            a = val / 10.0
+            return 'Tone', a, pol
+        else:
+            if val > 0x69:
+                index = val - 0x6A
+                pol = "R"
+            else:
+                index = val - 1
+                pol = "N"
+
+            tone = DTCS[index]
+            return 'DTCS', tone, pol
+
+    def _encode_tone(self, memval, mode, val, pol):
+        """Parse the tone data to encode from UI to mem"""
+        if mode == '' or mode is None:
+            memval.set_raw("\x00\x00")
+        elif mode == 'Tone':
+            memval.set_value(val * 10)
+        elif mode == 'DTCS':
+            # detect the index in the DTCS list
+            try:
+                index = DTCS.index(val)
+                if pol == "N":
+                    index += 1
+                else:
+                    index += 0x6A
+                memval.set_value(index)
+            except:
+                msg = "Digital Tone '%d' is not supported" % value
+                LOG.error(msg)
+                raise errors.RadioError(msg)
+        else:
+            msg = "Internal error: invalid mode '%s'" % mode
+            LOG.error(msg)
+            raise errors.InvalidDataError(msg)
+
+    def get_memory(self, number):
+        """Get the mem representation from the radio image"""
+        _mem = self._memobj.memory[number]
+        _names = self._memobj.names[number]
+
+        # Create a high-level memory object to return to the UI
+        mem = chirp_common.Memory()
+
+        # Memory number
+        mem.number = number
+
+        if _mem.get_raw()[0] == "\xFF":
+            mem.empty = True
+            return mem
+
+        # Freq and offset
+        mem.freq = int(_mem.rxfreq) * 10
+        # tx freq can be blank
+        if _mem.get_raw()[4] == "\xFF":
+            # TX freq not set
+            mem.offset = 0
+            mem.duplex = "off"
+        else:
+            # TX freq set
+            offset = (int(_mem.txfreq) * 10) - mem.freq
+            if offset != 0:
+                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
+                    mem.duplex = "split"
+                    mem.offset = int(_mem.txfreq) * 10
+                elif offset < 0:
+                    mem.offset = abs(offset)
+                    mem.duplex = "-"
+                elif offset > 0:
+                    mem.offset = offset
+                    mem.duplex = "+"
+            else:
+                mem.offset = 0
+
+        # name TAG of the channel
+        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
+
+        # power
+        mem.power = POWER_LEVELS[int(_mem.power)]
+
+        # wide/narrow
+        mem.mode = MODES[int(_mem.wide)]
+
+        # skip
+        mem.skip = SKIP_VALUES[_mem.add]
+
+        # tone data
+        rxtone = txtone = None
+        txtone = self._decode_tone(_mem.txtone)
+        rxtone = self._decode_tone(_mem.rxtone)
+        chirp_common.split_tone_decode(mem, txtone, rxtone)
+
+        # Extra
+        mem.extra = RadioSettingGroup("extra", "Extra")
+
+        spmute = RadioSetting("spmute", "Speaker mute",
+                              RadioSettingValueBoolean(bool(_mem.spmute)))
+        mem.extra.append(spmute)
+
+        scramble = RadioSetting("scramble", "Scramble",
+                                RadioSettingValueBoolean(bool(_mem.scramble)))
+        mem.extra.append(scramble)
+
+        bcl = RadioSetting("bcl", "Busy channel lockout",
+                           RadioSettingValueBoolean(bool(_mem.bcl)))
+        mem.extra.append(bcl)
+
+        pttid = RadioSetting("pttid", "PTT ID",
+                             RadioSettingValueList(PTTID_LIST,
+                                                   PTTID_LIST[_mem.pttid]))
+        mem.extra.append(pttid)
+
+        # validating scode
+        scode = _mem.scode if _mem.scode != 15 else 0
+        pttidcode = RadioSetting("scode", "PTT ID signal code",
+                                 RadioSettingValueList(
+                                     PTTIDCODE_LIST,
+                                     PTTIDCODE_LIST[scode]))
+        mem.extra.append(pttidcode)
+
+        optsig = RadioSetting("optsig", "Optional signaling",
+                              RadioSettingValueList(
+                                  OPTSIG_LIST,
+                                  OPTSIG_LIST[_mem.optsig]))
+        mem.extra.append(optsig)
+
+        return mem
+
+    def set_memory(self, mem):
+        """Set the memory data in the eeprom img from the UI"""
+        # get the eprom representation of this channel
+        _mem = self._memobj.memory[mem.number]
+        _names = self._memobj.names[mem.number]
+
+        # if empty memmory
+        if mem.empty:
+            # the channel itself
+            _mem.set_raw("\xFF" * 16)
+            # the name tag
+            _names.set_raw("\xFF" * 16)
+            return
+
+        # frequency
+        _mem.rxfreq = mem.freq / 10
+
+        # duplex
+        if mem.duplex == "+":
+            _mem.txfreq = (mem.freq + mem.offset) / 10
+        elif mem.duplex == "-":
+            _mem.txfreq = (mem.freq - mem.offset) / 10
+        elif mem.duplex == "off":
+            for i in _mem.txfreq:
+                i.set_raw("\xFF")
+        elif mem.duplex == "split":
+            _mem.txfreq = mem.offset / 10
+        else:
+            _mem.txfreq = mem.freq / 10
+
+        # tone data
+        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
+            chirp_common.split_tone_encode(mem)
+        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
+        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
+
+        # name TAG of the channel
+        if len(mem.name) < NAME_LENGTH:
+            # we must pad to NAME_LENGTH chars, " " = "\xFF"
+            mem.name = str(mem.name).ljust(NAME_LENGTH, " ")
+        _names.name = str(mem.name).replace(" ", "\xFF")
+
+        # power, # default power level is high
+        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
+
+        # wide/narrow
+        _mem.wide = MODES.index(mem.mode)
+
+        # scan add property
+        _mem.add = SKIP_VALUES.index(mem.skip)
+
+        # reseting unknowns, this have to be set by hand
+        _mem.unknown0 = 0
+        _mem.unknown1 = 0
+        _mem.unknown2 = 0
+        _mem.unknown3 = 0
+        _mem.unknown4 = 0
+        _mem.unknown5 = 0
+        _mem.unknown6 = 0
+
+        # extra settings
+        if len(mem.extra) > 0:
+            # there are setting, parse
+            for setting in mem.extra:
+                setattr(_mem, setting.get_name(), setting.value)
+        else:
+            # there is no extra settings, load defaults
+            _mem.spmute = 0
+            _mem.optsig = 0
+            _mem.scramble = 0
+            _mem.bcl = 0
+            _mem.pttid = 0
+            _mem.scode = 0
+
+        return mem
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        match_size = False
+        match_model = False
+
+        # testing the file data size
+        if len(filedata) == MEM_SIZE:
+            match_size = True
+
+        # testing the firmware model fingerprint
+        match_model = model_match(cls, filedata)
+
+        if match_size and match_model:
+            return True
+        else:
+            return False
+
+
+ at directory.register
+class UV2501(BTech):
+    """Baofeng Tech UV2501"""
+    MODEL = "UV-2501"
+    _fileid = [UV2501G2_fp, UV2501pp2_fp, UV2501pp_fp]
+
+
+ at directory.register
+class UV2501_220(BTech):
+    """Baofeng Tech UV2501+220"""
+    MODEL = "UV-2501+220"
+    _magic = MSTRING_220
+    _fileid = [UV2501_220_fp, UV2501_220pp_fp]
+    _id2 = UV2501_220pp_id
+
+
+ at directory.register
+class UV5001(BTech):
+    """Baofeng Tech UV5001"""
+    MODEL = "UV-5001"
+    _fileid = [UV5001G22_fp, UV5001G2_fp, UV5001alpha_fp, UV5001pp_fp]
+
+
+ at directory.register
+class MINI8900(BTech):
+    """WACCOM MINI-8900"""
+    VENDOR = "WACCOM"
+    MODEL = "MINI-8900"
+    _magic = MSTRING_MINI8900
+    _fileid = [MINI8900_fp, ]
+
+
+ at directory.register
+class KTUV980(BTech):
+    """QYT KT-UV980"""
+    VENDOR = "QYT"
+    MODEL = "KT-UV980"
+    _vhf_range = (136000000, 175000000)
+    _uhf_range = (400000000, 481000000)
+    _magic = MSTRING_MINI8900
+    _fileid = [KTUV980_fp, ]
+
+
+ at directory.register
+class KT9800(BTech):
+    """QYT KT8900"""
+    VENDOR = "QYT"
+    MODEL = "KT8900"
+    _vhf_range = (136000000, 175000000)
+    _uhf_range = (400000000, 481000000)
+    _magic = MSTRING_KT8900
+    _fileid = [KT8900_fp, ]
+    _id2 = KT8900_id
diff --git a/chirp/drivers/fd268.py b/chirp/drivers/fd268.py
index a9d9f25..b524c4f 100644
--- a/chirp/drivers/fd268.py
+++ b/chirp/drivers/fd268.py
@@ -1,4 +1,4 @@
-# Copyright 2012 Dan Smith <dsmith at danplanet.com>
+# Copyright 2015 Pavel Milanes CO7WT, <co7wt at frcuba.co.cu> <pavelmc 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
@@ -13,8 +13,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Driver author: Pavel CO7WT, co7wt at frcuba.co.cu, pavelmc at gmail.com
-
 import struct
 import os
 import logging
@@ -287,8 +285,6 @@ def model_match(cls, data):
     else:
         LOG.debug("Unknowd Feidaxing radio, ID:")
         LOG.debug(util.hexprint(fp))
-        print("Unknowd Feidaxing radio, ID:")
-        print util.hexprint(fp)
 
         return False
 
diff --git a/chirp/drivers/ft2800.py b/chirp/drivers/ft2800.py
index 9c39ad1..b1a8928 100644
--- a/chirp/drivers/ft2800.py
+++ b/chirp/drivers/ft2800.py
@@ -207,7 +207,7 @@ class FT2800Radio(YaesuCloneModeRadio):
         self.process_mmap()
 
     def sync_out(self):
-        self.pipe.setTimeout(1)
+        self.pipe.timeout = 1
         self.pipe.setParity("E")
         start = time.time()
         try:
diff --git a/chirp/drivers/ft2900.py b/chirp/drivers/ft2900.py
index f56f915..9fc93dc 100644
--- a/chirp/drivers/ft2900.py
+++ b/chirp/drivers/ft2900.py
@@ -1,6 +1,7 @@
 # Copyright 2011 Dan Smith <dsmith at danplanet.com>
 #
 # FT-2900-specific modifications by Richard Cochran, <ag6qr at sonic.net>
+# Initial work on settings by Chris Fosnight, <chris.fosnight 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
@@ -21,6 +22,8 @@ import logging
 
 from chirp import util, memmap, chirp_common, bitwise, directory, errors
 from chirp.drivers.yaesu_clone import YaesuCloneModeRadio
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+    RadioSettingValueList, RadioSettingValueString, RadioSettings
 
 from textwrap import dedent
 
@@ -132,7 +135,10 @@ def _upload(radio):
     ack = radio.pipe.read(300)
     LOG.debug("Ack was (%i):\n%s" % (len(ack), util.hexprint(ack)))
     if ack != ACK:
-        raise Exception("Radio did not ack ID")
+        raise Exception("Radio did not ack ID. Check cable, verify"
+                        " radio is not locked.\n"
+                        " (press & Hold red \"*L\" button to unlock"
+                        " radio if needed)")
 
     block = 0
     cs = INITIAL_CHECKSUM
@@ -161,6 +167,87 @@ def _upload(radio):
     _send(radio.pipe, chr(cs & 0xFF))
 
 MEM_FORMAT = """
+#seekto 0x0080;
+struct {
+    u8  apo;
+    u8  arts_beep;
+    u8  bell;
+    u8  dimmer;
+    u8  cw_id_string[16];
+    u8  cw_trng;
+    u8  x95;
+    u8  x96;
+    u8  x97;
+    u8  int_cd;
+    u8  int_set;
+    u8  x9A;
+    u8  x9B;
+    u8  lock;
+    u8  x9D;
+    u8  mic_gain;
+    u8  open_msg;
+    u8  openMsg_Text[6];
+    u8  rf_sql;
+    u8  unk:6,
+        pag_abk:1,
+        unk:1;
+    u8  pag_cdr_1;
+    u8  pag_cdr_2;
+    u8  pag_cdt_1;
+    u8  pag_cdt_2;
+    u8  prog_p1;
+    u8  xAD;
+    u8  prog_p2;
+    u8  xAF;
+    u8  prog_p3;
+    u8  xB1;
+    u8  prog_p4;
+    u8  xB3;
+    u8  resume;
+    u8  tot;
+    u8  unk:1,
+        cw_id:1,
+        unk:1,
+        ts_speed:1,
+        ars:1,
+        unk:2,
+        dtmf_mode:1;
+    u8  unk:1,
+        ts_mut:1
+        wires_auto:1,
+        busy_lockout:1,
+        edge_beep:1,
+        unk:3;
+    u8  unk:2,
+        s_search:1,
+        unk:2,
+        cw_trng_units:1,
+        unk:2;
+    u8  dtmf_speed:1,
+        unk:2,
+        arts_interval:1,
+        unk:1,
+        inverted_dcs:1,
+        unk:1,
+        mw_mode:1;
+    u8  unk:2,
+        wires_mode:1,
+        wx_alert:1,
+        unk:1,
+        wx_vol_max:1,
+        revert:1,
+        unk:1;
+    u8  vfo_scan;
+    u8  scan_mode;
+    u8  dtmf_delay;
+    u8  beep;
+    u8  xBF;
+} settings;
+
+#seekto 0x00d0;
+    u8  passwd[4];
+    u8  mbs;
+
 #seekto 0x00c0;
 struct {
   u16 in_use;
@@ -172,6 +259,11 @@ struct {
 #seekto 0x00f0;
   u8 curChannelMem[20];
 
+#seekto 0x1e0;
+struct {
+  u8 dtmf_string[16];
+} dtmf_strings[10];
+
 #seekto 0x0127;
   u8 curChannelNum;
 
@@ -421,6 +513,7 @@ class FT2900Radio(YaesuCloneModeRadio):
         rf.has_dtcs_polarity = False
         rf.has_bank = True
         rf.has_bank_names = True
+        rf.has_settings = True
 
         rf.valid_tuning_steps = STEPS
         rf.valid_modes = MODES
@@ -447,7 +540,7 @@ class FT2900Radio(YaesuCloneModeRadio):
         self.process_mmap()
 
     def sync_out(self):
-        self.pipe.setTimeout(1)
+        self.pipe.timeout = 1
         start = time.time()
         try:
             _upload(self)
@@ -617,6 +710,496 @@ class FT2900Radio(YaesuCloneModeRadio):
 
         LOG.debug("encoded mem\n%s\n" % (util.hexprint(_mem.get_raw()[0:20])))
 
+    def get_settings(self):
+        _settings = self._memobj.settings
+        _dtmf_strings = self._memobj.dtmf_strings
+        _passwd = self._memobj.passwd
+
+        repeater = RadioSettingGroup("repeater", "Repeater Settings")
+        ctcss = RadioSettingGroup("ctcss", "CTCSS/DCS/EPCS Settings")
+        arts = RadioSettingGroup("arts", "ARTS Settings")
+        mbls = RadioSettingGroup("banks", "Memory Settings")
+        scan = RadioSettingGroup("scan", "Scan Settings")
+        dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
+        wires = RadioSettingGroup("wires", "WiRES(tm) Settings")
+        switch = RadioSettingGroup("switch", "Switch/Knob Settings")
+        disp = RadioSettingGroup("disp", "Display Settings")
+        misc = RadioSettingGroup("misc", "Miscellaneous Settings")
+
+        setmode = RadioSettings(repeater, ctcss, arts, mbls, scan,
+                                dtmf, wires, switch, disp, misc)
+
+        # numbers and names of settings refer to the way they're
+        # presented in the set menu, as well as the list starting on
+        # page 74 of the manual
+
+        # 1 APO
+        opts = ["Off", "30 Min", "1 Hour", "3 Hour", "5 Hour", "8 Hour"]
+        misc.append(
+            RadioSetting(
+                "apo", "Automatic Power Off",
+                RadioSettingValueList(opts, opts[_settings.apo])))
+
+        # 2 AR.BEP
+        opts = ["Off", "In Range", "Always"]
+        arts.append(
+            RadioSetting(
+                "arts_beep", "ARTS Beep",
+                RadioSettingValueList(opts, opts[_settings.arts_beep])))
+
+        # 3 AR.INT
+        opts = ["15 Sec", "25 Sec"]
+        arts.append(
+            RadioSetting(
+                "arts_interval", "ARTS Polling Interval",
+                RadioSettingValueList(opts, opts[_settings.arts_interval])))
+
+        # 4 ARS
+        opts = ["Off", "On"]
+        repeater.append(
+            RadioSetting(
+                "ars", "Automatic Repeater Shift",
+                RadioSettingValueList(opts, opts[_settings.ars])))
+
+        # 5 BCLO
+        opts = ["Off", "On"]
+        misc.append(RadioSetting(
+                "busy_lockout", "Busy Channel Lock-Out",
+                RadioSettingValueList(opts, opts[_settings.busy_lockout])))
+
+        # 6 BEEP
+        opts = ["Off", "Key+Scan", "Key"]
+        switch.append(RadioSetting(
+                "beep", "Enable the Beeper",
+                RadioSettingValueList(opts, opts[_settings.beep])))
+
+        # 7 BELL
+        opts = ["Off", "1", "3", "5", "8", "Continuous"]
+        ctcss.append(RadioSetting("bell", "Bell Repetitions",
+                     RadioSettingValueList(opts, opts[_settings.bell])))
+
+        # 8 BNK.LNK
+        for i in range(0, 8):
+            opts = ["Off", "On"]
+            mbs = (self._memobj.mbs >> i) & 1
+            rs = RadioSetting("mbs%i" % i, "Bank %s Scan" % (i + 1),
+                              RadioSettingValueList(opts, opts[mbs]))
+
+            def apply_mbs(s, index):
+                if int(s.value):
+                    self._memobj.mbs |= (1 << index)
+                else:
+                    self._memobj.mbs &= ~(1 << index)
+            rs.set_apply_callback(apply_mbs, i)
+            mbls.append(rs)
+
+        # 9 BNK.NM - A per-bank attribute, nothing to do here.
+
+        # 10 CLK.SFT - A per-channel attribute, nothing to do here.
+
+        # 11 CW.ID
+        opts = ["Off", "On"]
+        arts.append(RadioSetting("cw_id", "CW ID Enable",
+                    RadioSettingValueList(opts, opts[_settings.cw_id])))
+
+        cw_id_text = ""
+        for i in _settings.cw_id_string:
+            try:
+                cw_id_text += CHARSET[i & 0x7F]
+            except IndexError:
+                if i != 0xff:
+                    LOG.debug("unknown char index in cw id: %x " % (i))
+
+        val = RadioSettingValueString(0, 16, cw_id_text, True)
+        val.set_charset(CHARSET + "abcdefghijklmnopqrstuvwxyz")
+        rs = RadioSetting("cw_id_string", "CW Identifier Text", val)
+
+        def apply_cw_id(s):
+            str = s.value.get_value().upper().rstrip()
+            mval = ""
+            mval = [chr(CHARSET.index(x)) for x in str]
+            for x in range(len(mval), 16):
+                mval.append(chr(0xff))
+            for x in range(0, 16):
+                _settings.cw_id_string[x] = ord(mval[x])
+        rs.set_apply_callback(apply_cw_id)
+        arts.append(rs)
+
+        # 12 CWTRNG
+        opts = ["Off", "4WPM", "5WPM", "6WPM", "7WPM", "8WPM", "9WPM",
+                "10WPM", "11WPM", "12WPM", "13WPM", "15WPM", "17WPM",
+                "20WPM", "24WPM", "30WPM", "40WPM"]
+        misc.append(RadioSetting("cw_trng", "CW Training",
+                    RadioSettingValueList(opts, opts[_settings.cw_trng])))
+
+        # todo: make the setting of the units here affect the display
+        # of the speed.  Not critical, but would be slick.
+        opts = ["CPM", "WPM"]
+        misc.append(RadioSetting("cw_trng_units", "CW Training Units",
+                    RadioSettingValueList(opts,
+                                          opts[_settings.cw_trng_units])))
+
+        # 13 DC VLT - a read-only status, so nothing to do here
+
+        # 14 DCS CD - A per-channel attribute, nothing to do here
+
+        # 15 DCS.RV
+        opts = ["Disabled", "Enabled"]
+        ctcss.append(RadioSetting(
+                     "inverted_dcs",
+                     "\"Inverted\" DCS Code Decoding",
+                     RadioSettingValueList(opts,
+                                           opts[_settings.inverted_dcs])))
+
+        # 16 DIMMER
+        opts = ["Off"] + ["Level %d" % (x) for x in range(1, 11)]
+        disp.append(RadioSetting("dimmer", "Dimmer",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .dimmer])))
+
+        # 17 DT.A/M
+        opts = ["Manual", "Auto"]
+        dtmf.append(RadioSetting("dtmf_mode", "DTMF Autodialer",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .dtmf_mode])))
+
+        # 18 DT.DLY
+        opts = ["50 ms", "250 ms", "450 ms", "750 ms", "1000 ms"]
+        dtmf.append(RadioSetting("dtmf_delay", "DTMF Autodialer Delay Time",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .dtmf_delay])))
+
+        # 19 DT.SET
+        for memslot in range(0, 10):
+            dtmf_memory = ""
+            for i in _dtmf_strings[memslot].dtmf_string:
+                if i != 0xFF:
+                    try:
+                        dtmf_memory += CHARSET[i]
+                    except IndexError:
+                        LOG.debug("unknown char index in dtmf: %x " % (i))
+
+            val = RadioSettingValueString(0, 16, dtmf_memory, True)
+            val.set_charset(CHARSET + "abcdef")
+            rs = RadioSetting("dtmf_string_%d" % memslot,
+                              "DTMF Memory %d" % memslot, val)
+
+            def apply_dtmf(s, i):
+                LOG.debug("applying dtmf for %x\n" % i)
+                str = s.value.get_value().upper().rstrip()
+                LOG.debug("str is %s\n" % str)
+                mval = ""
+                mval = [chr(CHARSET.index(x)) for x in str]
+                for x in range(len(mval), 16):
+                    mval.append(chr(0xff))
+                for x in range(0, 16):
+                    _dtmf_strings[i].dtmf_string[x] = ord(mval[x])
+            rs.set_apply_callback(apply_dtmf, memslot)
+            dtmf.append(rs)
+
+        # 20 DT.SPD
+        opts = ["50 ms", "100 ms"]
+        dtmf.append(RadioSetting("dtmf_speed",
+                                 "DTMF Autodialer Sending Speed",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings.
+                                                            dtmf_speed])))
+
+        # 21 EDG.BEP
+        opts = ["Off", "On"]
+        mbls.append(RadioSetting("edge_beep", "Band Edge Beeper",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings.
+                                                            edge_beep])))
+
+        # 22 INT.CD
+        opts = ["DTMF %X" % (x) for x in range(0, 16)]
+        wires.append(RadioSetting("int_cd", "Access Number for WiRES(TM)",
+                     RadioSettingValueList(opts, opts[_settings.int_cd])))
+
+        # 23 ING MD
+        opts = ["Sister Radio Group", "Friends Radio Group"]
+        wires.append(RadioSetting("wires_mode",
+                                  "Internet Link Connection Mode",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings.
+                                                             wires_mode])))
+
+        # 24 INT.A/M
+        opts = ["Manual", "Auto"]
+        wires.append(RadioSetting("wires_auto", "Internet Link Autodialer",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .wires_auto])))
+        # 25 INT.SET
+        opts = ["F%d" % (x) for x in range(0, 10)]
+
+        wires.append(RadioSetting("int_set", "Memory Register for "
+                                  "non-WiRES Internet",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .int_set])))
+
+        # 26 LOCK
+        opts = ["Key", "Dial", "Key + Dial", "PTT",
+                "Key + PTT", "Dial + PTT", "All"]
+        switch.append(RadioSetting("lock", "Control Locking",
+                                   RadioSettingValueList(opts,
+                                                         opts[_settings
+                                                              .lock])))
+
+        # 27 MCGAIN
+        opts = ["Level %d" % (x) for x in range(1, 10)]
+        misc.append(RadioSetting("mic_gain", "Microphone Gain",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .mic_gain])))
+
+        # 28 MEM.SCN
+        opts = ["Tag 1", "Tag 2", "All Channels"]
+        rs = RadioSetting("scan_mode", "Memory Scan Mode",
+                          RadioSettingValueList(opts,
+                                                opts[_settings
+                                                     .scan_mode - 1]))
+        # this setting is unusual in that it starts at 1 instead of 0.
+        # that is, index 1 corresponds to "Tag 1", and index 0 is invalid.
+        # so we create a custom callback to handle this.
+
+        def apply_scan_mode(s):
+            myopts = ["Tag 1", "Tag 2", "All Channels"]
+            _settings.scan_mode = myopts.index(s.value.get_value()) + 1
+        rs.set_apply_callback(apply_scan_mode)
+        mbls.append(rs)
+
+        # 29 MW MD
+        opts = ["Lower", "Next"]
+        mbls.append(RadioSetting("mw_mode", "Memory Write Mode",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .mw_mode])))
+
+        # 30 NM SET - This is per channel, so nothing to do here
+
+        # 31 OPN.MSG
+        opts = ["Off", "DC Supply Voltage", "Text Message"]
+        disp.append(RadioSetting("open_msg", "Opening Message Type",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings.
+                                                            open_msg])))
+
+        openmsg = ""
+        for i in _settings.openMsg_Text:
+            try:
+                openmsg += CHARSET[i & 0x7F]
+            except IndexError:
+                if i != 0xff:
+                    LOG.debug("unknown char index in openmsg: %x " % (i))
+
+        val = RadioSettingValueString(0, 6, openmsg, True)
+        val.set_charset(CHARSET + "abcdefghijklmnopqrstuvwxyz")
+        rs = RadioSetting("openMsg_Text", "Opening Message Text", val)
+
+        def apply_openmsg(s):
+            str = s.value.get_value().upper().rstrip()
+            mval = ""
+            mval = [chr(CHARSET.index(x)) for x in str]
+            for x in range(len(mval), 6):
+                mval.append(chr(0xff))
+            for x in range(0, 6):
+                _settings.openMsg_Text[x] = ord(mval[x])
+        rs.set_apply_callback(apply_openmsg)
+        disp.append(rs)
+
+        # 32 PAGER - a per-channel attribute
+
+        # 33 PAG.ABK
+        opts = ["Off", "On"]
+        ctcss.append(RadioSetting("pag_abk", "Paging Answer Back",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .pag_abk])))
+
+        # 34 PAG.CDR
+        opts = ["%2.2d" % (x) for x in range(1, 50)]
+        ctcss.append(RadioSetting("pag_cdr_1", "Receive Page Code 1",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .pag_cdr_1])))
+
+        ctcss.append(RadioSetting("pag_cdr_2", "Receive Page Code 2",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .pag_cdr_2])))
+
+        # 35 PAG.CDT
+        opts = ["%2.2d" % (x) for x in range(1, 50)]
+        ctcss.append(RadioSetting("pag_cdt_1", "Transmit Page Code 1",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .pag_cdt_1])))
+
+        ctcss.append(RadioSetting("pag_cdt_2", "Transmit Page Code 2",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .pag_cdt_2])))
+
+        # Common Button Options
+        button_opts = ["Squelch Off", "Weather", "Smart Search",
+                       "Tone Scan", "Scan", "T Call", "ARTS"]
+
+        # 36 PRG P1
+        opts = button_opts + ["DC Volts"]
+        switch.append(RadioSetting(
+                "prog_p1", "P1 Button",
+                RadioSettingValueList(opts, opts[_settings.prog_p1])))
+
+        # 37 PRG P2
+        opts = button_opts + ["Dimmer"]
+        switch.append(RadioSetting(
+                "prog_p2", "P2 Button",
+                RadioSettingValueList(opts, opts[_settings.prog_p2])))
+
+        # 38 PRG P3
+        opts = button_opts + ["Mic Gain"]
+        switch.append(RadioSetting(
+                "prog_p3", "P3 Button",
+                RadioSettingValueList(opts, opts[_settings.prog_p3])))
+
+        # 39 PRG P4
+        opts = button_opts + ["Skip"]
+        switch.append(RadioSetting(
+                "prog_p4", "P4 Button",
+                RadioSettingValueList(opts, opts[_settings.prog_p4])))
+
+        # 40 PSWD
+        password = ""
+        for i in _passwd:
+            if i != 0xFF:
+                try:
+                    password += CHARSET[i]
+                except IndexError:
+                    LOG.debug("unknown char index in password: %x " % (i))
+
+        val = RadioSettingValueString(0, 4, password, True)
+        val.set_charset(CHARSET[0:15] + "abcdef ")
+        rs = RadioSetting("passwd", "Password", val)
+
+        def apply_password(s):
+            str = s.value.get_value().upper().rstrip()
+            mval = ""
+            mval = [chr(CHARSET.index(x)) for x in str]
+            for x in range(len(mval), 4):
+                mval.append(chr(0xff))
+            for x in range(0, 4):
+                _passwd[x] = ord(mval[x])
+        rs.set_apply_callback(apply_password)
+        misc.append(rs)
+
+        # 41 RESUME
+        opts = ["3 Sec", "5 Sec", "10 Sec", "Busy", "Hold"]
+        scan.append(RadioSetting("resume", "Scan Resume Mode",
+                    RadioSettingValueList(opts, opts[_settings.resume])))
+
+        # 42 RF.SQL
+        opts = ["Off"] + ["S-%d" % (x) for x in range(1, 10)]
+        misc.append(RadioSetting("rf_sql", "RF Squelch Threshold",
+                    RadioSettingValueList(opts, opts[_settings.rf_sql])))
+
+        # 43 RPT - per channel attribute, nothing to do here
+
+        # 44 RVRT
+        opts = ["Off", "On"]
+        misc.append(RadioSetting("revert", "Priority Revert",
+                    RadioSettingValueList(opts, opts[_settings.revert])))
+
+        # 45 S.SRCH
+        opts = ["Single", "Continuous"]
+        misc.append(RadioSetting("s_search", "Smart Search Sweep Mode",
+                    RadioSettingValueList(opts, opts[_settings.s_search])))
+
+        # 46 SHIFT - per channel setting, nothing to do here
+
+        # 47 SKIP = per channel setting, nothing to do here
+
+        # 48 SPLIT - per channel attribute, nothing to do here
+
+        # 49 SQL.TYP - per channel attribute, nothing to do here
+
+        # 50 STEP - per channel attribute, nothing to do here
+
+        # 51 TEMP - read-only status, nothing to do here
+
+        # 52 TN FRQ - per channel attribute, nothing to do here
+
+        # 53 TOT
+        opts = ["Off", "1 Min", "3 Min", "5 Min", "10 Min"]
+        misc.append(RadioSetting("tot", "Timeout Timer",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings.tot])))
+
+        # 54 TS MUT
+        opts = ["Off", "On"]
+        ctcss.append(RadioSetting("ts_mut", "Tone Search Mute",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .ts_mut])))
+
+        # 55 TS SPEED
+        opts = ["Fast", "Slow"]
+        ctcss.append(RadioSetting("ts_speed", "Tone Search Scanner Speed",
+                                  RadioSettingValueList(opts,
+                                                        opts[_settings
+                                                             .ts_speed])))
+
+        # 56 VFO.SCN
+        opts = ["+/- 1MHz", "+/- 2MHz", "+/-5MHz", "All"]
+        scan.append(RadioSetting("vfo_scan", "VFO Scanner Width",
+                                 RadioSettingValueList(opts,
+                                                       opts[_settings
+                                                            .vfo_scan])))
+
+        # 57 WX.ALT
+        opts = ["Off", "On"]
+        misc.append(RadioSetting("wx_alert", "Weather Alert Scan",
+                    RadioSettingValueList(opts, opts[_settings.wx_alert])))
+
+        # 58 WX.VOL
+        opts = ["Normal", "Maximum"]
+        misc.append(RadioSetting("wx_vol_max", "Weather Alert Volume",
+                    RadioSettingValueList(opts, opts[_settings.wx_vol_max])))
+
+        # 59 W/N DV - this is a per-channel attribute, nothing to do here
+
+        return setmode
+
+    def set_settings(self, uisettings):
+        _settings = self._memobj.settings
+        for element in uisettings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            if not element.changed():
+                continue
+
+            try:
+                name = element.get_name()
+                value = element.value
+
+                if element.has_apply_callback():
+                    LOG.debug("Using apply callback")
+                    element.run_apply_callback()
+                else:
+                    obj = getattr(_settings, name)
+                    setattr(_settings, name, value)
+
+                LOG.debug("Setting %s: %s" % (name, value))
+            except Exception, e:
+                LOG.debug(element.get_name())
+                raise
+
     def get_bank_model(self):
         return FT2900BankModel(self)
 
@@ -637,7 +1220,12 @@ class FT2900Radio(YaesuCloneModeRadio):
             2. Connect data cable.
             3. While holding "A/N LOW" button, turn radio on.
             4. Press "MW D/MR" to receive image.
-            5. Click OK to dismiss this dialog and start transfer."""))
+            5. Make sure display says "-WAIT-" (see note below if not)
+            6. Click OK to dismiss this dialog and start transfer.
+
+            Note: if you don't see "-WAIT-" at step 5, try cycling
+                  power and pressing and holding red "*L" button to unlock
+                  radio, then start back at step 1."""))
         return rp
 
 
@@ -650,3 +1238,16 @@ class FT2900ERadio(FT2900Radio):
     MODEL = "FT-2900E/1900E"
     VARIANT = "E"
     IDBLOCK = "\x56\x43\x32\x33\x00\x02\x41\x02\x01\x01"
+
+
+# the FT2900Mod is a version of the radio that has been modified to
+# allow transmit on a greater range of frequencies.  It is almost
+# identical to the standard version, except for the model number and
+# ID Block. We create and register a class for it, with only the
+# needed overrides
+ at directory.register
+class FT2900ModRadio(FT2900Radio):
+    """Yaesu FT-2900Mod"""
+    MODEL = "FT-2900/1900 (Modded)"
+    VARIANT = "Opened Xmit"
+    IDBLOCK = "\x56\x43\x32\x33\x00\x02\xc7\x01\x01\x01"
diff --git a/chirp/drivers/ft50.py b/chirp/drivers/ft50.py
index 6a9cf21..5840c02 100644
--- a/chirp/drivers/ft50.py
+++ b/chirp/drivers/ft50.py
@@ -1,4 +1,4 @@
-# Copyright 2010 Dan Smith <dsmith at danplanet.com>
+# Copyright 2011 Dan Smith <dsmith at danplanet.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
@@ -12,44 +12,631 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+import time
+import logging
+import re
 
-from chirp.drivers import yaesu_clone, ft50_ll
-from chirp import chirp_common, directory
+from chirp.drivers import yaesu_clone
+from chirp import chirp_common, directory, errors, bitwise, util
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+    RadioSettingValueInteger, RadioSettingValueList, \
+    RadioSettingValueBoolean, RadioSettingValueString, \
+    RadioSettings
+from textwrap import dedent
 
+LOG = logging.getLogger(__name__)
 
-# Not working, don't register
-# @directory.register
+MEM_FORMAT = """
+struct flag_struct {
+  u8 unknown1f:5,
+     skip:1,
+     mask:1,
+     used:1;
+};
+
+struct mem_struct {
+  u8 showname:1,
+     unknown1:3,
+     unknown2:2,
+     unknown3:2;
+  u8 ishighpower:1,
+     power:2,
+     unknown4:1,
+     tuning_step:4;
+  u8 codememno:4,
+     codeorpage:2,
+     duplex:2;
+  u8 tmode:2,
+     tone:6;
+  u8 unknown5:1,
+     dtcs:7;
+  u8 unknown6:6,
+     mode:2;
+  bbcd freq[3];
+  bbcd offset[3];
+  u8 name[4];
+};
+
+#seekto 0x000C;
+struct {
+  u8 extendedrx_flg;    // Seems to be set to 03 when extended rx is enabled
+  u8 extendedrx;        // Seems to be set to 01 when extended rx is enabled
+} extendedrx_struct;    // UNFINISHED!!
+
+#seekto 0x001A;
+struct flag_struct flag[100];
+
+#seekto 0x079C;
+struct flag_struct flag_repeat[100];
+
+#seekto 0x00AA;
+struct mem_struct memory[100];
+struct mem_struct special[11];
+
+#seekto 0x08C7;
+struct {
+  u8 sub_display;
+  u8 unknown1s;
+  u8 apo;
+  u8 timeout;
+  u8 lock;
+  u8 rxsave;
+  u8 lamp;
+  u8 bell;
+  u8 cwid[16];
+  u8 unknown2s;
+  u8 artsmode;
+  u8 artsbeep;
+  u8 unknown3s;
+  u8 unknown4s;
+  struct {
+    u8 header[3];
+    u8 mem_num;
+    u8 digits[16];
+  } autodial[8];
+  struct {
+    u8 header[3];
+    u8 mem_num;
+    u8 digits[32];
+  } autodial9_ro;
+  bbcd pagingcodec_ro[2];
+  bbcd pagingcodep[2];
+  struct {
+    bbcd digits[2];
+  } pagingcode[6];
+  u8 code_dec_c_en:1,
+     code_dec_p_en:1,
+     code_dec_1_en:1,
+     code_dec_2_en:1,
+     code_dec_3_en:1,
+     code_dec_4_en:1,
+     code_dec_5_en:1,
+     code_dec_6_en:1;
+  u8 pagingspeed;
+  u8 pagingdelay;
+  u8 pagingbell;
+  u8 paginganswer;
+
+  #seekto 0x0E30;
+  u8 squelch;       // squelch
+  u8 unknown0c;
+  u8 rptl:1,        // repeater input tracking
+     amod:1,        // auto mode
+     scnl:1,        // scan lamp
+     resm:1,        // scan resume mode 0=5sec, 1=carr
+     ars:1,         // automatic repeater shift
+     keybeep:1,     // keypad beep
+     lck:1,         // lock
+     unknown1c:1;
+  u8 lgt:1,
+     pageamsg:1,
+     unknown2c:1,
+     bclo:1,        // Busy channel lock out
+     unknown3c:2,
+     cwid_en:1,     // CWID off/on
+     tsav:1;        // TX save
+  u8 unknown4c:4,
+     artssped:1,    // ARTS/SPED: 0=15s, 1=25s
+     unknown5c:1,
+     rvhm:1,        // RVHM: 0=home, 1=rev
+     mon:1;         // MON: 0=mon, 1=tcal
+} settings;
+
+#seekto 0x080E;
+struct mem_struct vfo_mem[10];
+
+
+"""
+
+# 10 VFO memories: A145, A220, A380, A430, A800,
+#                  B145, B220, B380, B430, B800
+
+DUPLEX = ["", "-", "+"]
+MODES = ["FM", "AM", "WFM"]
+SKIP_VALUES = ["", "S"]
+TMODES = ["", "Tone", "TSQL", "DTCS"]
+TUNING_STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0]
+TONES = list(chirp_common.OLD_TONES)
+
+# CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ()+-=*/???|0123456789"
+# the = displays as an underscored dash on radio
+# the first ? is an uppercase delta - \xA7
+# the second ? is an uppercase gamma - \xD1
+# the thrid ? is an uppercase sigma - \xCF
+NUMERIC_CHARSET = list("0123456789")
+CHARSET = [str(x) for x in range(0, 10)] + \
+    [chr(x) for x in range(ord("A"), ord("Z")+1)] + \
+    list(" ()+-=*/" + ("\x00" * 3) + "|") + NUMERIC_CHARSET
+DTMFCHARSET = NUMERIC_CHARSET + list("ABCD*#")
+
+POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.0),
+                chirp_common.PowerLevel("L3", watts=2.5),
+                chirp_common.PowerLevel("L2", watts=1.0),
+                chirp_common.PowerLevel("L1", watts=0.1)]
+SPECIALS = ["L1", "U1", "L2", "U2", "L3", "U3", "L4", "U4", "L5", "U5", "UNK"]
+
+
+ at directory.register
 class FT50Radio(yaesu_clone.YaesuCloneModeRadio):
+    """Yaesu FT-50"""
     BAUD_RATE = 9600
     VENDOR = "Yaesu"
     MODEL = "FT-50"
 
+    _model = ""
     _memsize = 3723
     _block_lengths = [10, 16, 112, 16, 16, 1776, 1776, 1]
-    _block_delay = 0.15
+    # _block_delay = 0.15
+    _block_size = 8
+
+    @classmethod
+    def get_prompts(cls):
+        rp = chirp_common.RadioPrompts()
+        rp.pre_download = _(dedent("""\
+1. Turn radio off.
+2. Connect cable to MIC/SP jack.
+3. Press and hold [PTT] & Knob while turning the
+     radio on.
+4. <b>After clicking OK</b>, press the [PTT] switch to send image."""))
+        rp.pre_upload = _(dedent("""\
+1. Turn radio off.
+2. Connect cable to MIC/SP jack.
+3. Press and hold [PTT] & Knob while turning the
+     radio on.
+4. Press the [MONI] switch ("WAIT" will appear on the LCD).
+5. Press OK."""))
+        return rp
 
     def get_features(self):
         rf = chirp_common.RadioFeatures()
         rf.memory_bounds = (1, 100)
-        rf.has_dtcs_polarity = False
+        rf.valid_duplexes = DUPLEX
+        rf.valid_tmodes = TMODES
+        rf.valid_power_levels = POWER_LEVELS
+        rf.valid_tuning_steps = TUNING_STEPS
+        rf.valid_power_levels = POWER_LEVELS
+        rf.valid_characters = "".join(CHARSET)
+        rf.valid_name_length = 4
+        rf.valid_modes = MODES
+        # Specials not yet implementd
+        # rf.valid_special_chans = SPECIALS
+        rf.valid_bands = [(76000000, 200000000),
+                          (300000000, 540000000),
+                          (590000000, 999000000)]
+        # rf.can_odd_split = True
+        rf.has_ctone = False
         rf.has_bank = False
-        rf.valid_modes = ["FM", "WFM", "AM"]
+        rf.has_settings = True
+        rf.has_dtcs_polarity = False
+
         return rf
 
-    def _update_checksum(self):
-        ft50_ll.update_checksum(self._mmap)
+    def _checksums(self):
+        return [yaesu_clone.YaesuChecksum(0x0000, 0xE89)]
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
 
     def get_raw_memory(self, number):
-        return ft50_ll.get_raw_memory(self._mmap, number)
+        return repr(self._memobj.memory[number - 1])
 
     def get_memory(self, number):
-        return ft50_ll.get_memory(self._mmap, number)
+        mem = chirp_common.Memory()
+        _mem = self._memobj.memory[number-1]
+        _flg = self._memobj.flag[number-1]
+        mem.number = number
+
+        # if not _flg.visible:
+        #    mem.empty = True
+        if not _flg.used:
+            mem.empty = True
+            return mem
+
+        for i in _mem.name:
+            mem.name += CHARSET[i & 0x7F]
+        mem.name = mem.name.rstrip()
+
+        mem.freq = chirp_common.fix_rounded_step(int(_mem.freq) * 1000)
+        mem.duplex = DUPLEX[_mem.duplex]
+        mem.offset = chirp_common.fix_rounded_step(int(_mem.offset) * 1000)
+        mem.rtone = mem.ctone = TONES[_mem.tone]
+        mem.tmode = TMODES[_mem.tmode]
+        mem.mode = MODES[_mem.mode]
+        mem.tuning_step = TUNING_STEPS[_mem.tuning_step]
+        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
+        # Power is stored as 2 bits to describe the 3 low power levels
+        # High power is determined by a different bit.
+        if not _mem.ishighpower:
+            mem.power = POWER_LEVELS[3 - _mem.power]
+        else:
+            mem.power = POWER_LEVELS[0]
+        mem.skip = SKIP_VALUES[_flg.skip]
+
+        return mem
+
+    def set_memory(self, mem):
+        _mem = self._memobj.memory[mem.number-1]
+        _flg = self._memobj.flag[mem.number-1]
+        _flg_repeat = self._memobj.flag_repeat[mem.number-1]
+
+        if mem.empty:
+            _flg.used = False
+            self._wipe_memory_banks(mem)
+            return
+
+        if (len(mem.name) == 0):
+            _mem.name = [0x24] * 4
+            _mem.showname = 0
+        else:
+            _mem.showname = 1
+            for i in range(0, 4):
+                _mem.name[i] = CHARSET.index(mem.name.ljust(4)[i])
+
+        _mem.freq = int(mem.freq / 1000)
+        _mem.duplex = DUPLEX.index(mem.duplex)
+        _mem.offset = int(mem.offset / 1000)
+        _mem.mode = MODES.index(mem.mode)
+        _mem.tuning_step = TUNING_STEPS.index(mem.tuning_step)
+        if mem.power:
+            if (mem.power == POWER_LEVELS[0]):
+                # low power level is not changed when high power is selected
+                _mem.ishighpower = 0x01
+                if (_mem.power == 3):
+                    # Set low power to L3 (0x02) if it is
+                    # set to 3 (new object default)
+                    LOG.debug("SETTING DEFAULT?")
+                    _mem.power = 0x02
+            else:
+                _mem.ishighpower = 0x00
+                _mem.power = 3 - POWER_LEVELS.index(mem.power)
+        else:
+            _mem.ishighpower = 0x01
+            _mem.power = 0x02
+        _mem.tmode = TMODES.index(mem.tmode)
+        try:
+            _mem.tone = TONES.index(mem.rtone)
+        except ValueError:
+            raise errors.UnsupportedToneError(
+                ("This radio does not support tone %s" % mem.rtone))
+        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
+
+        _flg.skip = SKIP_VALUES.index(mem.skip)
+
+        # initialize new channel to safe defaults
+        if not mem.empty and not _flg.used:
+            _flg.used = True
+            _flg.mask = True        # Mask = True to be visible on radio
+            _mem.unknown1 = 0x00
+            _mem.unknown2 = 0x00
+            _mem.unknown3 = 0x00
+            _mem.unknown4 = 0x00
+            _mem.unknown5 = 0x00
+            _mem.unknown6 = 0x00
+            _mem.codememno = 0x02   # Not implemented in chirp
+            _mem.codeorpage = 0x00  # Not implemented in chirp
+
+        # Duplicate flags to repeated part in memory
+        _flg_repeat.skip = _flg.skip
+        _flg_repeat.mask = _flg.mask
+        _flg_repeat.used = _flg.used
+
+    def _decode_cwid(self, inarr):
+        LOG.debug("@_decode_chars, type: %s" % type(inarr))
+        LOG.debug(inarr)
+        outstr = ""
+        for i in inarr:
+            if i == 0xFF:
+                break
+            outstr += CHARSET[i & 0x7F]
+        LOG.debug(outstr)
+        return outstr.rstrip()
+
+    def _encode_cwid(self, instr, length=16):
+        LOG.debug("@_encode_chars, type: %s" % type(instr))
+        LOG.debug(instr)
+        outarr = []
+        instr = str(instr)
+        for i in range(0, length):
+            if i < len(instr):
+                outarr.append(CHARSET.index(instr[i]))
+            else:
+                outarr.append(0xFF)
+        return outarr
+
+    def get_settings(self):
+        _settings = self._memobj.settings
+        basic = RadioSettingGroup("basic", "Basic")
+        dtmf = RadioSettingGroup("dtmf", "DTMF Code & Paging")
+        arts = RadioSettingGroup("arts", "ARTS")
+        autodial = RadioSettingGroup("autodial", "AutoDial")
+        top = RadioSettings(basic, autodial, arts, dtmf)
+
+        rs = RadioSetting(
+                "squelch", "Squelch",
+                RadioSettingValueInteger(0, 15, _settings.squelch))
+        basic.append(rs)
+
+        rs = RadioSetting(
+                "keybeep", "Keypad Beep",
+                RadioSettingValueBoolean(_settings.keybeep))
+        basic.append(rs)
+
+        rs = RadioSetting(
+                "scnl", "Scan Lamp",
+                RadioSettingValueBoolean(_settings.scnl))
+        basic.append(rs)
+
+        options = ["off", "30m", "1h", "3h", "5h", "8h"]
+        rs = RadioSetting(
+                "apo", "APO time (hrs)",
+                RadioSettingValueList(options, options[_settings.apo]))
+        basic.append(rs)
+
+        options = ["off", "1m", "2.5m", "5m", "10m"]
+        rs = RadioSetting(
+                "timeout", "Time Out Timer",
+                RadioSettingValueList(options, options[_settings.timeout]))
+        basic.append(rs)
+
+        options = ["key", "dial", "key+dial", "ptt",
+                   "key+ptt", "dial+ptt", "all"]
+        rs = RadioSetting(
+                "lock", "Lock mode",
+                RadioSettingValueList(options, options[_settings.lock]))
+        basic.append(rs)
+
+        options = ["off", "0.2", "0.3", "0.5", "1.0", "2.0"]
+        rs = RadioSetting(
+                "rxsave", "RX Save (sec)",
+                RadioSettingValueList(options, options[_settings.rxsave]))
+        basic.append(rs)
+
+        options = ["5sec", "key", "tgl"]
+        rs = RadioSetting(
+                "lamp", "Lamp mode",
+                RadioSettingValueList(options, options[_settings.lamp]))
+        basic.append(rs)
+
+        options = ["off", "1", "3", "5", "8", "rpt"]
+        rs = RadioSetting(
+                "bell", "Bell Repetitions",
+                RadioSettingValueList(options, options[_settings.bell]))
+        basic.append(rs)
+
+        rs = RadioSetting(
+                "cwid_en", "CWID Enable",
+                RadioSettingValueBoolean(_settings.cwid_en))
+        arts.append(rs)
+
+        cwid = RadioSettingValueString(
+                0, 16, self._decode_cwid(_settings.cwid.get_value()))
+        cwid.set_charset(CHARSET)
+        rs = RadioSetting("cwid", "CWID", cwid)
+        arts.append(rs)
+
+        options = ["off", "rx", "tx", "trx"]
+        rs = RadioSetting(
+                "artsmode", "ARTS Mode",
+                RadioSettingValueList(
+                    options, options[_settings.artsmode]))
+        arts.append(rs)
+
+        options = ["off", "in range", "always"]
+        rs = RadioSetting(
+                "artsbeep", "ARTS Beep",
+                RadioSettingValueList(options, options[_settings.artsbeep]))
+        arts.append(rs)
+
+        for i in range(0, 8):
+            dialsettings = _settings.autodial[i]
+            dialstr = ""
+            for c in dialsettings.digits:
+                if c < len(DTMFCHARSET):
+                    dialstr += DTMFCHARSET[c]
+            dialentry = RadioSettingValueString(0, 16, dialstr)
+            dialentry.set_charset(DTMFCHARSET + list(" "))
+            rs = RadioSetting("autodial" + str(i+1),
+                              "AutoDial " + str(i+1), dialentry)
+            autodial.append(rs)
+
+        dialstr = ""
+        for c in _settings.autodial9_ro.digits:
+            if c < len(DTMFCHARSET):
+                dialstr += DTMFCHARSET[c]
+        dialentry = RadioSettingValueString(0, 32, dialstr)
+        dialentry.set_mutable(False)
+        rs = RadioSetting("autodial9_ro", "AutoDial 9 (read only)", dialentry)
+        autodial.append(rs)
+
+        options = ["50ms", "100ms"]
+        rs = RadioSetting(
+                "pagingspeed", "Paging Speed",
+                RadioSettingValueList(options, options[_settings.pagingspeed]))
+        dtmf.append(rs)
+
+        options = ["250ms", "450ms", "750ms", "1000ms"]
+        rs = RadioSetting(
+                "pagingdelay", "Paging Delay",
+                RadioSettingValueList(options, options[_settings.pagingdelay]))
+        dtmf.append(rs)
+
+        options = ["off", "1", "3", "5", "8", "rpt"]
+        rs = RadioSetting(
+                "pagingbell", "Paging Bell Repetitions",
+                RadioSettingValueList(options, options[_settings.pagingbell]))
+        dtmf.append(rs)
+
+        options = ["off", "ans", "for"]
+        rs = RadioSetting(
+                "paginganswer", "Paging Answerback",
+                RadioSettingValueList(options,
+                                      options[_settings.paginganswer]))
+        dtmf.append(rs)
+
+        rs = RadioSetting(
+                "code_dec_c_en", "Paging Code C Decode Enable",
+                RadioSettingValueBoolean(_settings.code_dec_c_en))
+        dtmf.append(rs)
+
+        _str = str(bitwise.bcd_to_int(_settings.pagingcodec_ro))
+        code = RadioSettingValueString(0, 3, _str)
+        code.set_charset(NUMERIC_CHARSET + list(" "))
+        code.set_mutable(False)
+        rs = RadioSetting("pagingcodec_ro", "Paging Code C (read only)", code)
+        dtmf.append(rs)
+
+        rs = RadioSetting(
+                "code_dec_p_en", "Paging Code P Decode Enable",
+                RadioSettingValueBoolean(_settings.code_dec_p_en))
+        dtmf.append(rs)
+
+        _str = str(bitwise.bcd_to_int(_settings.pagingcodep))
+        code = RadioSettingValueString(0, 3, _str)
+        code.set_charset(NUMERIC_CHARSET + list(" "))
+        rs = RadioSetting("pagingcodep", "Paging Code P", code)
+        dtmf.append(rs)
+
+        for i in range(0, 6):
+            num = str(i+1)
+            name = "code_dec_" + num + "_en"
+            rs = RadioSetting(
+                    name, "Paging Code " + num + " Decode Enable",
+                    RadioSettingValueBoolean(getattr(_settings, name)))
+            dtmf.append(rs)
+
+            _str = str(bitwise.bcd_to_int(_settings.pagingcode[i].digits))
+            code = RadioSettingValueString(0, 3, _str)
+            code.set_charset(NUMERIC_CHARSET + list(" "))
+            rs = RadioSetting("pagingcode" + num, "Paging Code " + num, code)
+            dtmf.append(rs)
+
+        return top
+
+    def set_settings(self, uisettings):
+        for element in uisettings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            if not element.changed():
+                continue
+            try:
+                setting = element.get_name()
+                _settings = self._memobj.settings
+                if re.match('autodial\d', setting):
+                    # set autodial fields
+                    dtmfstr = str(element.value).strip()
+                    newval = []
+                    for i in range(0, 16):
+                        if i < len(dtmfstr):
+                            newval.append(DTMFCHARSET.index(dtmfstr[i]))
+                        else:
+                            newval.append(0xFF)
+                    LOG.debug(newval)
+                    idx = int(setting[-1:]) - 1
+                    _settings = self._memobj.settings.autodial[idx]
+                    _settings.digits = newval
+                    continue
+                if (setting == "pagingcodep"):
+                    bitwise.int_to_bcd(_settings.pagingcodep,
+                                       int(element.value))
+                    continue
+                if re.match('pagingcode\d', setting):
+                    idx = int(setting[-1:]) - 1
+                    bitwise.int_to_bcd(_settings.pagingcode[idx].digits,
+                                       int(element.value))
+                    continue
+                newval = element.value
+                oldval = getattr(_settings, setting)
+                if setting == "cwid":
+                    newval = self._encode_cwid(newval)
+                LOG.debug("Setting %s(%s) <= %s" % (setting, oldval, newval))
+                setattr(_settings, setting, newval)
+            except Exception:
+                LOG.debug(element.get_name())
+                raise
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == cls._memsize
+
+    def sync_out(self):
+        self.update_checksums()
+        return _clone_out(self)
+
+
+def _clone_out(radio):
+    try:
+        return __clone_out(radio)
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with the radio: %s" % e)
+
+
+def __clone_out(radio):
+    pipe = radio.pipe
+    block_lengths = radio._block_lengths
+    total_written = 0
+
+    def _status():
+        status = chirp_common.Status()
+        status.msg = "Cloning to radio"
+        status.max = sum(block_lengths)
+        status.cur = total_written
+        radio.status_fn(status)
+
+    start = time.time()
+
+    blocks = 0
+    pos = 0
+    for block in radio._block_lengths:
+        blocks += 1
+        data = radio.get_mmap()[pos:pos + block]
+        # LOG.debug(util.hexprint(data))
+
+        recvd = ""
+        # Radio echos every block received
+        for byte in data:
+            time.sleep(0.01)
+            pipe.write(byte)
+            # flush & sleep so don't loose ack
+            pipe.flush()
+            time.sleep(0.015)
+            recvd += pipe.read(1)  # chew the echo
+        # LOG.debug(util.hexprint(recvd))
+        LOG.debug("Bytes sent: %i" % len(data))
+
+        # Radio does not ack last block
+        if (blocks < 8):
+            buf = pipe.read(block)
+            LOG.debug("ACK attempt: " + util.hexprint(buf))
+            if buf and buf[0] != chr(yaesu_clone.CMD_ACK):
+                buf = pipe.read(block)
+            if not buf or buf[-1] != chr(yaesu_clone.CMD_ACK):
+                raise errors.RadioError("Radio did not ack block %i" % blocks)
 
-    def set_memory(self, number):
-        return ft50_ll.set_memory(self._mmap, number)
+        total_written += len(data)
+        _status()
+        pos += block
 
-    def erase_memory(self, number):
-        return ft50_ll.erase_memory(self._mmap, number)
+    pipe.read(pos)  # Chew the echo if using a 2-pin cable
 
-    def filter_name(self, name):
-        return name[:4].upper()
+    LOG.debug("Clone completed in %i seconds" % (time.time() - start))
diff --git a/chirp/drivers/ft50_ll.py b/chirp/drivers/ft50_ll.py
deleted file mode 100644
index abf4b48..0000000
--- a/chirp/drivers/ft50_ll.py
+++ /dev/null
@@ -1,319 +0,0 @@
-# Copyright 2010 Dan Smith <dsmith at danplanet.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, util, errors, memmap
-import time
-import logging
-
-LOG = logging.getLogger(__name__)
-
-ACK = chr(0x06)
-
-MEM_LOC_BASE = 0x00AB
-MEM_LOC_SIZE = 16
-
-POS_DUPLEX = 1
-POS_TMODE = 2
-POS_TONE = 2
-POS_DTCS = 3
-POS_MODE = 4
-POS_FREQ = 5
-POS_OFFSET = 9
-POS_NAME = 11
-
-POS_USED = 0x079C
-
-CHARSET = [str(x) for x in range(0, 10)] + \
-    [chr(x) for x in range(ord("A"), ord("Z")+1)] + \
-    list(" ()+--*/???|0123456789")
-
-
-def send(s, data):
-    s.write(data)
-    r = s.read(len(data))
-    if len(r) != len(data):
-        raise errors.RadioError("Failed to read echo")
-
-
-def read_exact(s, count):
-    data = ""
-    i = 0
-    while len(data) < count:
-        if i == 3:
-            LOG.debug(util.hexprint(data))
-            raise errors.RadioError("Failed to read %i (%i) from radio" %
-                                    (count, len(data)))
-        elif i > 0:
-            LOG.info("Retry %i" % i)
-        data += s.read(count - len(data))
-        i += 1
-
-    return data
-
-
-def download(radio):
-    data = ""
-
-    radio.pipe.setTimeout(1)
-
-    for block in radio._block_lengths:
-        LOG.debug("Doing block %i" % block)
-        if block > 112:
-            step = 16
-        else:
-            step = block
-        for i in range(0, block, step):
-            # data += read_exact(radio.pipe, step)
-            chunk = radio.pipe.read(step*2)
-            LOG.debug("Length of chunk: %i" % len(chunk))
-            data += chunk
-            LOG.debug("Reading %i" % i)
-            time.sleep(0.1)
-            send(radio.pipe, ACK)
-            if radio.status_fn:
-                status = chirp_common.Status()
-                status.max = radio._memsize
-                status.cur = len(data)
-                status.msg = "Cloning from radio"
-                radio.status_fn(status)
-
-    r = radio.pipe.read(100)
-    send(radio.pipe, ACK)
-    LOG.debug("R: %i" % len(r))
-    LOG.debug(util.hexprint(r))
-
-    LOG.debug("Got: %i Expecting %i" % (len(data), radio._memsize))
-
-    return memmap.MemoryMap(data)
-
-
-def get_mem_offset(number):
-    return MEM_LOC_BASE + (number * MEM_LOC_SIZE)
-
-
-def get_raw_memory(map, number):
-    pos = get_mem_offset(number)
-    return memmap.MemoryMap(map[pos:pos+MEM_LOC_SIZE])
-
-
-def get_freq(mmap):
-    khz = (int("%02x" % (ord(mmap[POS_FREQ])), 10) * 100000) + \
-        (int("%02x" % ord(mmap[POS_FREQ+1]), 10) * 1000) + \
-        (int("%02x" % ord(mmap[POS_FREQ+2]), 10) * 10)
-    return khz / 10000.0
-
-
-def set_freq(mmap, freq):
-    val = util.bcd_encode(int(freq * 1000), width=6)[:3]
-    mmap[POS_FREQ] = val
-
-
-def get_tmode(mmap):
-    val = ord(mmap[POS_TMODE]) & 0xC0
-
-    tmodemap = {
-        0x00: "",
-        0x40: "Tone",
-        0x80: "TSQL",
-        0xC0: "DTCS",
-        }
-
-    return tmodemap[val]
-
-
-def set_tmode(mmap, tmode):
-    val = ord(mmap[POS_TMODE]) & 0x3F
-
-    tmodemap = {
-        "":      0x00,
-        "Tone":  0x40,
-        "TSQL":  0x80,
-        "DTCS":  0xC0,
-        }
-
-    val |= tmodemap[tmode]
-
-    mmap[POS_TMODE] = val
-
-
-def get_tone(mmap):
-    val = ord(mmap[POS_TONE]) & 0x3F
-
-    return chirp_common.TONES[val]
-
-
-def set_tone(mmap, tone):
-    val = ord(mmap[POS_TONE]) & 0xC0
-
-    mmap[POS_TONE] = val | chirp_common.TONES.index(tone)
-
-
-def get_dtcs(mmap):
-    val = ord(mmap[POS_DTCS])
-
-    return chirp_common.DTCS_CODES[val]
-
-
-def set_dtcs(mmap, dtcs):
-    mmap[POS_DTCS] = chirp_common.DTCS_CODES.index(dtcs)
-
-
-def get_offset(mmap):
-    khz = (int("%02x" % ord(mmap[POS_OFFSET]), 10) * 10) + \
-        (int("%02x" % (ord(mmap[POS_OFFSET+1]) >> 4), 10) * 1)
-
-    return khz / 1000.0
-
-
-def set_offset(mmap, offset):
-    val = util.bcd_encode(int(offset * 1000), width=4)[:3]
-    LOG.debug("Offset:\n%s" % util.hexprint(val))
-    mmap[POS_OFFSET] = val
-
-
-def get_duplex(mmap):
-    val = ord(mmap[POS_DUPLEX]) & 0x03
-
-    dupmap = {
-        0x00: "",
-        0x01: "-",
-        0x02: "+",
-        0x03: "split",
-        }
-
-    return dupmap[val]
-
-
-def set_duplex(mmap, duplex):
-    val = ord(mmap[POS_DUPLEX]) & 0xFC
-
-    dupmap = {
-        "":       0x00,
-        "-":      0x01,
-        "+":      0x02,
-        "split":  0x03,
-        }
-
-    mmap[POS_DUPLEX] = val | dupmap[duplex]
-
-
-def get_name(mmap):
-    name = ""
-    for x in mmap[POS_NAME:POS_NAME+4]:
-        if ord(x) >= len(CHARSET):
-            break
-        name += CHARSET[ord(x)]
-    return name
-
-
-def set_name(mmap, name):
-    val = ""
-    for i in name[:4].ljust(4):
-        val += chr(CHARSET.index(i))
-    mmap[POS_NAME] = val
-
-
-def get_mode(mmap):
-    val = ord(mmap[POS_MODE]) & 0x03
-
-    modemap = {
-        0x00: "FM",
-        0x01: "AM",
-        0x02: "WFM",
-        0x03: "WFM",
-        }
-
-    return modemap[val]
-
-
-def set_mode(mmap, mode):
-    val = ord(mmap[POS_MODE]) & 0xCF
-
-    modemap = {
-        "FM":   0x00,
-        "AM":   0x01,
-        "WFM":  0x02,
-        }
-
-    mmap[POS_MODE] = val | modemap[mode]
-
-
-def get_used(mmap, number):
-    return ord(mmap[POS_USED + number]) & 0x01
-
-
-def set_used(mmap, number, used):
-    val = ord(mmap[POS_USED + number]) & 0xFC
-    if used:
-        val |= 0x03
-    mmap[POS_USED + number] = val
-
-
-def get_memory(map, number):
-    index = number - 1
-    mmap = get_raw_memory(map, index)
-
-    mem = chirp_common.Memory()
-    mem.number = number
-    if not get_used(map, index):
-        mem.empty = True
-        return mem
-
-    mem.freq = get_freq(mmap)
-    mem.tmode = get_tmode(mmap)
-    mem.rtone = mem.ctone = get_tone(mmap)
-    mem.dtcs = get_dtcs(mmap)
-    mem.offset = get_offset(mmap)
-    mem.duplex = get_duplex(mmap)
-    mem.name = get_name(mmap)
-    mem.mode = get_mode(mmap)
-
-    return mem
-
-
-def set_memory(_map, mem):
-    index = mem.number - 1
-    mmap = get_raw_memory(_map, index)
-
-    if not get_used(_map, index):
-        mmap[0] = ("\x00" * MEM_LOC_SIZE)
-
-    set_freq(mmap, mem.freq)
-    set_tmode(mmap, mem.tmode)
-    set_tone(mmap, mem.rtone)
-    set_dtcs(mmap, mem.dtcs)
-    set_offset(mmap, mem.offset)
-    set_duplex(mmap, mem.duplex)
-    set_name(mmap, mem.name)
-    set_mode(mmap, mem.mode)
-
-    _map[get_mem_offset(index)] = mmap.get_packed()
-    set_used(_map, index, True)
-
-    return _map
-
-
-def erase_memory(map, number):
-    set_used(map, number-1, False)
-    return map
-
-
-def update_checksum(map):
-    cs = 0
-    for i in range(0, 3722):
-        cs += ord(map[i])
-    cs %= 256
-    LOG.debug("Checksum old=%02x new=%02x" % (ord(map[3722]), cs))
-    map[3722] = cs
diff --git a/chirp/drivers/ft60.py b/chirp/drivers/ft60.py
index 2bd29f7..9b7020e 100644
--- a/chirp/drivers/ft60.py
+++ b/chirp/drivers/ft60.py
@@ -216,6 +216,11 @@ struct {
 #seekto 0x09E;
 ul16 mbs;
 
+#seekto 0x0C8;
+struct {
+  u8 memory[16];
+} dtmf[9];
+
 struct mem {
   u8 used:1,
      unknown1:1,
@@ -280,6 +285,7 @@ POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.0),
                 chirp_common.PowerLevel("Low", watts=0.5)]
 STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
 SKIPS = ["", "S", "P"]
+DTMF_CHARS = list("0123456789ABCD*#")
 CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ [?]^__|`?$%&-()*+,-,/|;/=>?@"
 SPECIALS = ["%s%d" % (c, i + 1) for i in range(0, 50) for c in ('L', 'U')]
 
@@ -501,6 +507,28 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         ctcss.append(RadioSetting("dt_spd", "DTMF Autodialer Sending Speed",
                      RadioSettingValueList(opts, opts[_settings.dt_spd])))
 
+        # DT.WRT
+        for i in range(0, 9):
+            dtmf = self._memobj.dtmf[i]
+            str = ""
+            for c in dtmf.memory:
+                if c == 0xFF:
+                    break
+                if c < len(DTMF_CHARS):
+                    str += DTMF_CHARS[c]
+            val = RadioSettingValueString(0, 16, str, False)
+            val.set_charset(DTMF_CHARS + list("abcd"))
+            rs = RadioSetting("dtmf_%i" % i, 
+                        "DTMF Autodialer Memory %i" % (i + 1), val)
+            def apply_dtmf(s, obj):
+                str = s.value.get_value().upper().rstrip()
+                val = [DTMF_CHARS.index(x) for x in str]
+                for x in range(len(val), 16):
+                    val.append(0xFF)
+                obj.memory = val
+            rs.set_apply_callback(apply_dtmf, dtmf)
+            ctcss.append(rs)
+
         # EDG.BEP
         opts = ["OFF", "ON"]
         misc.append(RadioSetting("edg_bep", "Band Edge Beeper",
diff --git a/chirp/drivers/ft817.py b/chirp/drivers/ft817.py
index 1dd94bf..3016538 100644
--- a/chirp/drivers/ft817.py
+++ b/chirp/drivers/ft817.py
@@ -328,7 +328,7 @@ class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
 
     def _clone_in(self):
         # Be very patient with the radio
-        self.pipe.setTimeout(2)
+        self.pipe.timeout = 2
 
         start = time.time()
 
@@ -581,8 +581,9 @@ class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
                             "unknown you hit a bug!!")
 
         for key in cur_mem.immutable:
-            if cur_mem.__dict__[key] != mem.__dict__[key]:
-                raise errors.RadioError("Editing field `%s' " % key +
+            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)
diff --git a/chirp/drivers/ft90.py b/chirp/drivers/ft90.py
index f777d94..cc22a14 100644
--- a/chirp/drivers/ft90.py
+++ b/chirp/drivers/ft90.py
@@ -263,7 +263,7 @@ struct  {
 
     def _clone_in(self):
         # Be very patient with the radio
-        self.pipe.setTimeout(4)
+        self.pipe.timeout = 4
         start = time.time()
 
         data = ""
diff --git a/chirp/drivers/ftm350.py b/chirp/drivers/ftm350.py
index fde98f7..fc4839f 100644
--- a/chirp/drivers/ftm350.py
+++ b/chirp/drivers/ftm350.py
@@ -116,7 +116,7 @@ def _safe_read(radio, length):
 def _clone_in(radio):
     data = ""
 
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     attempts = 30
 
     data = memmap.MemoryMap("\x00" * (radio._memsize + 128))
@@ -165,7 +165,7 @@ def _clone_in(radio):
 
 
 def _clone_out(radio):
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
 
     # Seriously, WTF Yaesu?
     ranges = [
diff --git a/chirp/drivers/ic9x.py b/chirp/drivers/ic9x.py
index bdccf22..37568eb 100644
--- a/chirp/drivers/ic9x.py
+++ b/chirp/drivers/ic9x.py
@@ -100,7 +100,7 @@ class IC9xRadio(icf.IcomLiveRadio):
         icf.IcomLiveRadio.__init__(self, *args, **kwargs)
 
         if self.pipe:
-            self.pipe.setTimeout(0.1)
+            self.pipe.timeout = 0.1
 
         self.__memcache = {}
         self.__bankcache = {}
diff --git a/chirp/drivers/icomciv.py b/chirp/drivers/icomciv.py
index 60e8b19..8d128ce 100644
--- a/chirp/drivers/icomciv.py
+++ b/chirp/drivers/icomciv.py
@@ -4,16 +4,26 @@ import logging
 from chirp.drivers import icf
 from chirp import chirp_common, util, errors, bitwise, directory
 from chirp.memmap import MemoryMap
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+    RadioSettingValueList, RadioSettingValueBoolean
 
 LOG = logging.getLogger(__name__)
 
 MEM_FORMAT = """
 bbcd number[2];
-u8   unknown1;
+u8   unknown:3
+     split:1,
+     unknown_0:4;
 lbcd freq[5];
 u8   unknown2:5,
      mode:3;
+u8   filter;
+u8   unknown_1:3,
+     dig:1,
+     unknown_2:4;
 """
+
+
 # http://www.vk4adc.com/
 #     web/index.php/reference-information/49-general-ref-info/182-civ7400
 MEM_IC7000_FORMAT = """
@@ -191,6 +201,12 @@ class IcomCIVRadio(icf.IcomLiveRadio):
     _model = "\x00"
     _template = 0
 
+    # complete list of modes from CI-V documentation
+    # each radio supports a subset
+    # WARNING: "S-AM" and "PSK" are not valid (yet) for chirp
+    _MODES = ["LSB", "USB", "AM", "CW", "RTTY",
+              "FM", "WFM", "CWR", "RTTYR", "S-AM", "PSK"]
+
     def mem_to_ch_bnk(self, mem):
         l, h = self._bank_index_bounds
         bank_no = (mem // (h - l + 1)) + l
@@ -227,7 +243,7 @@ class IcomCIVRadio(icf.IcomLiveRadio):
         if self.pipe:
             self._willecho = self._detect_echo()
             LOG.debug("Interface echo: %s" % self._willecho)
-            self.pipe.setTimeout(1)
+            self.pipe.timeout = 1
 
         # f = Frame()
         # f.set_command(0x19, 0x00)
@@ -307,12 +323,16 @@ class IcomCIVRadio(icf.IcomLiveRadio):
         memobj = f.get_obj()
         LOG.debug(repr(memobj))
 
-        if memobj.skip == 1:
-            mem.skip = ""
-        else:
-            mem.skip = "S"
+        try:
+            if memobj.skip == 1:
+                mem.skip = ""
+            else:
+                mem.skip = "S"
+        except AttributeError:
+            pass
+
         mem.freq = int(memobj.freq)
-        mem.mode = self._rf.valid_modes[memobj.mode]
+        mem.mode = self._MODES[memobj.mode]
 
         if self._rf.has_name:
             mem.name = str(memobj.name).rstrip()
@@ -349,6 +369,28 @@ class IcomCIVRadio(icf.IcomLiveRadio):
         else:
             mem.immutable = ["offset"]
 
+        mem.extra = RadioSettingGroup("extra", "Extra")
+        try:
+            dig = RadioSetting("dig", "Digital",
+                               RadioSettingValueBoolean(bool(memobj.dig)))
+        except AttributeError:
+            pass
+        else:
+            dig.set_doc("Enable digital mode")
+            mem.extra.append(dig)
+
+        options = ["Wide", "Mid", "Narrow"]
+        try:
+            fil = RadioSetting(
+                "filter", "Filter",
+                RadioSettingValueList(options,
+                                      options[memobj.filter - 1]))
+        except AttributeError:
+            pass
+        else:
+            fil.set_doc("Filter settings")
+            mem.extra.append(fil)
+
         return mem
 
     def set_memory(self, mem):
@@ -386,9 +428,12 @@ class IcomCIVRadio(icf.IcomLiveRadio):
         if mem.skip == "S":
             memobj.skip = 0
         else:
-            memobj.skip = 1
+            try:
+                memobj.skip = 1
+            except KeyError:
+                pass
         memobj.freq = int(mem.freq)
-        memobj.mode = self._rf.valid_modes.index(mem.mode)
+        memobj.mode = self._MODES.index(mem.mode)
         if self._rf.has_name:
             memobj.name = mem.name.ljust(9)[:9]
 
@@ -424,6 +469,12 @@ class IcomCIVRadio(icf.IcomLiveRadio):
         elif self._rf.valid_duplexes:
             memobj.duplex = self._rf.valid_duplexes.index(mem.duplex)
 
+        for setting in mem.extra:
+            if setting.get_name() == "filter":
+                setattr(memobj, setting.get_name(), int(setting.value) + 1)
+            else:
+                setattr(memobj, setting.get_name(), setting.value)
+
         LOG.debug(repr(memobj))
         self._send_frame(f)
 
@@ -447,13 +498,14 @@ class Icom7200Radio(IcomCIVRadio):
         self._rf.has_ctone = False
         self._rf.has_offset = False
         self._rf.has_name = False
-        self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY"]
+        self._rf.has_tuning_step = False
+        self._rf.valid_modes = ["LSB", "USB", "AM", "CW", "RTTY",
+                                "CWR", "RTTYR"]
         self._rf.valid_tmodes = []
         self._rf.valid_duplexes = []
-        self._rf.valid_bands = [(1800000, 59000000)]
-        self._rf.valid_tuning_steps = []
+        self._rf.valid_bands = [(30000, 60000000)]
         self._rf.valid_skips = []
-        self._rf.memory_bounds = (1, 200)
+        self._rf.memory_bounds = (1, 201)
 
 
 @directory.register
diff --git a/chirp/drivers/kenwood_live.py b/chirp/drivers/kenwood_live.py
index f4a9d5a..efda1d5 100644
--- a/chirp/drivers/kenwood_live.py
+++ b/chirp/drivers/kenwood_live.py
@@ -155,7 +155,7 @@ class KenwoodLiveRadio(chirp_common.LiveRadio):
         self._memcache = {}
 
         if self.pipe:
-            self.pipe.setTimeout(0.1)
+            self.pipe.timeout = 0.1
             radio_id = get_id(self.pipe)
             if radio_id != self.MODEL.split(" ")[0]:
                 raise Exception("Radio reports %s (not %s)" % (radio_id,
diff --git a/chirp/drivers/kguv8d.py b/chirp/drivers/kguv8d.py
index cf130ce..bc2c299 100644
--- a/chirp/drivers/kguv8d.py
+++ b/chirp/drivers/kguv8d.py
@@ -1,4 +1,4 @@
-# Copyright 2014 Ron Wellsted <ron at wellsted.org.uk> M0RNW
+# Copyright 2014 Ron Wellsted <ron at m0rnw.uk> M0RNW
 #
 # 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
@@ -34,7 +34,7 @@ CMD_WR = 131
 MEM_VALID = 158
 
 AB_LIST = ["A", "B"]
-STEPS = [5.0, 6.25, 10.0, 12.5, 25.0, 50.0, 100.0]
+STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0, 50.0, 100.0]
 STEP_LIST = [str(x) for x in STEPS]
 ROGER_LIST = ["Off", "BOT", "EOT", "Both"]
 TIMEOUT_LIST = ["Off"] + [str(x) + "s" for x in range(15, 901, 15)]
@@ -45,14 +45,15 @@ LANGUAGE_LIST = ["Chinese", "English"]
 SCANMODE_LIST = ["TO", "CO", "SE"]
 PF1KEY_LIST = ["Call", "VFTX"]
 PF3KEY_LIST = ["Scan", "Lamp", "Tele Alarm", "SOS-CH", "Radio", "Disable"]
-WORKMODE_LIST = ["VFO", "Channel No.", "Frequency + No", "Name"]
+WORKMODE_LIST = ["VFO", "Channel No.", "Ch. No.+Freq.", "Ch. No.+Name"]
 BACKLIGHT_LIST = ["Always On"] + [str(x) + "s" for x in range(1, 21)] + \
                 ["Always Off"]
 OFFSET_LIST = ["+", "-"]
 PONMSG_LIST = ["Bitmap", "Battery Volts"]
 SPMUTE_LIST = ["QT", "QT+DTMF", "QT*DTMF"]
 DTMFST_LIST = ["DT-ST", "ANI-ST", "DT-ANI", "Off"]
-RPTSET_LIST = ["X-DIRPT", "X-TWRPT"]
+DTMF_TIMES = ["%s" % x for x in range(50, 501, 10)]
+RPTSET_LIST = ["X-TWRPT", "X-DIRRPT"]
 ALERTS = [1750, 2100, 1000, 1450]
 ALERTS_LIST = [str(x) for x in ALERTS]
 PTTID_LIST = ["BOT", "EOT", "Both"]
@@ -61,6 +62,8 @@ SCANGRP_LIST = ["All"] + ["%s" % x for x in range(1, 11)]
 SCQT_LIST = ["All", "Decoder", "Encoder"]
 SMUTESET_LIST = ["Off", "Tx", "Rx", "Tx/Rx"]
 POWER_LIST = ["Lo", "Hi"]
+HOLD_TIMES = ["Off"] + ["%s" % x for x in range(100, 5001, 100)]
+RPTMODE_LIST = ["Radio", "Repeater"]
 
 # memory slot 0 is not used, start at 1 (so need 1000 slots, not 999)
 # structure elements whose name starts with x are currently unidentified
@@ -148,8 +151,8 @@ _MEM_FORMAT = """
         u8      alert;
         u8      pf1_func;
         u8      pf3_func;
-        u8      workmode_a;
         u8      workmode_b;
+        u8      workmode_a;
         u8 x0845;
         u8      dtmf_tx_time;
         u8      dtmf_interval;
@@ -175,7 +178,7 @@ _MEM_FORMAT = """
         u8 x085d;
         u8 x085e;
         u8      single_display;
-        u8      ring;
+        u8      ring_time;
         u8      scg_a;
         u8      scg_b;
         u8 x0863;
@@ -643,7 +646,7 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
             _mem.power = self.POWER_LEVELS.index(mem.power)
         else:
             _mem.power = True
-        # TODO: sett the correct mute mode, for now just
+        # TODO: set the correct mute mode, for now just
         # set to mute mode to QT (not QT+DTMF or QT*DTMF)
         _mem.mute_mode = 0
 
@@ -658,7 +661,7 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         _settings = self._memobj.settings
         _vfoa = self._memobj.vfoa
         _vfob = self._memobj.vfob
-        cfg_grp = RadioSettingGroup("cfg_grp", "Configuration Settings")
+        cfg_grp = RadioSettingGroup("cfg_grp", "Configuration")
         vfoa_grp = RadioSettingGroup("vfoa_grp", "VFO A Settings")
         vfob_grp = RadioSettingGroup("vfob_grp", "VFO B Settings")
         key_grp = RadioSettingGroup("key_grp", "Key Settings")
@@ -671,87 +674,176 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         #
         # Configuration Settings
         #
+        rs = RadioSetting("channel_menu", "Menu available in channel mode",
+                          RadioSettingValueBoolean(_settings.channel_menu))
+        cfg_grp.append(rs)
         rs = RadioSetting("ponmsg", "Poweron message",
-                          RadioSettingValueList(
-                              PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
+                        RadioSettingValueList(
+                            PONMSG_LIST, PONMSG_LIST[_settings.ponmsg]))
         cfg_grp.append(rs)
         rs = RadioSetting("voice", "Voice Guide",
-                          RadioSettingValueBoolean(_settings.voice))
+                        RadioSettingValueBoolean(_settings.voice))
         cfg_grp.append(rs)
         rs = RadioSetting("language", "Language",
-                          RadioSettingValueList(
-                              LANGUAGE_LIST,
-                              LANGUAGE_LIST[_settings.language]))
+                        RadioSettingValueList(LANGUAGE_LIST,
+                            LANGUAGE_LIST[_settings.language]))
         cfg_grp.append(rs)
         rs = RadioSetting("timeout", "Timeout Timer",
-                          RadioSettingValueInteger(
-                              15, 900, _settings.timeout * 15, 15))
+                        RadioSettingValueInteger(15, 900,
+                            _settings.timeout * 15, 15))
         cfg_grp.append(rs)
         rs = RadioSetting("toalarm", "Timeout Alarm",
-                          RadioSettingValueInteger(0, 10, _settings.toalarm))
+                        RadioSettingValueInteger(0, 10, _settings.toalarm))
         cfg_grp.append(rs)
-        rs = RadioSetting("channel_menu", "Menu available in channel mode",
-                          RadioSettingValueBoolean(_settings.channel_menu))
+        rs = RadioSetting("roger_beep", "Roger Beep",
+                        RadioSettingValueBoolean(_settings.roger_beep))
         cfg_grp.append(rs)
         rs = RadioSetting("power_save", "Power save",
-                          RadioSettingValueBoolean(_settings.power_save))
+                        RadioSettingValueBoolean(_settings.power_save))
         cfg_grp.append(rs)
         rs = RadioSetting("autolock", "Autolock",
-                          RadioSettingValueBoolean(_settings.autolock))
+                        RadioSettingValueBoolean(_settings.autolock))
         cfg_grp.append(rs)
         rs = RadioSetting("keylock", "Keypad Lock",
-                          RadioSettingValueBoolean(_settings.keylock))
+                        RadioSettingValueBoolean(_settings.keylock))
         cfg_grp.append(rs)
         rs = RadioSetting("beep", "Keypad Beep",
-                          RadioSettingValueBoolean(_settings.keylock))
+                        RadioSettingValueBoolean(_settings.beep))
         cfg_grp.append(rs)
         rs = RadioSetting("stopwatch", "Stopwatch",
-                          RadioSettingValueBoolean(_settings.keylock))
+                        RadioSettingValueBoolean(_settings.stopwatch))
+        cfg_grp.append(rs)
+        rs = RadioSetting("backlight", "Backlight",
+                        RadioSettingValueList(BACKLIGHT_LIST,
+                            BACKLIGHT_LIST[_settings.backlight]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("dtmf_st", "DTMF Sidetone",
+                        RadioSettingValueList(DTMFST_LIST,
+                            DTMFST_LIST[_settings.dtmf_st]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("ani-id_sw", "ANI-ID Switch",
+                        RadioSettingValueBoolean(_settings.ani_sw))
+        cfg_grp.append(rs)
+        rs = RadioSetting("ptt-id_delay", "PTT-ID Delay",
+                        RadioSettingValueList(PTTID_LIST,
+                            PTTID_LIST[_settings.ptt_id]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("ring_time", "Ring Time",
+                        RadioSettingValueList(LIST_10,
+                            LIST_10[_settings.ring_time]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("scan_rev", "Scan Mode",
+                        RadioSettingValueList(SCANMODE_LIST,
+                            SCANMODE_LIST[_settings.scan_rev]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("vox", "VOX",
+                        RadioSettingValueList(LIST_10,
+                            LIST_10[_settings.vox]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("prich_sw", "Priority Channel Switch",
+                        RadioSettingValueBoolean(_settings.prich_sw))
+        cfg_grp.append(rs)
+        rs = RadioSetting("pri_ch", "Priority Channel",
+                        RadioSettingValueInteger(1, 999, _settings.pri_ch))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_mode", "Radio Mode",
+                        RadioSettingValueList(RPTMODE_LIST,
+                            RPTMODE_LIST[_settings.rpt_mode]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_set", "Repeater Setting",
+                        RadioSettingValueList(RPTSET_LIST,
+                            RPTSET_LIST[_settings.rpt_set]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_spk", "Repeater Mode Speaker",
+                        RadioSettingValueBoolean(_settings.rpt_spk))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_ptt", "Repeater PTT",
+                        RadioSettingValueBoolean(_settings.rpt_ptt))
+        cfg_grp.append(rs)
+        rs = RadioSetting("dtmf_tx_time", "DTMF Tx Duration",
+                        RadioSettingValueList(DTMF_TIMES,
+                            DTMF_TIMES[_settings.dtmf_tx_time]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("dtmf_interval", "DTMF Interval",
+                        RadioSettingValueList(DTMF_TIMES,
+                            DTMF_TIMES[_settings.dtmf_interval]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("alert", "Alert Tone",
+                        RadioSettingValueList(ALERTS_LIST,
+                            ALERTS_LIST[_settings.alert]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_tone", "Repeater Tone",
+                        RadioSettingValueBoolean(_settings.rpt_tone))
+        cfg_grp.append(rs)
+        rs = RadioSetting("rpt_hold", "Repeater Hold Time",
+                        RadioSettingValueList(HOLD_TIMES,
+                            HOLD_TIMES[_settings.rpt_hold]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("scan_det", "Scan DET",
+                        RadioSettingValueBoolean(_settings.scan_det))
+        cfg_grp.append(rs)
+        rs = RadioSetting("sc_qt", "SC-QT",
+                        RadioSettingValueList(SCQT_LIST,
+                            SCQT_LIST[_settings.smuteset]))
+        cfg_grp.append(rs)
+        rs = RadioSetting("smuteset", "SubFreq Mute",
+                        RadioSettingValueList(SMUTESET_LIST,
+                            SMUTESET_LIST[_settings.smuteset]))
+        cfg_grp.append(rs)
+        _pwd = "".join(map(chr, _settings.mode_sw_pwd))
+        val = RadioSettingValueString(0, 6, _pwd)
+        val.set_mutable(True)
+        rs = RadioSetting("mode_sw_pwd", "Mode Switch Password", val)
+        cfg_grp.append(rs)
+        _pwd = "".join(map(chr, _settings.reset_pwd))
+        val = RadioSettingValueString(0, 6, _pwd)
+        val.set_mutable(True)
+        rs = RadioSetting("reset_pwd", "Reset Password", val)
         cfg_grp.append(rs)
-
         #
         # VFO A Settings
         #
         rs = RadioSetting("vfoa_mode", "VFO A Workmode",
-                          RadioSettingValueList(
-                              WORKMODE_LIST,
+                        RadioSettingValueList(WORKMODE_LIST,
                               WORKMODE_LIST[_settings.workmode_a]))
         vfoa_grp.append(rs)
         rs = RadioSetting("vfoa_chan", "VFO A Channel",
-                          RadioSettingValueInteger(1, 999, _settings.work_cha))
+                        RadioSettingValueInteger(1, 999, _settings.work_cha))
         vfoa_grp.append(rs)
         rs = RadioSetting("rxfreqa", "VFO A Rx Frequency",
-                          RadioSettingValueInteger(
+                        RadioSettingValueInteger(
                               134000000, 520000000, _vfoa.rxfreq * 10, 5000))
         vfoa_grp.append(rs)
-        #   u32   txoffset;
+        rs = RadioSetting("txoffa", "VFO A Tx Offset",
+                        RadioSettingValueInteger(
+                              0, 520000000, _vfoa.txoffset * 10, 5000))
+        vfoa_grp.append(rs)
         #   u16   rxtone;
         #   u16   txtone;
-        #   u8    unknown1:6,
         rs = RadioSetting("vfoa_power", "VFO A Power",
-                          RadioSettingValueList(
+                        RadioSettingValueList(
                               POWER_LIST, POWER_LIST[_vfoa.power]))
         vfoa_grp.append(rs)
-        #         unknown2:1;
-        #   u8    unknown3:1,
         #         shift_dir:2
-        #         unknown4:2,
-        rs = RadioSetting("vfoa_mute_mode", "VFO A Mute",
-                          RadioSettingValueList(
-                              SPMUTE_LIST, SPMUTE_LIST[_vfoa.mute_mode]))
-        vfoa_grp.append(rs)
         rs = RadioSetting("vfoa_iswide", "VFO A NBFM",
-                          RadioSettingValueList(
+                        RadioSettingValueList(
                               BANDWIDTH_LIST, BANDWIDTH_LIST[_vfoa.iswide]))
         vfoa_grp.append(rs)
+        rs = RadioSetting("vfoa_mute_mode", "VFO A Mute",
+                        RadioSettingValueList(
+                              SPMUTE_LIST, SPMUTE_LIST[_vfoa.mute_mode]))
+        vfoa_grp.append(rs)
         rs = RadioSetting("vfoa_step", "VFO A Step (kHz)",
-                          RadioSettingValueList(
+                        RadioSettingValueList(
                               STEP_LIST, STEP_LIST[_vfoa.step]))
         vfoa_grp.append(rs)
         rs = RadioSetting("vfoa_squelch", "VFO A Squelch",
-                          RadioSettingValueList(
+                        RadioSettingValueList(
                               LIST_10, LIST_10[_vfoa.squelch]))
         vfoa_grp.append(rs)
+        rs = RadioSetting("bcl_a", "Busy Channel Lock-out A",
+                        RadioSettingValueBoolean(_settings.bcl_a))
+        vfoa_grp.append(rs)
         #
         # VFO B Settings
         #
@@ -767,26 +859,25 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
                           RadioSettingValueInteger(
                               134000000, 520000000, _vfob.rxfreq * 10, 5000))
         vfob_grp.append(rs)
-        #   u32   txoffset;
+        rs = RadioSetting("txoffb", "VFO B Tx Offset",
+                          RadioSettingValueInteger(
+                              0, 520000000, _vfob.txoffset * 10, 5000))
+        vfob_grp.append(rs)
         #   u16   rxtone;
         #   u16   txtone;
-        #   u8    unknown1:6,
         rs = RadioSetting("vfob_power", "VFO B Power",
                           RadioSettingValueList(
                               POWER_LIST, POWER_LIST[_vfob.power]))
         vfob_grp.append(rs)
-        #         unknown2:1;
-        #   u8    unknown3:1,
         #         shift_dir:2
-        #         unknown4:2,
-        rs = RadioSetting("vfob_mute_mode", "VFO B Mute",
-                          RadioSettingValueList(
-                              SPMUTE_LIST, SPMUTE_LIST[_vfob.mute_mode]))
-        vfob_grp.append(rs)
         rs = RadioSetting("vfob_iswide", "VFO B NBFM",
                           RadioSettingValueList(
                               BANDWIDTH_LIST, BANDWIDTH_LIST[_vfob.iswide]))
         vfob_grp.append(rs)
+        rs = RadioSetting("vfob_mute_mode", "VFO B Mute",
+                          RadioSettingValueList(
+                              SPMUTE_LIST, SPMUTE_LIST[_vfob.mute_mode]))
+        vfob_grp.append(rs)
         rs = RadioSetting("vfob_step", "VFO B Step (kHz)",
                           RadioSettingValueList(
                               STEP_LIST, STEP_LIST[_vfob.step]))
@@ -795,7 +886,9 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
                           RadioSettingValueList(
                               LIST_10, LIST_10[_vfob.squelch]))
         vfob_grp.append(rs)
-
+        rs = RadioSetting("bcl_b", "Busy Channel Lock-out B",
+                        RadioSettingValueBoolean(_settings.bcl_b))
+        vfob_grp.append(rs)
         #
         # Key Settings
         #
diff --git a/chirp/drivers/puxing.py b/chirp/drivers/puxing.py
index 7a1ad55..4ceecb5 100644
--- a/chirp/drivers/puxing.py
+++ b/chirp/drivers/puxing.py
@@ -352,7 +352,7 @@ class Puxing777Radio(chirp_common.CloneModeRadio):
 
 def puxing_2r_prep(radio):
     """Do the Puxing 2R identification dance"""
-    radio.pipe.setTimeout(0.2)
+    radio.pipe.timeout = 0.2
     radio.pipe.write("PROGRAM\x02")
     ack = radio.pipe.read(1)
     if ack != "\x06":
diff --git a/chirp/drivers/th9000.py b/chirp/drivers/th9000.py
index ef52da7..58e138e 100644
--- a/chirp/drivers/th9000.py
+++ b/chirp/drivers/th9000.py
@@ -380,7 +380,7 @@ def _read(radio, length):
 
 
 def _ident(radio):
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     _echo_write(radio,"PROGRAM")
     response = radio.pipe.read(3)
     if response != "QX\06":
diff --git a/chirp/drivers/th9800.py b/chirp/drivers/th9800.py
index 545c3f4..1dbdaa7 100644
--- a/chirp/drivers/th9800.py
+++ b/chirp/drivers/th9800.py
@@ -688,7 +688,7 @@ def _upload(radio, memsize=0xF400, blocksize=0x80):
     """Upload to TYT TH-9800"""
     data = _identify(radio)
 
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
 
     if data != radio._mmap[:radio._mmap_offset]:
         raise errors.RadioError(
diff --git a/chirp/drivers/th_uv3r.py b/chirp/drivers/th_uv3r.py
index 77eca93..10273a2 100644
--- a/chirp/drivers/th_uv3r.py
+++ b/chirp/drivers/th_uv3r.py
@@ -100,7 +100,7 @@ class TYTUV3RRadio(chirp_common.CloneModeRadio):
         return rf
 
     def sync_in(self):
-        self.pipe.setTimeout(2)
+        self.pipe.timeout = 2
         self._mmap = tyt_uv3r_download(self)
         self.process_mmap()
 
diff --git a/chirp/drivers/th_uv3r25.py b/chirp/drivers/th_uv3r25.py
index bba73d4..6100d4b 100644
--- a/chirp/drivers/th_uv3r25.py
+++ b/chirp/drivers/th_uv3r25.py
@@ -82,7 +82,7 @@ class TYTUV3R25Radio(TYTUV3RRadio):
         return rf
 
     def sync_in(self):
-        self.pipe.setTimeout(2)
+        self.pipe.timeout = 2
         self._mmap = tyt_uv3r_download(self)
         self.process_mmap()
 
diff --git a/chirp/drivers/th_uvf8d.py b/chirp/drivers/th_uvf8d.py
index f20042e..e302784 100644
--- a/chirp/drivers/th_uvf8d.py
+++ b/chirp/drivers/th_uvf8d.py
@@ -84,7 +84,7 @@ def tyt_uvf8d_upload(radio):
     """Upload to TYT TH-UVF8D"""
     data = uvf8d_identify(radio)
 
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
 
     if data != radio._mmap[:32]:
         raise errors.RadioError("Model mis-match: \n%s\n%s" %
@@ -481,7 +481,7 @@ class TYTUVF8DRadio(chirp_common.CloneModeRadio):
         _settings = self._memobj.settings
 
         group = RadioSettingGroup("basic", "Basic")
-        top = RadioSettings(basic)
+        top = RadioSettings(group)
 
         group.append(RadioSetting(
                 "mode", "Mode",
diff --git a/chirp/drivers/thd72.py b/chirp/drivers/thd72.py
index a1167c6..20a86b7 100644
--- a/chirp/drivers/thd72.py
+++ b/chirp/drivers/thd72.py
@@ -1,4 +1,5 @@
 # Copyright 2010 Vernon Mauery <vernon at mauery.org>
+# Copyright 2016 Angus Ainslie <angus at akkea.ca>
 #
 # 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
@@ -15,6 +16,9 @@
 
 from chirp import chirp_common, errors, util, directory
 from chirp import bitwise, memmap
+from chirp.settings import RadioSettingGroup, RadioSetting, RadioSettings
+from chirp.settings import RadioSettingValueInteger, RadioSettingValueString
+from chirp.settings import RadioSettingValueList, RadioSettingValueBoolean
 import time
 import struct
 import sys
@@ -65,6 +69,24 @@ struct {
   u8   passwd[6];
 } frontmatter;
 
+#seekto 0x0300;
+struct {
+  char power_on_msg[8];
+  u8 unknown0[8];
+  u8 unknown1[2];
+  u8 lamp_timer;
+  u8 contrast;
+  u8 battery_saver;
+  u8 APO;
+  u8 unknown2;
+  u8 key_beep;
+  u8 unknown3[8];
+  u8 unknown4;
+  u8 balance;
+  u8 unknown5[23];
+  u8 lamp_control;
+} settings;
+
 #seekto 0x0c00;
 struct {
   u8 disabled:7,
@@ -161,7 +183,6 @@ DUPLEX_REV = {
 EXCH_R = "R\x00\x00\x00\x00"
 EXCH_W = "W\x00\x00\x00\x00"
 
-
 # Uploads result in "MCP Error" and garbage data in memory
 # Clone driver disabled in favor of error-checking live driver.
 @directory.register
@@ -176,6 +197,14 @@ class THD72Radio(chirp_common.CloneModeRadio):
     _model = ""  # FIXME: REMOVE
     _dirty_blocks = []
 
+    _LCD_CONTRAST = ["Level %d" % x for x in range(1, 16)]
+    _LAMP_CONTROL = ["Manual", "Auto"]
+    _LAMP_TIMER = ["Seconds %d" % x for x in range(2, 11)]
+    _BATTERY_SAVER = [ "OFF", "0.03 Seconds", "0.2 Seconds", "0.4 Seconds", "0.6 Seconds", "0.8 Seconds", "1 Seconds", "2 Seconds", "3 Seconds", "4 Seconds", "5 Seconds" ]
+    _APO = [ "OFF", "15 Minutes", "30 Minutes", "60 Minutes" ]
+    _AUDIO_BALANCE = [ "Center", "A +50%", "A +100%", "B +50%", "B +100%" ]
+    _KEY_BEEP = [ "OFF", "Radio & GPS", "Radio Only", "GPS Only" ]
+
     def get_features(self):
         rf = chirp_common.RadioFeatures()
         rf.memory_bounds = (0, 1031)
@@ -186,6 +215,7 @@ class THD72Radio(chirp_common.CloneModeRadio):
         rf.has_dtcs_polarity = False
         rf.has_tuning_step = False
         rf.has_bank = False
+        rf.has_settings = True
         rf.valid_tuning_steps = []
         rf.valid_modes = MODES_REV.keys()
         rf.valid_tmodes = TMODES_REV.keys()
@@ -474,6 +504,191 @@ class THD72Radio(chirp_common.CloneModeRadio):
             "\x80\xc8\xb3\x08\x00\x01\x00\x08" + \
             "\x08\x00\xc0\x27\x09\x00\x00\xff"
 
+    def _get_settings(self):
+        top = RadioSettings(self._get_display_settings(),
+                            self._get_audio_settings(),
+                            self._get_battery_settings())
+        return top
+
+    def set_settings(self, settings):
+        _mem = self._memobj
+        for element in settings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            if not element.changed():
+                continue
+            try:
+                if element.has_apply_callback():
+                    LOG.debug("Using apply callback")
+                    try:
+                        element.run_apply_callback()
+                    except NotImplementedError as e:
+                        LOG.error(e)
+                    continue
+
+                # Find the object containing setting.
+                obj = _mem
+                bits = element.get_name().split(".")
+                setting = bits[-1]
+                for name in bits[:-1]:
+                    if name.endswith("]"):
+                        name, index = name.split("[")
+                        index = int(index[:-1])
+                        obj = getattr(obj, name)[index]
+                    else:
+                        obj = getattr(obj, name)
+
+                try:
+                    old_val = getattr(obj, setting)
+                    LOG.debug("Setting %s(%r) <= %s" % (
+                            element.get_name(), old_val, element.value))
+                    setattr(obj, setting, element.value)
+                except AttributeError as e:
+                    LOG.error("Setting %s is not in the memory map: %s" %
+                              (element.get_name(), e))
+            except Exception, e:
+                LOG.debug(element.get_name())
+                raise
+
+    def get_settings(self):
+        try:
+            return self._get_settings()
+        except:
+            import traceback
+            LOG.error("Failed to parse settings: %s", traceback.format_exc())
+            return None
+
+    @classmethod
+    def apply_power_on_msg(cls, setting, obj):
+        message = setting.value.get_value()
+        setattr(obj, "power_on_msg", cls._add_ff_pad(message, 8))
+
+    def apply_lcd_contrast(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._LCD_CONTRAST.index(rawval) + 1
+        obj.contrast = val
+
+    def apply_lamp_control(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._LAMP_CONTROL.index(rawval)
+        obj.lamp_control = val
+
+    def apply_lamp_timer(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._LAMP_TIMER.index(rawval) + 2
+        obj.lamp_timer = val
+
+    def _get_display_settings(self):
+        menu = RadioSettingGroup("display", "Display")
+        display_settings = self._memobj.settings
+
+        val = RadioSettingValueString(
+            0, 8, str(display_settings.power_on_msg).rstrip("\xFF"))
+        rs = RadioSetting("display.power_on_msg", "Power on message", val)
+        rs.set_apply_callback(self.apply_power_on_msg, display_settings)
+        menu.append(rs)
+        
+        val = RadioSettingValueList(
+            self._LCD_CONTRAST,
+            self._LCD_CONTRAST[display_settings.contrast - 1])
+        rs = RadioSetting("display.contrast", "LCD Contrast",
+                          val)
+        rs.set_apply_callback(self.apply_lcd_contrast, display_settings)
+        menu.append(rs)
+        
+        val = RadioSettingValueList(
+            self._LAMP_CONTROL,
+            self._LAMP_CONTROL[display_settings.lamp_control])
+        rs = RadioSetting("display.lamp_control", "Lamp Control",
+                          val)
+        rs.set_apply_callback(self.apply_lamp_control, display_settings)
+        menu.append(rs)
+
+        val = RadioSettingValueList(
+            self._LAMP_TIMER,
+            self._LAMP_TIMER[display_settings.lamp_timer - 2])
+        rs = RadioSetting("display.lamp_timer", "Lamp Timer",
+                          val)
+        rs.set_apply_callback(self.apply_lamp_timer, display_settings)
+        menu.append(rs)
+
+        return menu
+
+    def apply_battery_saver(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._BATTERY_SAVER.index(rawval)
+        obj.battery_saver = val
+
+    def apply_APO(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._APO.index(rawval)
+        obj.APO = val
+
+    def _get_battery_settings(self):
+        menu = RadioSettingGroup("battery", "Battery")
+        battery_settings = self._memobj.settings
+
+        val = RadioSettingValueList(
+            self._BATTERY_SAVER,
+            self._BATTERY_SAVER[battery_settings.battery_saver])
+        rs = RadioSetting("battery.battery_saver", "Battery Saver",
+                          val)
+        rs.set_apply_callback(self.apply_battery_saver, battery_settings)
+        menu.append(rs)
+
+        val = RadioSettingValueList(
+            self._APO,
+            self._APO[battery_settings.APO])
+        rs = RadioSetting("battery.APO", "Auto Power Off",
+                          val)
+        rs.set_apply_callback(self.apply_APO, battery_settings)
+        menu.append(rs)
+
+        return menu
+
+    def apply_balance(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._AUDIO_BALANCE.index(rawval)
+        obj.balance = val
+
+    def apply_key_beep(cls, setting, obj):
+        rawval = setting.value.get_value()
+        val = cls._KEY_BEEP.index(rawval)
+        obj.key_beep = val
+
+    def _get_audio_settings(self):
+        menu = RadioSettingGroup("audio", "Audio")
+        audio_settings = self._memobj.settings
+
+        val = RadioSettingValueList(
+            self._AUDIO_BALANCE,
+            self._AUDIO_BALANCE[audio_settings.balance])
+        rs = RadioSetting("audio.balance", "Balance",
+                          val)
+        rs.set_apply_callback(self.apply_balance, audio_settings)
+        menu.append(rs)
+
+        val = RadioSettingValueList(
+            self._KEY_BEEP,
+            self._KEY_BEEP[audio_settings.key_beep])
+        rs = RadioSetting("audio.key_beep", "Key Beep",
+                          val)
+        rs.set_apply_callback(self.apply_key_beep, audio_settings)
+        menu.append(rs)
+
+        return menu
+
+    @staticmethod
+    def _add_ff_pad(val, length):
+        return val.ljust(length, "\xFF")[:length]
+
+    @classmethod
+    def _strip_ff_pads(cls, messages):
+        result = []
+        for msg_text in messages:
+            result.append(str(msg_text).rstrip("\xFF"))
+        return result
 
 if __name__ == "__main__":
     import sys
diff --git a/chirp/drivers/thuv1f.py b/chirp/drivers/thuv1f.py
index 3771633..c39f17a 100644
--- a/chirp/drivers/thuv1f.py
+++ b/chirp/drivers/thuv1f.py
@@ -73,7 +73,7 @@ def uvf1_upload(radio):
     """Upload to TYT TH-UVF1"""
     data = uvf1_identify(radio)
 
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
 
     if data != radio._mmap[:16]:
         raise errors.RadioError("Unable to talk to this model")
diff --git a/chirp/drivers/tk760.py b/chirp/drivers/tk270.py
similarity index 58%
copy from chirp/drivers/tk760.py
copy to chirp/drivers/tk270.py
index 5fb46a4..4481258 100644
--- a/chirp/drivers/tk760.py
+++ b/chirp/drivers/tk270.py
@@ -1,4 +1,4 @@
-# Copyright 2016 Dan Smith <dsmith at danplanet.com>
+# Copyright 2016 Pavel Milanes CO7WT, <co7wt at frcuba.co.cu> <pavelmc 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
@@ -13,8 +13,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Driver author: Pavel CO7WT, co7wt at frcuba.co.cu, pavelmc at gmail.com
-
 import time
 import struct
 import logging
@@ -30,72 +28,71 @@ from chirp.settings import RadioSettingGroup, RadioSetting, \
 from textwrap import dedent
 
 MEM_FORMAT = """
-#seekto 0x0000;
+#seekto 0x0010;
 struct {
   lbcd rxfreq[4];
   lbcd txfreq[4];
-} memory[32];
-
-#seekto 0x0100;
-struct {
   lbcd rx_tone[2];
   lbcd tx_tone[2];
-} tone[32];
+  u8 unknown41:1,
+     unknown42:1,
+     power:1,           // high power set (1=off)
+     shift:1,           // Shift (1=off)
+     busy:1,            // Busy lock (1=off)
+     unknown46:1,
+     unknown47:1,
+     unknown48:1;
+  u8 rxen;              // xff if off, x00 if enabled (if chan sel = 00)
+  u8 txen;              // xff if off, x00 if enabled
+  u8 unknown7;
+} memory[32];
 
-#seekto 0x0180;
-struct {
-  u8 unknown0:1,
-     unknown1:1,
-     wide:1,            // wide: 1 = wide, 0 = narrow
-     power:1,           // power: 1 = high, 0 = low
-     busy_lock:1,       // busy lock:  1 = off, 0 = on
-     pttid:1,           // ptt id:  1 = off, 0 = on
-     dtmf:1,            // dtmf signaling:  1 = off, 0 = on
-     twotone:1;         // 2-tone signaling:  1 = off, 0 = on
-} ch_settings[32];
-
-#seekto 0x02B0;
+#seekto 0x0338;
+u8 scan[4];             //  4 bytes / bit LSBF for the channel
+
+#seekto 0x033C;
+u8 active[4];           //  4 bytes / bit LSBF for the active cha
+                        // active = 0
+
+#seekto 0x0340;
 struct {
-    u8 unknown10[16];      // x02b0
-    u8 unknown11[16];      // x02c0
-    u8 active[4];            // x02d0
-    u8 scan[4];              // x02d4
-    u8 unknown12[8];         // x02d8
-    u8 unknown13;          // x02e0
-    u8 kMON;                 // 0x02d1 MON Key
-    u8 kA;                   // 0x02d2 A Key
-    u8 kSCN;                 // 0x02d3 SCN Key
-    u8 kDA;                  // 0x02d4 D/A Key
-    u8 unknown14;            // x02e5
-    u8 min_vol;              // x02e6 byte 0-31 0 = off
-    u8 poweron_tone;         // x02e7 power on tone 0 = off, 1 = on
-    u8 tot;                  // x02e8 Time out Timer 0 = off, 1 = 30s (max 300)
-    u8 unknown15[3];         // x02e9-x02eb
-    u8 dealer_tuning;        // x02ec ? bit 0? 0 = off, 1 = on
-    u8 clone;                // x02ed ? bit 0? 0 = off, 1 = on
-    u8 unknown16[2];         // x02ee-x2ef
-    u8 unknown17[16];      // x02f0
-    u8 unknown18[5];       // x0300
-    u8 clear2transpond;      // x0305 byte 0 = off, 1 = on
-    u8 off_hook_decode;      // x0306 byte 0 = off, 1 = on
-    u8 off_hook_hornalert;   // x0307 byte 0 = off, 1 = on
-    u8 unknown19[8];         // x0308-x030f
-    u8 unknown20[16];      // x0310
+  u8 kMoni;             // monitor key funcion
+  u8 kScan;             // scan key funcion
+  u8 kDial;             // dial key funcion
+  u8 kTa;               // ta key funcion
+  u8 kLo;               // low key funcion
+  u8 unknown40[7];
+  // 0x034c
+  u8 tot;               // TOT val * 30 steps (x00-0xa)
+  u8 tot_alert;         // TOT pre-alert val * 10 steps, (x00-x19)
+  u8 tot_rekey;         // TOT rekey val, 0-60, (x00-x3c)
+  u8 tot_reset;         // TOT reset val, 0-15, (x00-x0f)
+  // 0x0350
+  u8 sql;               // SQL level val, 0-9 (default 6)
+  u8 unknown50[12];
+  u8 unknown30:1,
+     unknown31:1,
+     dealer:1,          // dealer & test mode (1=on)
+     add:1,             // add/del from the scan (1=on)
+     unknown34:1,
+     batt_save:1,       // Battery save (1=on)
+     unknown36:1,
+     beep:1;            // beep on tone (1=on)
+  u8 unknown51[2];
 } settings;
-"""
 
-KEYS = {
-    0x00: "Disabled",
-    0x01: "Monitor",
-    0x02: "Talk Around",
-    0x03: "Horn Alert",
-    0x04: "Public Adress",
-    0x05: "Auxiliary",
-    0x06: "Scan",
-    0x07: "Scan Del/Add",
-    0x08: "Home Channel",
-    0x09: "Operator Selectable Tone"
-}
+#seekto 0x03f0;
+struct {
+  u8 batt_level;        // inverted (ff-val)
+  u8 sq_tight;          // sq tight (ff-val)
+  u8 sq_open;           // sq open (ff-val)
+  u8 high_power;        // High power
+  u8 qt_dev;            // QT deviation
+  u8 dqt_dev;           // DQT deviation
+  u8 low_power;         // low power
+} tune;
+
+"""
 
 MEM_SIZE = 0x400
 BLOCK_SIZE = 8
@@ -105,15 +102,24 @@ TIMEOUT = 0.05  # from 0.03 up it' s safe, we set in 0.05 for a margin
 
 POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1),
                 chirp_common.PowerLevel("High", watts=5)]
-MODES = ["NFM", "FM"]
 SKIP_VALUES = ["", "S"]
 TONES = chirp_common.TONES
 #TONES.remove(254.1)
 DTCS_CODES = chirp_common.DTCS_CODES
 
-TOT = ["off"] + ["%s" % x for x in range(30, 330, 30)]
-VOL = ["off"] + ["%s" % x for x in range(1, 32)]
+# some vars for the UI
+off = ["off"]
+TOT = off + ["%s" % x for x in range(30, 330, 30)]
+TOT_A = off + ["%s" % x for x in range(10, 260, 10)]
+TOT_RK = off + ["%s" % x for x in range(1, 61)]
+TOT_RS = off + ["%s" % x for x in range(1, 16)]
+SQL = off + ["%s" % x for x in range(1, 10)]
 
+# keys
+MONI = off + ["Monitor momentary", "Monitor lock", "SQ off momentary"]
+SCAN = off + ["Carrier operated (COS)", "Time operated (TOS)"]
+YESNO = ["Enabled", "Disabled"]
+TA = off + ["Turn around", "Reverse"]
 
 def rawrecv(radio, amount):
     """Raw read from the radio device"""
@@ -150,7 +156,7 @@ def make_frame(cmd, addr, data=""):
         if len(data) == 8:
             return ts + data
         else:
-            raise errors.InvalidValueError("Data length of unexpected length")
+            raise errors.InvalidValueError("Data of unexpected length to send")
 
 
 def handshake(radio, msg="", full=False):
@@ -164,7 +170,7 @@ def handshake(radio, msg="", full=False):
     if ack != ACK_CMD:
         #close_radio(radio)
         mesg = "Handshake failed: " + msg
-        raise errors.RadioError(mesg)
+        raise Exception(mesg)
 
 
 def recv(radio):
@@ -196,13 +202,29 @@ def open_radio(radio):
         msg = "Serial error: Can't set serial line discipline"
         raise errors.RadioError(msg)
 
+    # we will try to open the radio 5 times, this is an improved mechanism
     magic = "PROGRAM"
-    for i in range(0, len(magic)):
-        ack = rawrecv(radio, 1)
-        time.sleep(0.05)
-        send(radio, magic[i])
+    exito = False
+    for i in range(0, 5):
+        for i in range(0, len(magic)):
+            ack = rawrecv(radio, 1)
+            time.sleep(0.05)
+            send(radio, magic[i])
+
+        try:
+            handshake(radio, "Radio not entering Program mode")
+            exito = True
+            break
+        except:
+            LOG.debug("Attempt #%s, failed, trying again" % i)
+            pass
+
+    # check if we had EXITO
+    if exito is False:
+        msg = "The radio did not accept program mode after five tries.\n"
+        msg += "Check you interface cable and power cycle your radio."
+        raise errors.RadioError(msg)
 
-    handshake(radio, "Radio not entering Program mode")
     rawsend(radio, "\x02")
     ident = rawrecv(radio, 8)
     handshake(radio, "Comm error after ident", True)
@@ -213,13 +235,6 @@ def open_radio(radio):
             (ident[0:5], radio.TYPE)
         raise errors.RadioError(msg)
 
-    # DEBUG
-    #print("Full ident string is %s" % util.hexprint(ident))
-
-    # this is needed, I don't know why, yet
-    send(radio, make_frame("W", 0x03e1, "\xff\x01" + "\xff" * 6))
-    handshake(radio, "Comm error  after setup", True)
-
 
 def do_download(radio):
     """This is your download function"""
@@ -267,23 +282,20 @@ def do_upload(radio):
         radio.status_fn(status)
 
         block = addr * BLOCK_SIZE
-        if block > 0x0378:
-            # it seems that from this point forward is read only !?!?!?
+        # Beyond 0x03d0 the data is not writable
+        if block > 0x3d0:
             continue
 
-        send(radio, make_frame("W", block,
-                               radio.get_mmap()[block:block + BLOCK_SIZE]))
-
-        # DEBUG
-        #print("Block: %04x, Pos: %04x" % (addr, addr * BLOCK_SIZE))
+        data = radio.get_mmap()[block:block + BLOCK_SIZE]
+        send(radio, make_frame("W", block, data))
 
-        time.sleep(0.1)
+        time.sleep(0.02)
         handshake(radio, "Rx error in block %03i" % addr)
 
 
 def get_rid(data):
     """Extract the radio identification from the firmware"""
-    rid = data[0x0378:0x0380]
+    rid = data[0x03d0:0x03d8]
     # we have to invert rid
     nrid = ""
     for i in range(1, len(rid) + 1):
@@ -307,13 +319,14 @@ def model_match(cls, data):
         return False
 
 
-class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
+class Kenwood_P60_Radio(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
     """Kenwood Mobile Family 60 Radios"""
     VENDOR = "Kenwood"
-    _range = [350000000, 500000000]  # don't mind, it will be overited
+    _range = [350000000, 512000000]  # don't mind, it will be overited
     _upper = 32
     VARIANT = ""
     MODEL = ""
+    _kind = ""
 
     @classmethod
     def get_prompts(cls):
@@ -322,27 +335,24 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
             ('This driver is experimental and for personal use only.'
              'It has a limited set of features, but the most used.\n'
              '\n'
-             'The most notorius missing features are this:\n'
-             '=> PTT ID, in fact it is disabled if detected\n'
-             '=> Priority / Home channel\n'
-             '=> DTMF/2-Tone\n'
-             '=> Others\n'
+             'This is a first release, so it will contains bugs, use it'
+             'on your own risk, keep a mem backup with the original '
+             'software at hand\n'
              '\n'
-             'If you need one of this, get your official software to do it'
-             'and raise and issue on the chirp site about it; maybe'
-             'it will be implemented in the future.'
              )
         rp.pre_download = _(dedent("""\
-            Follow this instructions to download your info:
+            Follow this instructions to read your radio:
             1 - Turn off your radio
             2 - Connect your interface cable
-            3 - Do the download of your radio data
+            3 - Turn on your radio
+            4 - Do the download of your radio data
             """))
         rp.pre_upload = _(dedent("""\
-            Follow this instructions to download your info:
+            Follow this instructions to write your radio:
             1 - Turn off your radio
             2 - Connect your interface cable
-            3 - Do the upload of your radio data
+            3 - Turn on your radio
+            4 - Do the upload of your radio data
             """))
         return rp
 
@@ -353,13 +363,13 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         rf.has_tuning_step = False
         rf.has_name = False
         rf.has_offset = True
-        rf.has_mode = True
+        rf.has_mode = False
         rf.has_dtcs = True
         rf.has_rx_dtcs = True
         rf.has_dtcs_polarity = True
         rf.has_ctone = True
         rf.has_cross = True
-        rf.valid_modes = MODES
+        rf.valid_modes = ["FM"]
         rf.valid_duplexes = ["", "-", "+", "off"]
         rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
         rf.valid_cross_modes = [
@@ -413,6 +423,9 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
             self._VARIANT += str(self._range[0]/1000000) + "-"
             self._VARIANT += str(self._range[1]/1000000) + " Mhz"
 
+            # DEBUG
+            #print self._VARIANT
+
         except KeyError:
             LOG.debug("Wrong Kenwood radio, ID or unknown variant")
             LOG.debug(util.hexprint(rid))
@@ -420,20 +433,22 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
                 "Wrong Kenwood radio, ID or unknown variant, see LOG output.")
 
     def _prep_data(self):
-        """Prepare the areas in the memmap to do a consistend write
-        it has to make an update on the x200 flag data"""
+        """Prepare the areas in the memmap to do a consistent write
+        it has to make an update on the x280 flag data"""
         achs = 0
 
         for i in range(0, self._upper):
             if self.get_active(i) is True:
                 achs += 1
 
-        # The x0200 area has the settings for the DTMF/2-Tone per channel,
-        # as by default any of this radios has the DTMF IC installed;
-        # we clean this areas
-        fldata = "\x00\xf0\xff\xff\xff" * achs + \
-            "\xff" * (5 * (self._upper - achs))
-        self._fill(0x0200, fldata)
+        # The x0280 area has the settings for the DTMF/2-Tone per channel,
+        # as we don't support this feature yet,
+        # we disabled by cleaning the data
+        #fldata = "\x00\xf0\xff\xff\xff" * achs + \
+            #"\xff" * (5 * (self._upper - achs))
+
+        fldata = "\xFF" * 5 * self._upper
+        self._fill(0x0280, fldata)
 
     def _fill(self, offset, data):
         """Fill an specified area of the memmap with the passed data"""
@@ -449,6 +464,37 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
     def get_raw_memory(self, number):
         return repr(self._memobj.memory[number])
 
+    def get_active(self, chan):
+        """Get the channel active status from the 4 bytes array on the eeprom"""
+        byte = int(chan/8)
+        bit = chan % 8
+        res = self._memobj.active[byte] & (pow(2, bit))
+        res = not bool(res)
+
+        # DEBUG
+        #print("GET Chan %s, Byte %s, Bit %s, Active %s" % (chan, byte, bit, int(res)))
+
+        return res
+
+    def set_active(self, chan, value=True):
+        """Set the channel active status from UI to the mem_map"""
+        byte = int(chan/8)
+        bit = chan % 8
+
+        # DEBUG
+        #print("SET Chan %s, Byte %s, Bit % s" % (chan, byte, bit))
+
+        # get the actual value to see if I need to change anything
+        actual = self.get_active(chan)
+        if actual != bool(value):
+            # DEBUG
+            #print "VALUE %s fliping" % int(not value)
+
+            # I have to flip the value
+            rbyte = self._memobj.active[byte]
+            rbyte = rbyte ^ pow(2, bit)
+            self._memobj.active[byte] = rbyte
+
     def decode_tone(self, val):
         """Parse the tone data to decode from mem, it returns:
         Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
@@ -486,7 +532,7 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         result = "S"
         byte = int(chan/8)
         bit = chan % 8
-        res = self._memobj.settings.scan[byte] & (pow(2, bit))
+        res = self._memobj.scan[byte] & (pow(2, bit))
         if res > 0:
             result = ""
 
@@ -501,36 +547,13 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         actual = self.get_scan(chan)
         if actual != value:
             # I have to flip the value
-            rbyte = self._memobj.settings.scan[byte]
+            rbyte = self._memobj.scan[byte]
             rbyte = rbyte ^ pow(2, bit)
-            self._memobj.settings.scan[byte] = rbyte
-
-    def get_active(self, chan):
-        """Get the channel active status from the 4 bytes array on the eeprom
-        then from the bits on the byte, return True/False"""
-        byte = int(chan/8)
-        bit = chan % 8
-        res = self._memobj.settings.active[byte] & (pow(2, bit))
-        return bool(res)
-
-    def set_active(self, chan, value=True):
-        """Set the channel active status from UI to the mem_map"""
-        byte = int(chan/8)
-        bit = chan % 8
-
-        # get the actual value to see if I need to change anything
-        actual = self.get_active(chan)
-        if actual != bool(value):
-            # I have to flip the value
-            rbyte = self._memobj.settings.active[byte]
-            rbyte = rbyte ^ pow(2, bit)
-            self._memobj.settings.active[byte] = rbyte
+            self._memobj.scan[byte] = rbyte
 
     def get_memory(self, number):
         """Get the mem representation from the radio image"""
         _mem = self._memobj.memory[number - 1]
-        _tone = self._memobj.tone[number - 1]
-        _ch = self._memobj.ch_settings[number - 1]
 
         # Create a high-level memory object to return to the UI
         mem = chirp_common.Memory()
@@ -538,17 +561,19 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         # Memory number
         mem.number = number
 
-        if _mem.get_raw()[0] == "\xFF" or not self.get_active(number - 1):
+        if _mem.get_raw()[0] == "\xFF":
             mem.empty = True
-            # but is not enough, you have to crear the memory in the mmap
-            # to get it ready for the sync_out process
-            _mem.set_raw("\xFF" * 8)
+            # but is not enough, you have to clear the memory in the mmap
+            # to get it ready for the sync_out process, just in case
+            _mem.set_raw("\xFF" * 16)
+            # set the channel to inactive state
+            self.set_active(number - 1, False)
             return mem
 
         # Freq and offset
         mem.freq = int(_mem.rxfreq) * 10
         # tx freq can be blank
-        if _mem.get_raw()[4] == "\xFF":
+        if _mem.get_raw()[4] == "\xFF" or int(_mem.txen) == 255:
             # TX freq not set
             mem.offset = 0
             mem.duplex = "off"
@@ -565,29 +590,31 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
                 mem.offset = 0
 
         # power
-        mem.power = POWER_LEVELS[_ch.power]
-
-        # wide/marrow
-        mem.mode = MODES[_ch.wide]
+        mem.power = POWER_LEVELS[int(_mem.power)]
 
         # skip
         mem.skip = self.get_scan(number - 1)
 
         # tone data
         rxtone = txtone = None
-        txtone = self.decode_tone(_tone.tx_tone)
-        rxtone = self.decode_tone(_tone.rx_tone)
+        txtone = self.decode_tone(_mem.tx_tone)
+        rxtone = self.decode_tone(_mem.rx_tone)
         chirp_common.split_tone_decode(mem, txtone, rxtone)
 
         # Extra
         # bank and number in the channel
         mem.extra = RadioSettingGroup("extra", "Extra")
 
-        bl = RadioSetting("busy_lock", "Busy Channel lock",
+        bl = RadioSetting("busy", "Busy Channel lock",
                           RadioSettingValueBoolean(
-                              not bool(_ch.busy_lock)))
+                              not bool(_mem.busy)))
         mem.extra.append(bl)
 
+        sf = RadioSetting("shift", "Beat Shift",
+                          RadioSettingValueBoolean(
+                              not bool(_mem.shift)))
+        mem.extra.append(sf)
+
         return mem
 
     def set_memory(self, mem):
@@ -596,55 +623,57 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
 
         # Get a low-level memory object mapped to the image
         _mem = self._memobj.memory[mem.number - 1]
-        _tone = self._memobj.tone[mem.number - 1]
-        _ch = self._memobj.ch_settings[mem.number - 1]
 
         # Empty memory
         if mem.empty:
-            _mem.set_raw("\xFF" * 8)
-            # empty the active bit
+            _mem.set_raw("\xFF" * 16)
             self.set_active(mem.number - 1, False)
             return
 
         # freq rx
         _mem.rxfreq = mem.freq / 10
 
+        # rx enabled if valid channel,
+        # set tx to on, we decide if off after duplex = off
+        _mem.rxen = 0
+        _mem.txen = 0
+
         # freq tx
         if mem.duplex == "+":
             _mem.txfreq = (mem.freq + mem.offset) / 10
         elif mem.duplex == "-":
             _mem.txfreq = (mem.freq - mem.offset) / 10
         elif mem.duplex == "off":
+            # set tx freq on the memap to xff
             for i in range(0, 4):
                 _mem.txfreq[i].set_raw("\xFF")
+            # erase the txen flag
+            _mem.txen = 255
         else:
             _mem.txfreq = mem.freq / 10
 
         # tone data
         ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
             chirp_common.split_tone_encode(mem)
-        self.encode_tone(_tone.tx_tone, txmode, txtone, txpol)
-        self.encode_tone(_tone.rx_tone, rxmode, rxtone, rxpol)
+        self.encode_tone(_mem.tx_tone, txmode, txtone, txpol)
+        self.encode_tone(_mem.rx_tone, rxmode, rxtone, rxpol)
 
-        # power, default power is low
+        # power, default power is high, as the low is configurable via a key
         if mem.power is None:
-            mem.power = POWER_LEVELS[0]
-
-        _ch.power = POWER_LEVELS.index(mem.power)
+            mem.power = POWER_LEVELS[1]
 
-        # wide/marrow
-        _ch.wide = MODES.index(mem.mode)
+        _mem.power = POWER_LEVELS.index(mem.power)
 
         # skip
         self.set_scan(mem.number - 1, mem.skip)
 
+        # set as active
+        self.set_active(mem.number - 1, True)
+
         # extra settings
         for setting in mem.extra:
             setattr(_mem, setting.get_name(), setting.value)
 
-        # set the mem a active in the _memmap
-        self.set_active(mem.number - 1)
-
         return mem
 
     @classmethod
@@ -670,65 +699,110 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
 
         # basic features of the radio
         basic = RadioSettingGroup("basic", "Basic Settings")
-        # buttons
-        fkeys = RadioSettingGroup("keys", "Front keys config")
+        # keys
+        fkeys = RadioSettingGroup("keys", "Function keys")
 
         top = RadioSettings(basic, fkeys)
 
         # Basic
-        val = RadioSettingValueString(0, 35, self.VARIANT)
+        val = RadioSettingValueString(0, 35, self._VARIANT)
         val.set_mutable(False)
         mod = RadioSetting("not.mod", "Radio version", val)
         basic.append(mod)
 
+        beep = RadioSetting("settings.beep", "Beep tone",
+                             RadioSettingValueBoolean(
+                                 bool(sett.beep)))
+        basic.append(beep)
+
+        bsave = RadioSetting("settings.batt_save", "Battery save",
+                             RadioSettingValueBoolean(
+                                 bool(sett.batt_save)))
+        basic.append(bsave)
+
+        deal = RadioSetting("settings.dealer", "Dealer & Test",
+                             RadioSettingValueBoolean(
+                                 bool(sett.dealer)))
+        basic.append(deal)
+
+        add = RadioSetting("settings.add", "Del / Add feature",
+                             RadioSettingValueBoolean(
+                                 bool(sett.add)))
+        basic.append(add)
+
+        # In some cases the values that follows can be 0xFF (HARD RESET)
+        # so we need to take and validate that
+        if int(sett.tot) == 0xff:
+            # 120 sec
+            sett.tot = 4
+        if int(sett.tot_alert) == 0xff:
+            # 10 secs
+            sett.tot_alert = 1
+        if int(sett.tot_rekey) == 0xff:
+            # off
+            sett.tot_rekey = 0
+        if int(sett.tot_reset) == 0xff:
+            # off
+            sett.tot_reset = 0
+        if int(sett.sql) == 0xff:
+            # a confortable level ~6
+            sett.sql = 6
+
         tot = RadioSetting("settings.tot", "Time Out Timer (TOT)",
                            RadioSettingValueList(TOT, TOT[int(sett.tot)]))
         basic.append(tot)
 
-        minvol = RadioSetting("settings.min_vol", "Minimum volume",
-                              RadioSettingValueList(VOL,
-                                                    VOL[int(sett.min_vol)]))
-        basic.append(minvol)
+        tota = RadioSetting("settings.tot_alert", "TOT pre-plert",
+                           RadioSettingValueList(TOT_A, TOT_A[int(sett.tot_alert)]))
+        basic.append(tota)
 
-        ptone = RadioSetting("settings.poweron_tone", "Power On tone",
-                             RadioSettingValueBoolean(
-                                 bool(sett.poweron_tone)))
-        basic.append(ptone)
+        totrk = RadioSetting("settings.tot_rekey", "TOT rekey time",
+                           RadioSettingValueList(TOT_RK, TOT_RK[int(sett.tot_rekey)]))
+        basic.append(totrk)
 
-        sprog = RadioSetting("settings.dealer_tuning", "Dealer Tuning",
-                             RadioSettingValueBoolean(
-                                 bool(sett.dealer_tuning)))
-        basic.append(sprog)
+        totrs = RadioSetting("settings.tot_reset", "TOT reset time",
+                           RadioSettingValueList(TOT_RS, TOT_RS[int(sett.tot_reset)]))
+        basic.append(totrs)
 
-        clone = RadioSetting("settings.clone", "Allow clone",
-                             RadioSettingValueBoolean(
-                                 bool(sett.clone)))
-        basic.append(clone)
+        sql = RadioSetting("settings.sql", "Squelch level",
+                           RadioSettingValueList(SQL, SQL[int(sett.sql)]))
+        basic.append(sql)
 
         # front keys
-        mon = RadioSetting("settings.kMON", "MON",
-                           RadioSettingValueList(KEYS.values(),
-                           KEYS.values()[KEYS.keys().index(
-                               int(sett.kMON))]))
+        m = int(sett.kMoni)
+        if m > 3:
+            m = 1
+        mon = RadioSetting("settings.kMoni", "Monitor",
+                           RadioSettingValueList(MONI, MONI[m]))
         fkeys.append(mon)
 
-        a = RadioSetting("settings.kA", "A",
-                         RadioSettingValueList(KEYS.values(),
-                         KEYS.values()[KEYS.keys().index(
-                             int(sett.kA))]))
-        fkeys.append(a)
-
-        scn = RadioSetting("settings.kSCN", "SCN",
-                           RadioSettingValueList(KEYS.values(),
-                           KEYS.values()[KEYS.keys().index(
-                               int(sett.kSCN))]))
+        s = int(sett.kScan)
+        if s > 3:
+            s = 1
+        scn = RadioSetting("settings.kScan", "Scan",
+                           RadioSettingValueList(SCAN, SCAN[s]))
         fkeys.append(scn)
 
-        da = RadioSetting("settings.kDA", "D/A",
-                          RadioSettingValueList(KEYS.values(),
-                          KEYS.values()[KEYS.keys().index(
-                              int(sett.kDA))]))
-        fkeys.append(da)
+        d = int(sett.kDial)
+        if d > 1:
+            d = 0
+        dial = RadioSetting("settings.kDial", "Dial",
+                           RadioSettingValueList(YESNO, YESNO[d]))
+        fkeys.append(dial)
+
+        t = int(sett.kTa)
+        if t > 2:
+            t = 2
+        ta = RadioSetting("settings.kTa", "Ta",
+                           RadioSettingValueList(TA, TA[t]))
+        fkeys.append(ta)
+
+        l = int(sett.kLo)
+        if l > 1:
+            l = 0
+        low = RadioSetting("settings.kLo", "Low",
+                           RadioSettingValueList(YESNO, YESNO[l]))
+        fkeys.append(low)
 
         return top
 
@@ -755,12 +829,10 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
                 obj = getattr(mobj, inter)
                 value = element.value
 
-                # case keys, with special config
-                if setting[0] == "k":
-                    value = KEYS.keys()[KEYS.values().index(str(value))]
-
                 # integers case + special case
-                if setting in ["tot", "min_vol"]:
+                if setting in ["tot", "tot_alert", "tot_rekey", \
+                               "tot_reset", "sql", "kMoni", "kScan", \
+                               "kDial", "kTa", "kLo"]:
                     # catching the "off" values as zero
                     try:
                         value = int(value)
@@ -768,7 +840,7 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
                         value = 0
 
                 # Bool types + inverted
-                if setting in ["poweron_tone", "dealer_tuning", "clone"]:
+                if setting in ["beep", "batt_save", "dealer", "add"]:
                     value = bool(value)
 
             # Apply al configs done
@@ -777,75 +849,98 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
             setattr(obj, setting, value)
 
 
-# This are the oldest family 60 models (Black keys), just mobiles support here
+# This are the oldest family 60 models just portables support here
+# Info striped from a hexdump inside the preogram and hack over a
+# tk-270
 
 @directory.register
-class TK760_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-760 Radios"""
-    MODEL = "TK-760"
-    TYPE = "M0760"
+class TK260_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-260 Radios"""
+    MODEL = "TK-260"
+    TYPE = "P0260"
     VARIANTS = {
-        "M0760\x01\x00\x00": (32, 136, 156, "K2"),
-        "M0760\x00\x00\x00": (32, 148, 174, "K")
+        "P0260\x20\x00\x00": (4, 136, 150, "F2"),
+        "P0260\x21\x00\x00": (4, 150, 174, "F1"),
         }
 
 
 @directory.register
-class TK762_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-762 Radios"""
-    MODEL = "TK-762"
-    TYPE = "M0762"
+class TK270_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-270 Radios"""
+    MODEL = "TK-270"
+    TYPE = "P0270"
     VARIANTS = {
-        "M0762\x01\x00\x00": (2, 136, 156, "K2"),
-        "M0762\x00\x00\x00": (2, 148, 174, "K")
+        "P0270\x10\x00\x00": (32, 136, 150, "F2"),
+        "P0270\x11\x00\x00": (32, 150, 174, "F1"),
         }
 
 
 @directory.register
-class TK768_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-768 Radios"""
-    MODEL = "TK-768"
-    TYPE = "M0768"
+class TK272_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-272 Radios"""
+    MODEL = "TK-272"
+    TYPE = "P0272"
     VARIANTS = {
-        "M0768\x21\x00\x00": (32, 136, 156, "K2"),
-        "M0768\x20\x00\x00": (32, 148, 174, "K")
+        "P0272\x10\x00\x00": (10, 136, 150, "F2"),
+        "P0272\x11\x00\x00": (10, 150, 174, "F1"),
         }
 
 
 @directory.register
-class TK860_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-860 Radios"""
-    MODEL = "TK-860"
-    TYPE = "M0860"
+class TK278_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-278 Radios"""
+    MODEL = "TK-278"
+    TYPE = "P0278"
     VARIANTS = {
-        "M0860\x05\x00\x00": (32, 406, 430, "F4"),
-        "M0860\x04\x00\x00": (32, 488, 512, "F3"),
-        "M0860\x03\x00\x00": (32, 470, 496, "F2"),
-        "M0860\x02\x00\x00": (32, 450, 476, "F1")
+        "P0278\x00\x00\x00": (32, 136, 150, "F2"),
+        "P0278\x01\x00\x00": (32, 150, 174, "F1"),
         }
 
 
 @directory.register
-class TK862_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-862 Radios"""
-    MODEL = "TK-862"
-    TYPE = "M0862"
+class TK360_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-360 Radios"""
+    MODEL = "TK-360"
+    TYPE = "P0360"
     VARIANTS = {
-        "M0862\x05\x00\x00": (2, 406, 430, "F4"),
-        "M0862\x04\x00\x00": (2, 488, 512, "F3"),
-        "M0862\x03\x00\x00": (2, 470, 496, "F2"),
-        "M0862\x02\x00\x00": (2, 450, 476, "F1")
+        "P0360\x24\x00\x00": (4, 450, 470, "F1"),
+        "P0360\x25\x00\x00": (4, 470, 490, "F2"),
+        "P0360\x26\x00\x00": (4, 490, 512, "F3"),
+        "P0360\x23\x00\x00": (4, 406, 430, "F4"),
         }
 
 
 @directory.register
-class TK868_Radio(Kenwood_M60_Radio):
-    """Kenwood TK-868 Radios"""
-    MODEL = "TK-868"
-    TYPE = "M0868"
+class TK370_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-370 Radios"""
+    MODEL = "TK-370"
+    TYPE = "P0370"
     VARIANTS = {
-        "M0868\x25\x00\x00": (2, 406, 430, "F4"),
-        "M0868\x24\x00\x00": (2, 488, 512, "F3"),
-        "M0868\x23\x00\x00": (2, 470, 496, "F2"),
-        "M0868\x22\x00\x00": (2, 450, 476, "F1")
+        "P0370\x14\x00\x00": (32, 450, 470, "F1"),
+        "P0370\x15\x00\x00": (32, 470, 490, "F2"),
+        "P0370\x16\x00\x00": (32, 490, 512, "F3"),
+        "P0370\x13\x00\x00": (32, 406, 430, "F4"),
         }
+
+
+ at directory.register
+class TK372_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-372 Radios"""
+    MODEL = "TK-372"
+    TYPE = "P0372"
+    VARIANTS = {
+        "P0372\x14\x00\x00": (10, 450, 470, "F1"),
+        "P0372\x15\x00\x00": (10, 470, 490, "F2"),
+        }
+
+
+ at directory.register
+class TK378_Radio(Kenwood_P60_Radio):
+    """Kenwood TK-378 Radios"""
+    MODEL = "TK-378"
+    TYPE = "P0378"
+    VARIANTS = {
+        "P0378\x04\x00\x00": (32, 370, 470, "SP1"),
+        "P0378\x02\x00\x00": (32, 350, 427, "SP2"),
+        }
+
diff --git a/chirp/drivers/tk760.py b/chirp/drivers/tk760.py
index 5fb46a4..bfed6b1 100644
--- a/chirp/drivers/tk760.py
+++ b/chirp/drivers/tk760.py
@@ -1,4 +1,4 @@
-# Copyright 2016 Dan Smith <dsmith at danplanet.com>
+# Copyright 2016 Pavel Milanes CO7WT, <co7wt at frcuba.co.cu> <pavelmc 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
@@ -13,8 +13,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# Driver author: Pavel CO7WT, co7wt at frcuba.co.cu, pavelmc at gmail.com
-
 import time
 import struct
 import logging
@@ -101,7 +99,9 @@ MEM_SIZE = 0x400
 BLOCK_SIZE = 8
 MEM_BLOCKS = range(0, (MEM_SIZE / BLOCK_SIZE))
 ACK_CMD = "\x06"
-TIMEOUT = 0.05  # from 0.03 up it' s safe, we set in 0.05 for a margin
+# from 0.03 up it' s safe
+# I have to turn it up, some users reported problems with this, was 0.05
+TIMEOUT = 0.1
 
 POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1),
                 chirp_common.PowerLevel("High", watts=5)]
@@ -192,33 +192,62 @@ def open_radio(radio):
         radio.pipe.setTimeout(TIMEOUT)
         radio.pipe.flushOutput()
         radio.pipe.flushInput()
+        LOG.debug("Serial port open successful")
     except:
         msg = "Serial error: Can't set serial line discipline"
         raise errors.RadioError(msg)
 
     magic = "PROGRAM"
-    for i in range(0, len(magic)):
-        ack = rawrecv(radio, 1)
-        time.sleep(0.05)
-        send(radio, magic[i])
+    LOG.debug("Sending MAGIC")
+    exito = False
+
+    # it appears that some buggy interfaces/serial devices keep sending
+    # data in the RX line, we will try to catch this garbage here
+    devnull = rawrecv(radio, 256)
+
+    for i in range(0, 5):
+        LOG.debug("Try %i" % i)
+        for i in range(0, len(magic)):
+            ack = rawrecv(radio, 1)
+            time.sleep(0.05)
+            send(radio, magic[i])
+
+        try:
+            handshake(radio, "Radio not entering Program mode")
+            LOG.debug("Radio opened for programming")
+            exito = True
+            break
+        except:
+            LOG.debug("No go, next try")
+            pass
+
+    # validate the success
+    if exito is False:
+        msg = "Radio refuse to enter into program mode after a few tries"
+        raise errors.RadioError(msg)
 
-    handshake(radio, "Radio not entering Program mode")
     rawsend(radio, "\x02")
     ident = rawrecv(radio, 8)
+
+    # validate the input
+    if  len(ident) != 8:
+        LOG.debug("Wrong ID, get only %s bytes, we expect 8" % len(ident))
+        LOG.debug(hexprint(ident))
+        msg = "Bad ID received, just %s bytes, we want 8" % len(ident)
+        raise errors.RadioError(msg)
+
     handshake(radio, "Comm error after ident", True)
+    LOG.debug("Correct get ident and hanshake")
 
     if not (radio.TYPE in ident):
-        LOG.debug("Incorrect model ID, got %s" % util.hexprint(ident))
+        LOG.debug("Incorrect model ID:")
+        LOG.debug(util.hexprint(ident))
         msg = "Incorrect model ID, got %s, it not contains %s" % \
             (ident[0:5], radio.TYPE)
         raise errors.RadioError(msg)
 
-    # DEBUG
-    #print("Full ident string is %s" % util.hexprint(ident))
-
-    # this is needed, I don't know why, yet
-    send(radio, make_frame("W", 0x03e1, "\xff\x01" + "\xff" * 6))
-    handshake(radio, "Comm error  after setup", True)
+    LOG.debug("Full ident string is:")
+    LOG.debug(util.hexprint(ident))
 
 
 def do_download(radio):
@@ -233,12 +262,12 @@ def do_download(radio):
     radio.status_fn(status)
 
     data = ""
+    LOG.debug("Starting the downolad")
     for addr in MEM_BLOCKS:
         send(radio, make_frame("R", addr * BLOCK_SIZE))
         data += recv(radio)
         handshake(radio, "Rx error in block %03i" % addr, True)
-        # DEBUG
-        #print("Block: %04x, Pos: %06x" % (addr, addr * BLOCK_SIZE))
+        LOG.debug("Block: %04x, Pos: %06x" % (addr, addr * BLOCK_SIZE))
 
         # UI Update
         status.cur = addr
@@ -266,19 +295,17 @@ def do_upload(radio):
         status.msg = "Cloning to radio..."
         radio.status_fn(status)
 
-        block = addr * BLOCK_SIZE
-        if block > 0x0378:
+        pos = addr * BLOCK_SIZE
+        if pos > 0x0378:
             # it seems that from this point forward is read only !?!?!?
             continue
 
-        send(radio, make_frame("W", block,
-                               radio.get_mmap()[block:block + BLOCK_SIZE]))
-
-        # DEBUG
-        #print("Block: %04x, Pos: %04x" % (addr, addr * BLOCK_SIZE))
+        data = radio.get_mmap()[pos:pos + BLOCK_SIZE]
+        send(radio, make_frame("W", pos, data))
+        LOG.debug("Block: %04x, Pos: %06x" % (addr, pos))
 
         time.sleep(0.1)
-        handshake(radio, "Rx error in block %03i" % addr)
+        handshake(radio, "Rx error in block %04x" % addr)
 
 
 def get_rid(data):
@@ -310,7 +337,7 @@ def model_match(cls, data):
 class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
     """Kenwood Mobile Family 60 Radios"""
     VENDOR = "Kenwood"
-    _range = [350000000, 500000000]  # don't mind, it will be overited
+    _range = [136000000, 500000000]  # don't mind, it will be overited
     _upper = 32
     VARIANT = ""
     MODEL = ""
@@ -333,16 +360,18 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
              'it will be implemented in the future.'
              )
         rp.pre_download = _(dedent("""\
-            Follow this instructions to download your info:
+            Follow this instructions to read your radio:
             1 - Turn off your radio
             2 - Connect your interface cable
-            3 - Do the download of your radio data
+            3 - Turn on your radio
+            4 - Do the download of your radio data
             """))
         rp.pre_upload = _(dedent("""\
-            Follow this instructions to download your info:
+            Follow this instructions to write your radio:
             1 - Turn off your radio
             2 - Connect your interface cable
-            3 - Do the upload of your radio data
+            3 - Turn on your radio
+            4 - Do the upload of your radio data
             """))
         return rp
 
@@ -615,8 +644,8 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         elif mem.duplex == "-":
             _mem.txfreq = (mem.freq - mem.offset) / 10
         elif mem.duplex == "off":
-            for i in range(0, 4):
-                _mem.txfreq[i].set_raw("\xFF")
+            for byte in _mem.txfreq:
+                byte.set_raw("\xFF")
         else:
             _mem.txfreq = mem.freq / 10
 
@@ -676,7 +705,7 @@ class Kenwood_M60_Radio(chirp_common.CloneModeRadio):
         top = RadioSettings(basic, fkeys)
 
         # Basic
-        val = RadioSettingValueString(0, 35, self.VARIANT)
+        val = RadioSettingValueString(0, 35, self._VARIANT)
         val.set_mutable(False)
         mod = RadioSetting("not.mod", "Radio version", val)
         basic.append(mod)
@@ -786,7 +815,7 @@ class TK760_Radio(Kenwood_M60_Radio):
     TYPE = "M0760"
     VARIANTS = {
         "M0760\x01\x00\x00": (32, 136, 156, "K2"),
-        "M0760\x00\x00\x00": (32, 148, 174, "K")
+        "M0760\x00\x00\x00": (32, 144, 174, "K")   # 148-147 Original
         }
 
 
@@ -797,7 +826,7 @@ class TK762_Radio(Kenwood_M60_Radio):
     TYPE = "M0762"
     VARIANTS = {
         "M0762\x01\x00\x00": (2, 136, 156, "K2"),
-        "M0762\x00\x00\x00": (2, 148, 174, "K")
+        "M0762\x00\x00\x00": (2, 144, 174, "K")   # 148-147 Original
         }
 
 
@@ -808,7 +837,7 @@ class TK768_Radio(Kenwood_M60_Radio):
     TYPE = "M0768"
     VARIANTS = {
         "M0768\x21\x00\x00": (32, 136, 156, "K2"),
-        "M0768\x20\x00\x00": (32, 148, 174, "K")
+        "M0768\x20\x00\x00": (32, 144, 174, "K")   # 148-147 Original
         }
 
 
@@ -818,7 +847,7 @@ class TK860_Radio(Kenwood_M60_Radio):
     MODEL = "TK-860"
     TYPE = "M0860"
     VARIANTS = {
-        "M0860\x05\x00\x00": (32, 406, 430, "F4"),
+        "M0860\x05\x00\x00": (32, 406, 440, "F4"),   # 406-430 Original
         "M0860\x04\x00\x00": (32, 488, 512, "F3"),
         "M0860\x03\x00\x00": (32, 470, 496, "F2"),
         "M0860\x02\x00\x00": (32, 450, 476, "F1")
@@ -831,7 +860,7 @@ class TK862_Radio(Kenwood_M60_Radio):
     MODEL = "TK-862"
     TYPE = "M0862"
     VARIANTS = {
-        "M0862\x05\x00\x00": (2, 406, 430, "F4"),
+        "M0862\x05\x00\x00": (2, 406, 440, "F4"),   # 406-430 Original
         "M0862\x04\x00\x00": (2, 488, 512, "F3"),
         "M0862\x03\x00\x00": (2, 470, 496, "F2"),
         "M0862\x02\x00\x00": (2, 450, 476, "F1")
@@ -844,8 +873,8 @@ class TK868_Radio(Kenwood_M60_Radio):
     MODEL = "TK-868"
     TYPE = "M0868"
     VARIANTS = {
-        "M0868\x25\x00\x00": (2, 406, 430, "F4"),
-        "M0868\x24\x00\x00": (2, 488, 512, "F3"),
-        "M0868\x23\x00\x00": (2, 470, 496, "F2"),
-        "M0868\x22\x00\x00": (2, 450, 476, "F1")
+        "M0868\x25\x00\x00": (32, 406, 440, "F4"),   # 406-430 Original
+        "M0868\x24\x00\x00": (32, 488, 512, "F3"),
+        "M0868\x23\x00\x00": (32, 470, 496, "F2"),
+        "M0868\x22\x00\x00": (32, 450, 476, "F1")
         }
diff --git a/chirp/drivers/tk760g.py b/chirp/drivers/tk760g.py
index 94ccd69..923b8b0 100644
--- a/chirp/drivers/tk760g.py
+++ b/chirp/drivers/tk760g.py
@@ -1,4 +1,4 @@
-# Copyright 2012 Dan Smith <dsmith at danplanet.com>
+# Copyright 2016 Pavel Milanes CO7WT, <co7wt at frcuba.co.cu> <pavelmc 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
@@ -13,10 +13,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# driver author Pavel Milanes, CO7WT, pavelmc at gmail.com, co7wt at frcuba.co.cu
-
 import logging
 import struct
+import time
+import sys
+
 from chirp import chirp_common, directory, memmap, errors, util, bitwise
 from textwrap import dedent
 from chirp.settings import RadioSettingGroup, RadioSetting, \
@@ -279,7 +280,7 @@ VALID_CHARS = chirp_common.CHARSET_UPPER_NUMERIC + "_-*()/\-+=)"
 SKIP_VALUES = ["", "S"]
 
 TONES = chirp_common.TONES
-TONES.remove(254.1)
+# TONES.remove(254.1)
 DTCS_CODES = chirp_common.DTCS_CODES
 
 TOT = ["off"] + ["%s" % x for x in range(15, 615, 15)]
@@ -348,9 +349,7 @@ def _raw_send(radio, data):
 
 def _close_radio(radio):
     """Get the radio out of program mode"""
-    # 3 times, it will don't harm in normal work,
-    # but it help's a lot in the developer process
-    _raw_send(radio, "\x45\x45\x45")
+    _raw_send(radio, "\x45")
 
 
 def _checksum(data):
@@ -381,15 +380,17 @@ def _handshake(radio, msg=""):
     if ack != ACK_CMD:
         _close_radio(radio)
         mesg = "Handshake failed " + msg
+        # DEBUG
+        LOG.debug(mesg)
         raise Exception(mesg)
 
 
 def _check_write_ack(r, ack, addr):
-    """Process the ack from the flock write process
+    """Process the ack from the write process
     this is half handshake needed in tx data block"""
     # all ok
     if ack == ACK_CMD:
-        return
+        return True
 
     # Explicit BAD checksum
     if ack == "\x15":
@@ -412,8 +413,16 @@ def _recv(radio):
     # when the RX block has two bytes and the first is \x5A
     # then the block is all \xFF
     if len(rxdata) == 2 and rxdata[0] == "\x5A":
-        _handshake(radio, "short block")
+        # fast work in linux has to make the handshake, slow windows don't
+        if not sys.platform in ["win32", "cygwin"]:
+            _handshake(radio, "short block")
         return False
+    elif len(rxdata) != 258:
+        # not the amount of data we want
+        msg = "The radio send %d bytes, we need 258" % len(rxdata)
+        # DEBUG
+        LOG.error(msg)
+        raise errors.RadioError(msg)
     else:
         rcs = ord(rxdata[-1])
         data = rxdata[1:-1]
@@ -429,53 +438,107 @@ def _recv(radio):
         return data
 
 
-def _open_radio(radio):
+def _open_radio(radio, status):
     """Open the radio into program mode and check if it's the correct model"""
-    radio.pipe.setTimeout(0.25)  # only works in the range 0.2 - 0.3
+    # linux min is 0.13, win min is 0.25; set to bigger to be safe
+    radio.pipe.setTimeout(0.25)
     radio.pipe.setParity("E")
 
-    _raw_send(radio, "PROGRAM")
-    ack = _raw_recv(radio, 1)
+    # DEBUG
+    LOG.debug("Entering program mode.")
+    # max tries
+    tries = 10
 
-    if ack != ACK_CMD:
-        # bad response, properly close the radio before exception
+    # UI
+    status.cur = 0
+    status.max = tries
+    status.msg = "Entering program mode..."
+
+    # try a few times to get the radio into program mode
+    exito = False
+    for i in range(0, tries):
+        _raw_send(radio, "PROGRAM")
+        ack = _raw_recv(radio, 1)
+
+        if ack != ACK_CMD:
+            # DEBUG
+            LOG.debug("Try %s failed, traying again...")
+            time.sleep(0.25)
+        else:
+            exito = True
+            break
+
+        status.cur += 1
+        radio.status_fn(status)
+
+
+    if exito is False:
         _close_radio(radio)
+        LOG.debug("Radio did not accepted PROGRAM command in five atempts")
         raise errors.RadioError("The radio doesn't accept program mode")
 
+    # DEBUG
+    LOG.debug("Received ACK to the PROGRAM command, send ID query.")
+
     _raw_send(radio, "\x02")
     rid = _raw_recv(radio, 8)
 
     if not (radio.TYPE in rid):
         # bad response, properly close the radio before exception
         _close_radio(radio)
-        # LOG.debug("Incorrect model ID, got %s" % util.hexprint(rid))
+
+        # DEBUG
+        LOG.debug("Incorrect model ID:")
+        LOG.debug(util.hexprint(rid))
+
         raise errors.RadioError(
             "Incorrect model ID, got %s, it not contains %s" %
             (rid.strip("\xff"), radio.TYPE))
 
     # DEBUG
-    LOG.debug("Full ident string is: %s" % util.hexprint(rid))
+    LOG.debug("Full ident string is:")
+    LOG.debug(util.hexprint(rid))
     _handshake(radio)
 
+    status.msg = "Radio ident success!"
+    radio.status_fn(status)
+    # a pause
+    time.sleep(1)
+
 
 def do_download(radio):
     """ The download function """
-    _open_radio(radio)
-
-    # speed up the reading
-    radio.pipe.setTimeout(0.03)  # only works in the range 0.25 and up
-
     # UI progress
     status = chirp_common.Status()
+    data = ""
+    count = 0
+
+    # open the radio
+    _open_radio(radio, status)
+
+    # reset UI data
     status.cur = 0
     status.max = MEM_SIZE / 256
     status.msg = "Cloning from radio..."
     radio.status_fn(status)
-    data = ""
-    count = 0
+
+    # set the timeout and if windows keep it bigger
+    if sys.platform in ["win32", "cygwin"]:
+        # bigger timeout
+        radio.pipe.setTimeout(0.55)
+    else:
+        # Linux can keep up, MAC?
+        radio.pipe.setTimeout(0.05)
+
+    # DEBUG
+    LOG.debug("Starting the download from radio")
 
     for addr in MEM_BLOCKS:
+        # send request, but before flush the rx buffer
+        radio.pipe.flushInput()
         _send(radio, _make_frame("R", addr))
+
+        # now we get the data
         d = _recv(radio)
         # if empty block, it return false
         # aka we asume a empty 256 xFF block
@@ -486,7 +549,6 @@ def do_download(radio):
 
         # UI Update
         status.cur = count
-        status.msg = "Cloning from radio..."
         radio.status_fn(status)
 
         count += 1
@@ -497,22 +559,28 @@ def do_download(radio):
 
 def do_upload(radio):
     """ The upload function """
-    _open_radio(radio)
-
-    # Radio need time to write data to eeprom
-    # 0.55 seconds as per the original software...
-    radio.pipe.setTimeout(0.55)
-
     # UI progress
     status = chirp_common.Status()
+    data = ""
+    count = 0
+
+    # open the radio
+    _open_radio(radio, status)
+
+    # update UI
     status.cur = 0
-    status.max = BLOCKS
+    status.max = MEM_SIZE / 256
     status.msg = "Cloning to radio..."
     radio.status_fn(status)
 
+    # the default for the original soft as measured
+    radio.pipe.setTimeout(0.5)
+
+    # DEBUG
+    LOG.debug("Starting the upload to the radio")
+
     count = 0
     raddr = 0
-
     for addr in MEM_BLOCKS:
         # this is the data block to write
         data = radio.get_mmap()[raddr:raddr+BLOCK_SIZE]
@@ -541,12 +609,16 @@ def do_upload(radio):
             frame = _make_frame("W", addr) + data + chr(cs)
 
         _send(radio, frame)
+
+        # get the ACK
         ack = _raw_recv(radio, 1)
         _check_write_ack(radio, ack, addr)
 
+        # DEBUG
+        LOG.debug("Sending block %02x" % addr)
+
         # UI Update
         status.cur = count
-        status.msg = "Cloning to radio..."
         radio.status_fn(status)
 
         count += 1
@@ -589,12 +661,6 @@ class Kenwood60GBankModel(chirp_common.BankModel):
             raise Exception("Memory %i not in bank %s. Cannot remove." %
                             (memory.number, bank))
 
-        # Warning about removing a channel on bank 0
-        #if bank.index == self._radio._get_bank(memory.number) == 0:
-            #mesg = "Can't remove, this is the default bank for "
-            #mesg += "all channels"
-            #raise Exception(mesg)
-
         # We can't "Remove" it for good
         # the kenwood paradigm don't allow it
         # instead we move it to bank 0
@@ -650,6 +716,9 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
              'If you need one of this, get your official software to do it'
              'and raise and issue on the chirp site about it and maybe'
              'it will be implemented in the future.'
+             ''
+             'A detail: this driver is slow reading from the radio in'
+             'Windows, due to the way windows serial works.'
              )
         rp.pre_download = _(dedent("""\
             Follow this instructions to download your info:
@@ -659,11 +728,11 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
             4 - Do the download of your radio data
             """))
         rp.pre_upload = _(dedent("""\
-            Follow this instructions to download your info:
+            Follow this instructions to upload your info:
             1 - Turn off your radio
             2 - Connect your interface cable
             3 - Turn on your radio (unblock it if password protected)
-            4 - Do the download of your radio data
+            4 - Do the upload of your radio data
             """))
         return rp
 
@@ -834,9 +903,11 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
         """Process the memory object"""
         # how many channels are programed
         self._chs_progs = ord(self._mmap[15])
+
         # load the memobj
         self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
-        # to ser the vars on the class to the correct ones
+
+        # to set the vars on the class to the correct ones
         self._set_variant()
 
     def get_raw_memory(self, number):
@@ -1017,6 +1088,9 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
             _mem.txfreq = (mem.freq + mem.offset) / 10
         elif mem.duplex == "-":
             _mem.txfreq = (mem.freq - mem.offset) / 10
+        elif mem.duplex == "off":
+            for byte in _mem.txfreq:
+                byte.set_raw("\xFF")
         else:
             _mem.txfreq = mem.freq / 10
 
@@ -1058,7 +1132,7 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
                 setattr(_mem, setting.get_name(), not bool(setting.value))
 
         # all data get sync after channel mod
-        #self._prep_data()
+        self._prep_data()
 
         return mem
 
@@ -1397,10 +1471,6 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
                     else:
                         value = value.ljust(just)
 
-                ## password with special case
-                #if setting == "radio" or setting == "data":
-                    #pass
-
                 # case keys, with special config
                 if inter == "keys":
                     value = KEYS.keys()[KEYS.values().index(str(value))]
@@ -1462,6 +1532,47 @@ class Kenwood_Serie_60G(chirp_common.CloneModeRadio):
 # this can change if user request it with high priority
 
 @directory.register
+class TK868G_Radios(Kenwood_Serie_60G):
+    """Kenwood TK-868G Radio M/C"""
+    MODEL = "TK-868G"
+    TYPE = "M8680"
+    VARIANTS = {
+        "M8680\x18\xff":    (8, 400, 490, "M"),
+        "M8680;\xff":       (128, 350, 390, "C1"),
+        "M86808\xff":       (128, 400, 440, "C2"),   # 400-430Mhz original
+        "M86806\xff":       (128, 450, 490, "C3"),
+        }
+
+
+ at directory.register
+class TK862G_Radios(Kenwood_Serie_60G):
+    """Kenwood TK-862G Radio K/E/(N)E"""
+    MODEL = "TK-862G"
+    TYPE = "M8620"
+    VARIANTS = {
+        "M8620\x06\xff":    (8, 450, 490, "K"),
+        "M8620\x07\xff":    (8, 485, 512, "K2"),
+        "M8620&\xff":       (8, 440, 470, "E"),
+        "M8620V\xff":       (8, 440, 470, "(N)E"),
+        }
+
+
+ at directory.register
+class TK860G_Radios(Kenwood_Serie_60G):
+    """Kenwood TK-860G Radio K"""
+    MODEL = "TK-860G"
+    TYPE = "M8600"
+    VARIANTS = {
+        "M8600\x08\xff":    (128, 400, 440, "K"),   # 400-430Mhz original
+        "M8600\x06\xff":    (128, 450, 490, "K1"),
+        "M8600\x07\xff":    (128, 485, 512, "K2"),
+        "M8600\x18\xff":    (128, 400, 440, "M"),   # 400-430Mhz original
+        "M8600\x16\xff":    (128, 450, 490, "M1"),
+        "M8600\x17\xff":    (128, 485, 520, "M2"),
+        }
+
+
+ at directory.register
 class TK768G_Radios(Kenwood_Serie_60G):
     """Kenwood TK-768G Radios [M/C]"""
     MODEL = "TK-768G"
@@ -1469,9 +1580,9 @@ class TK768G_Radios(Kenwood_Serie_60G):
     # Note that 8 CH don't have banks
     VARIANTS = {
         "M7680\x15\xff": (8, 136, 162, "M2"),
-        "M7680\x14\xff": (8, 148, 174, "M"),
+        "M7680\x14\xff": (8, 144, 174, "M"),      # 148-174 original
         "M76805\xff":    (128, 136, 162, "C2"),
-        "M76804\xff":    (128, 148, 174, "C"),
+        "M76804\xff":    (128, 144, 174, "C"),    # 148-174 original
         }
 
 
@@ -1483,9 +1594,9 @@ class TK762G_Radios(Kenwood_Serie_60G):
     # Note that 8 CH don't have banks
     VARIANTS = {
         "M7620\x05\xff": (8, 136, 162, "K2"),
-        "M7620\x04\xff": (8, 148, 172, "K"),
-        "M7620$\xff":    (8, 148, 172, "E"),
-        "M7620T\xff":    (8, 148, 172, "NE"),
+        "M7620\x04\xff": (8, 144, 172, "K"),    # 148-172 original
+        "M7620$\xff":    (8, 144, 172, "E"),    # 148-172 original
+        "M7620T\xff":    (8, 144, 172, "NE"),   # 148-172 original
         }
 
 
@@ -1496,9 +1607,9 @@ class TK760G_Radios(Kenwood_Serie_60G):
     TYPE = "M7600"
     VARIANTS = {
         "M7600\x05\xff": (128, 136, 162, "K2"),
-        "M7600\x04\xff": (128, 148, 174, "K"),
-        "M7600\x14\xff": (128, 146, 174, "M"),
-        "M7600T\xff":    (128, 146, 174, "NE")
+        "M7600\x04\xff": (128, 144, 174, "K"),   # 148-174 original
+        "M7600\x14\xff": (128, 144, 174, "M"),   # 148-174 original
+        "M7600T\xff":    (128, 144, 174, "NE")   # 148-174 original
         }
 
 
@@ -1533,8 +1644,8 @@ class TK270G_Radios(Kenwood_Serie_60G):
     MODEL = "TK-270G"
     TYPE = "P2700"
     VARIANTS = {
-        "P2700T\xff":    (128, 146, 174, "NE/NT"),
-        "P2700$\xff":    (128, 146, 174, "E"),
+        "P2700T\xff":    (128, 144, 174, "NE/NT"),   # 146-174 original
+        "P2700$\xff":    (128, 144, 174, "E"),       # 146-174 original
         "P2700\x14\xff": (128, 150, 174, "M"),
         "P2700\x05\xff": (128, 136, 150, "K1"),
         "P2700\x04\xff": (128, 150, 174, "K"),
@@ -1549,8 +1660,8 @@ class TK260G_Radios(Kenwood_Serie_60G):
     TYPE = "P2600"
     VARIANTS = {
         "P2600U\xff":    (8, 136, 150, "N1"),
-        "P2600T\xff":    (8, 146, 174, "N"),
-        "P2600$\xff":    (8, 146, 174, "E"),
+        "P2600T\xff":    (8, 144, 174, "N"),   # 146-174 original
+        "P2600$\xff":    (8, 144, 174, "E"),   # 144-174 original
         "P2600\x14\xff": (8, 150, 174, "M"),
         "P2600\x05\xff": (8, 136, 150, "K1"),
         "P2600\x04\xff": (8, 150, 174, "K")
diff --git a/chirp/drivers/tk8102.py b/chirp/drivers/tk8102.py
index 8fe8211..068314a 100644
--- a/chirp/drivers/tk8102.py
+++ b/chirp/drivers/tk8102.py
@@ -100,7 +100,7 @@ def do_ident(radio):
 
 def do_download(radio):
     radio.pipe.setParity("E")
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     do_ident(radio)
 
     data = ""
@@ -130,7 +130,7 @@ def do_download(radio):
 
 def do_upload(radio):
     radio.pipe.setParity("E")
-    radio.pipe.setTimeout(1)
+    radio.pipe.timeout = 1
     do_ident(radio)
 
     for addr in range(0, 0x0400, 8):
diff --git a/chirp/drivers/ts2000.py b/chirp/drivers/ts2000.py
index 970c021..f2160ad 100644
--- a/chirp/drivers/ts2000.py
+++ b/chirp/drivers/ts2000.py
@@ -25,7 +25,7 @@ TS2000_DUPLEX = dict(kenwood_live.DUPLEX)
 TS2000_DUPLEX[3] = "="
 TS2000_DUPLEX[4] = "split"
 TS2000_MODES = ["?", "LSB", "USB", "CW", "FM", "AM",
-                "FSK", "CW-R", "?", "FSK-R"]
+                "FSK", "CWR", "?", "FSKR"]
 TS2000_TMODES = ["", "Tone", "TSQL", "DTCS"]
 TS2000_TONES = list(chirp_common.OLD_TONES)
 TS2000_TONES.remove(69.3)
diff --git a/chirp/drivers/uv5r.py b/chirp/drivers/uv5r.py
index 509ea7c..38dca8a 100644
--- a/chirp/drivers/uv5r.py
+++ b/chirp/drivers/uv5r.py
@@ -395,20 +395,9 @@ def _firmware_version_from_image(radio):
     return version
 
 
-def _special_block_from_data(data, special_block_start, special_block_stop):
-    special_block_tag = data[special_block_start:special_block_stop]
-    return special_block_tag
-
-
-def _special_block_from_image(radio):
-    special_block = _special_block_from_data(radio.get_mmap(), 0x0CFA, 0x0D01)
-    LOG.debug("_special_block_from_image: " + util.hexprint(special_block))
-    return special_block
-
-
 def _do_ident(radio, magic):
     serial = radio.pipe
-    serial.setTimeout(1)
+    serial.timeout = 1
 
     LOG.info("Sending Magic: %s" % util.hexprint(magic))
     for byte in magic:
@@ -479,12 +468,6 @@ def _get_radio_firmware_version(radio):
     return version
 
 
-def _get_radio_special_block(radio):
-    block = _read_block(radio, 0xCF0, 0x40, False)
-    special_block = block[2:9]
-    return special_block
-
-
 def _ident_radio(radio):
     for magic in radio._idents:
         error = None
@@ -554,15 +537,6 @@ def _do_upload(radio):
                "of the radio (%s).")
         raise errors.RadioError(msg % (image_version, radio_version))
 
-    image_special_block = _special_block_from_image(radio)
-    radio_special_block = _get_radio_special_block(radio)
-    LOG.debug("Image Special Block is " + util.hexprint(image_special_block))
-    LOG.debug("Radio Special Block is " + util.hexprint(radio_special_block))
-
-    if image_special_block != radio_special_block:
-        raise errors.RadioError("Image not supported by radio: `%s'" %
-                                radio_special_block)
-
     # Main block
     for i in range(0x08, 0x1808, 0x10):
         _send_block(radio, i - 0x08, radio.get_mmap()[i:i + 0x10])
@@ -852,6 +826,7 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
 
         if mem.empty:
             _mem.set_raw("\xff" * 16)
+            _nam.set_raw("\xff" * 16)
             return
 
         _mem.set_raw("\x00" * 16)
@@ -1574,7 +1549,7 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
                     if element.has_apply_callback():
                         LOG.debug("Using apply callback")
                         element.run_apply_callback()
-                    else:
+                    elif element.value.get_mutable():
                         LOG.debug("Setting %s = %s" % (setting, element.value))
                         setattr(obj, setting, element.value)
                 except Exception, e:
diff --git a/chirp/drivers/uvb5.py b/chirp/drivers/uvb5.py
index 32976bc..f05aa73 100644
--- a/chirp/drivers/uvb5.py
+++ b/chirp/drivers/uvb5.py
@@ -174,7 +174,7 @@ struct {
 
 
 def do_ident(radio):
-    radio.pipe.setTimeout(3)
+    radio.pipe.timeout = 3
     radio.pipe.write("\x05PROGRAM")
     for x in xrange(10):
         ack = radio.pipe.read(1)
diff --git a/chirp/drivers/vxa700.py b/chirp/drivers/vxa700.py
index 3aa39d5..87aeaa0 100644
--- a/chirp/drivers/vxa700.py
+++ b/chirp/drivers/vxa700.py
@@ -173,7 +173,7 @@ class VXA700Radio(chirp_common.CloneModeRadio):
 
     def sync_in(self):
         try:
-            self.pipe.setTimeout(2)
+            self.pipe.timeout = 2
             self._mmap = _download(self)
         except errors.RadioError:
             raise
@@ -188,7 +188,7 @@ class VXA700Radio(chirp_common.CloneModeRadio):
         #             0x01 <- air band only
         #             0x02 <- air band only
         try:
-            self.pipe.setTimeout(2)
+            self.pipe.timeout = 2
             _upload(self)
         except errors.RadioError:
             raise
diff --git a/chirp/ui/mainapp.py b/chirp/ui/mainapp.py
index faff97c..6f850ba 100644
--- a/chirp/ui/mainapp.py
+++ b/chirp/ui/mainapp.py
@@ -633,6 +633,8 @@ of file.
         again = gtk.CheckButton(
             _("Don't show instructions for any radio again"))
         again.show()
+        again.connect("toggled", lambda action:
+            self.clonemenu.set_active(not action.get_active()))
         d.vbox.pack_start(again, 0, 0, 0)
         h_button_box = d.vbox.get_children()[2]
         try:
@@ -644,7 +646,7 @@ of file.
             pass
         d.run()
         d.destroy()
-        CONF.set_bool("clone_instructions", again.get_active(), "noconfirm")
+
 
     def do_download(self, port=None, rtype=None):
         d = clone.CloneSettingsDialog(parent=self)
@@ -1349,9 +1351,12 @@ of file.
             devaction = self.menu_ag.get_action(name)
             devaction.set_visible(action.get_active())
 
+    def do_toggle_clone_instructions(self, action):
+        CONF.set_bool("clone_instructions", not action.get_active(), "noconfirm")
+
     def do_change_language(self):
         langs = ["Auto", "English", "Polish", "Italian", "Dutch", "German",
-                 "Hungarian", "Russian", "Portuguese (BR)", "French"]
+                 "Hungarian", "Russian", "Portuguese (BR)", "French", "Spanish"]
         d = inputdialog.ChoiceDialog(langs, parent=self,
                                      title="Choose Language")
         d.label.set_text(_("Choose a language or Auto to use the "
@@ -1440,6 +1445,8 @@ of file.
             self.do_toggle_no_smart_tmode(_action)
         elif action == "developer":
             self.do_toggle_developer(_action)
+        elif action == "clone_instructions":
+            self.do_toggle_clone_instructions(_action)
         elif action in ["cut", "copy", "paste", "delete",
                         "move_up", "move_dn", "exchange", "all",
                         "devshowraw", "devdiffraw", "properties"]:
@@ -1524,6 +1531,7 @@ of file.
       <menuitem action="gethelp"/>
       <separator/>
       <menuitem action="report"/>
+      <menuitem action="clone_instructions"/>
       <menuitem action="developer"/>
       <separator/>
       <menuitem action="about"/>
@@ -1601,14 +1609,17 @@ of file.
         re = not conf.get_bool("no_report")
         hu = conf.get_bool("hide_unused", "memedit", default=True)
         dv = conf.get_bool("developer", "state")
+        ci = not conf.get_bool("clone_instructions", "noconfirm")
         st = not conf.get_bool("no_smart_tmode", "memedit")
 
-        toggles = [('report', None, _("Report statistics"),
+        toggles = [('report', None, _("Report Statistics"),
                     None, None, self.mh, re),
                    ('hide_unused', None, _("Hide Unused Fields"),
                     None, None, self.mh, hu),
                    ('no_smart_tmode', None, _("Smart Tone Modes"),
                     None, None, self.mh, st),
+                   ('clone_instructions', None, _("Show Instructions"),
+                    None, None, self.mh, ci),
                    ('developer', None, _("Enable Developer Functions"),
                     None, None, self.mh, dv),
                    ]
@@ -1623,7 +1634,7 @@ of file.
 
         self.add_accel_group(self.menu_uim.get_accel_group())
 
-        self.recentmenu = self.menu_uim.get_widget("/MenuBar/file/recent")
+        self.clonemenu = self.menu_uim.get_widget("/MenuBar/help/clone_instructions")
 
         # Initialize
         self.do_toggle_developer(self.menu_ag.get_action("developer"))
diff --git a/chirpw b/chirpw
index cb67047..9199332 100755
--- a/chirpw
+++ b/chirpw
@@ -52,6 +52,7 @@ if manual_language and manual_language != "Auto":
                   "Russian":          "ru",
                   "Portuguese (BR)":  "pt_BR",
                   "French":           "fr",
+                  "Spanish":          "es_ES",
                   }
     try:
         LOG.info("Language: %s", lang_codes[manual_language])
diff --git a/locale/es_ES/LC_MESSAGES/CHIRP.mo b/locale/es_ES/LC_MESSAGES/CHIRP.mo
new file mode 100644
index 0000000..761bee2
Binary files /dev/null and b/locale/es_ES/LC_MESSAGES/CHIRP.mo differ
diff --git a/stock_configs/NOAA Weather Alert.csv b/stock_configs/NOAA Weather Alert.csv
index ae1564a..41cc764 100644
--- a/stock_configs/NOAA Weather Alert.csv	
+++ b/stock_configs/NOAA Weather Alert.csv	
@@ -1,8 +1,11 @@
-Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,Mode,TStep,Skip,Comment,URCALL,RPT1CALL,RPT2CALL
-1,NOAA1,162.4,,0,,88.5,88.5,23,NN,FM,5,,,,,
-2,NOAA2,162.425,,0,,88.5,88.5,23,NN,FM,5,,,,,
-3,NOAA3,162.45,,0,,88.5,88.5,23,NN,FM,5,,,,,
-4,NOAA4,162.475,,0,,88.5,88.5,23,NN,FM,5,,,,,
-5,NOAA5,162.5,,0,,88.5,88.5,23,NN,FM,5,,,,,
-6,NOAA6,162.525,,0,,88.5,88.5,23,NN,FM,5,,,,,
-7,NOAA7,162.55,,0,,88.5,88.5,23,NN,FM,5,,,,,
+Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,Mode,TStep,Skip,Comment,URCALL,RPT1CALL,RPT2CALL

+1,WX1PA7,162.550000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+2,WX2PA1,162.400000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+3,WX3PA4,162.475000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+4,WX4PA2,162.425000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+5,WX5PA3,162.450000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+6,WX6PA5,162.500000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+7,WX7PA6,162.525000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+8,WX8,161.650000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+9,WX9,161.775000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+10,WX10,163.275000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
diff --git a/stock_configs/US CA Railroad Channels.csv b/stock_configs/US CA Railroad Channels.csv
new file mode 100644
index 0000000..fe5f4e6
--- /dev/null
+++ b/stock_configs/US CA Railroad Channels.csv	
@@ -0,0 +1,187 @@
+Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,Mode,Tstep,Skip,Comment,URCALL,RPT1CALL,RPT2CALL
+1,AAR002,159.810000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+2,AAR003,159.930000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+3,AAR004,160.050000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+4,AAR005,160.185000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+5,AAR006,160.200000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+6,AAR007,160.215000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+7,AAR008,160.230000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+8,AAR009,160.245000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+9,AAR010,160.260000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+10,AAR011,160.275000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+11,AAR012,160.290000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+12,AAR013,160.305000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+13,AAR014,160.320000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+14,AAR015,160.335000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+15,AAR016,160.350000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+16,AAR017,160.365000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+17,AAR018,160.380000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+18,AAR019,160.395000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+19,AAR020,160.410000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+20,AAR021,160.425000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+21,AAR022,160.440000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+22,AAR023,160.455000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+23,AAR024,160.470000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+24,AAR025,160.485000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+25,AAR026,160.500000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+26,AAR027,160.515000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+27,AAR028,160.530000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+28,AAR029,160.545000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+29,AAR030,160.560000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+30,AAR031,160.575000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+31,AAR032,160.590000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+32,AAR033,160.605000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+33,AAR034,160.620000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+34,AAR035,160.635000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+35,AAR036,160.650000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+36,AAR037,160.665000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+37,AAR038,160.680000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+38,AAR039,160.695000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+39,AAR040,160.710000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+40,AAR041,160.725000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+41,AAR042,160.740000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+42,AAR043,160.755000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+43,AAR044,160.770000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+44,AAR045,160.785000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+45,AAR046,160.800000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+46,AAR047,160.815000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+47,AAR048,160.830000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+48,AAR049,160.845000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+49,AAR050,160.860000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+50,AAR051,160.875000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+51,AAR052,160.890000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+52,AAR053,160.905000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+53,AAR054,160.920000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+54,AAR055,160.935000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+55,AAR056,160.950000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+56,AAR057,160.965000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+57,AAR058,160.980000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+58,AAR059,160.995000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+59,AAR060,161.010000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+60,AAR061,161.025000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+61,AAR062,161.040000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+62,AAR063,161.055000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+63,AAR064,161.070000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+64,AAR065,161.085000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+65,AAR066,161.100000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+66,AAR067,161.115000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+67,AAR068,161.130000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+68,AAR069,161.145000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+69,AAR070,161.160000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+70,AAR071,161.175000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+71,AAR072,161.190000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+72,AAR073,161.205000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+73,AAR074,161.220000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+74,AAR075,161.235000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+75,AAR076,161.250000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+76,AAR077,161.265000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+77,AAR078,161.280000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+78,AAR079,161.295000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+79,AAR080,161.310000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+80,AAR081,161.325000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+81,AAR082,161.340000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+82,AAR083,161.355000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+83,AAR084,161.370000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+84,AAR085,161.385000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+85,AAR086,161.400000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+86,AAR087,161.415000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+87,AAR088,161.430000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+88,AAR089,161.445000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+89,AAR090,161.460000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+90,AAR091,161.475000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+91,AAR092,161.490000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+92,AAR093,161.505000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+93,AAR094,161.520000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+94,AAR095,161.535000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+95,AAR096,161.550000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+96,AAR097,161.565000,,0.000000,,88.5,88.5,023,NN,FM,5.00,,,,,
+97,AAR107,160.222500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+98,AAR108,160.237500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+99,AAR109,160.252500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+100,AAR110,160.267500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+101,AAR111,160.282500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+102,AAR112,160.297500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+103,AAR113,160.312500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+104,AAR114,160.327500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+105,AAR115,160.342500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+106,AAR116,160.357500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+107,AAR117,160.372500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+108,AAR118,160.387500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+109,AAR119,160.402500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+110,AAR120,160.417500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+111,AAR121,160.432500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+112,AAR122,160.447500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+113,AAR123,160.462500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+114,AAR124,160.477500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+115,AAR125,160.492500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+116,AAR126,160.507500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+117,AAR127,160.522500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+118,AAR128,160.537500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+119,AAR129,160.552500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+120,AAR130,160.567500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+121,AAR131,160.582500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+122,AAR132,160.597500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+123,AAR133,160.612500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+124,AAR134,160.627500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+125,AAR135,160.642500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+126,AAR136,160.657500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+127,AAR137,160.672500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+128,AAR138,160.687500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+129,AAR139,160.702500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+130,AAR140,160.717500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+131,AAR141,160.732500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+132,AAR142,160.747500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+133,AAR143,160.762500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+134,AAR144,160.777500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+135,AAR145,160.792500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+136,AAR146,160.807500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+137,AAR147,160.822500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+138,AAR148,160.837500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+139,AAR149,160.852500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+140,AAR150,160.867500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+141,AAR151,160.882500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+142,AAR152,160.897500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+143,AAR153,160.912500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+144,AAR154,160.927500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+145,AAR155,160.942500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+146,AAR156,160.957500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+147,AAR157,160.972500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+148,AAR158,160.987500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+149,AAR159,161.002500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+150,AAR160,161.017500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+151,AAR161,161.032500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+152,AAR162,161.047500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+153,AAR163,161.062500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+154,AAR164,161.077500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+155,AAR165,161.092500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+156,AAR166,161.107500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+157,AAR167,161.122500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+158,AAR168,161.137500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+159,AAR169,161.152500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+160,AAR170,161.167500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+161,AAR171,161.182500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+162,AAR172,161.197500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+163,AAR173,161.212500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+164,AAR174,161.227500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+165,AAR175,161.242500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+166,AAR176,161.257500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+167,AAR177,161.272500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+168,AAR178,161.287500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+169,AAR179,161.302500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+170,AAR180,161.317500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+171,AAR181,161.332500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+172,AAR182,161.347500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+173,AAR183,161.362500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+174,AAR184,161.377500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+175,AAR185,161.392500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+176,AAR186,161.407500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+177,AAR187,161.422500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+178,AAR188,161.437500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+179,AAR189,161.452500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+180,AAR190,161.467500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+181,AAR191,161.482500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+182,AAR192,161.497500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+183,AAR193,161.512500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+184,AAR194,161.527500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+185,AAR195,161.542500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,
+186,AAR196,161.557500,,0.000000,,88.5,88.5,023,NN,NFM,5.00,,,,,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/chirp.git



More information about the pkg-hamradio-commits mailing list