[hamradio-commits] [chirp] 02/05: Imported Upstream version 20160717

Iain R. Learmonth irl at moszumanska.debian.org
Mon Jul 25 18:41:24 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 456f3bf74934939b058f5c26eed369c76db952db
Author: Iain R. Learmonth <irl at debian.org>
Date:   Mon Jul 25 19:26:43 2016 +0100

    Imported Upstream version 20160717
---
 PKG-INFO                               |    2 +-
 chirp/__init__.py                      |    2 +-
 chirp/chirp_common.py                  |   12 +-
 chirp/detect.py                        |    6 +-
 chirp/drivers/ap510.py                 |    1 -
 chirp/drivers/btech.py                 |  206 ++++-
 chirp/drivers/ft2800.py                |    4 +-
 chirp/drivers/ft2900.py                |   16 +-
 chirp/drivers/hobbypcb.py              |  327 +++++++
 chirp/drivers/ic9x_ll.py               |   14 +-
 chirp/drivers/icf.py                   |    2 +-
 chirp/drivers/ict8.py                  |   42 +-
 chirp/drivers/kenwood_live.py          |    4 +-
 chirp/drivers/kguv8d.py                |    3 +
 chirp/drivers/{th9800.py => th7800.py} |  371 ++++----
 chirp/drivers/th9800.py                |   13 +-
 chirp/drivers/thd72.py                 |   18 +-
 chirp/drivers/tk270.py                 |    7 +-
 chirp/drivers/tk760.py                 |    7 +-
 chirp/drivers/tk760g.py                |   12 +-
 chirp/drivers/tk8102.py                |    4 +-
 chirp/drivers/tmv71.py                 |    2 +-
 chirp/drivers/uv5r.py                  |   12 +-
 chirp/drivers/vgc.py                   | 1449 ++++++++++++++++++++++++++++++++
 chirp/platform.py                      |    2 +
 chirp/settings.py                      |   57 ++
 chirp/ui/clone.py                      |   19 +-
 chirp/ui/mainapp.py                    |   58 +-
 chirp/ui/memedit.py                    |    9 +-
 chirp/ui/shiftdialog.py                |    2 -
 30 files changed, 2331 insertions(+), 352 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 9c6dbb9..a334b87 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: chirp
-Version: daily-20160419
+Version: daily-20160717
 Summary: UNKNOWN
 Home-page: UNKNOWN
 Author: UNKNOWN
diff --git a/chirp/__init__.py b/chirp/__init__.py
index 932835b..54e0067 100644
--- a/chirp/__init__.py
+++ b/chirp/__init__.py
@@ -17,7 +17,7 @@ import os
 import sys
 from glob import glob
 
-CHIRP_VERSION="daily-20160419"
+CHIRP_VERSION="daily-20160717"
 
 module_dir = os.path.dirname(sys.modules["chirp"].__file__)
 __all__ = []
diff --git a/chirp/chirp_common.py b/chirp/chirp_common.py
index 46e9453..4291dec 100644
--- a/chirp/chirp_common.py
+++ b/chirp/chirp_common.py
@@ -987,14 +987,18 @@ class ValidationError(ValidationMessage):
     pass
 
 
-class Radio(object):
-    """Base class for all Radio drivers"""
-    BAUD_RATE = 9600
-    HARDWARE_FLOW = False
+class Alias(object):
     VENDOR = "Unknown"
     MODEL = "Unknown"
     VARIANT = ""
 
+
+class Radio(Alias):
+    """Base class for all Radio drivers"""
+    BAUD_RATE = 9600
+    HARDWARE_FLOW = False
+    ALIASES = []
+
     def status_fn(self, status):
         """Deliver @status to the UI"""
         console_status(status)
diff --git a/chirp/detect.py b/chirp/detect.py
index 7f3ee87..5115217 100644
--- a/chirp/detect.py
+++ b/chirp/detect.py
@@ -39,7 +39,7 @@ def _detect_icom_radio(ser):
     # ICOM VHF/UHF Clone-type radios @ 9600 baud
 
     try:
-        ser.setBaudrate(9600)
+        ser.baudrate = 9600
         md = icf.get_model_data(ser)
         return _icom_model_data_to_rclass(md)
     except errors.RadioError, e:
@@ -47,7 +47,7 @@ def _detect_icom_radio(ser):
 
     # ICOM IC-91/92 Live-mode radios @ 4800/38400 baud
 
-    ser.setBaudrate(4800)
+    ser.baudrate = 4800
     try:
         ic9x_ll.send_magic(ser)
         return _icom_model_data_to_rclass("ic9x")
@@ -58,7 +58,7 @@ def _detect_icom_radio(ser):
 
     for rate in [9600, 4800, 19200]:
         try:
-            ser.setBaudrate(rate)
+            ser.baudrate = rate
             return icomciv.probe_model(ser)
         except errors.RadioError:
             pass
diff --git a/chirp/drivers/ap510.py b/chirp/drivers/ap510.py
index d7b3ff2..36df4c0 100644
--- a/chirp/drivers/ap510.py
+++ b/chirp/drivers/ap510.py
@@ -343,7 +343,6 @@ RP_IMMUTABLE = ["number", "skip", "bank", "extd_number", "name", "rtone",
                 "offset", "mode", "tuning_step", "bank_index"]
 
 
- at directory.register
 class AP510Radio(chirp_common.CloneModeRadio):
     """Sainsonic AP510"""
     BAUD_RATE = 9600
diff --git a/chirp/drivers/btech.py b/chirp/drivers/btech.py
index 40ca5e8..bed353f 100644
--- a/chirp/drivers/btech.py
+++ b/chirp/drivers/btech.py
@@ -96,6 +96,8 @@ struct {
   u8 ste;
   u8 rpste;
   u8 rptdl;
+  u8 mgain;
+  u8 dtmfg;
 } settings;
 
 #seekto 0x0E80;
@@ -160,7 +162,7 @@ struct {
   u8 uhf_high[3];
 } ranges;
 
-// the 2501+220 has a different zone for storing ranges
+// the UV-2501+220 & KT8900R has different zones for storing ranges
 
 #seekto 0x3CD0;
 struct {
@@ -258,7 +260,8 @@ UV2501pp_fp = "M2C294"
 UV2501pp2_fp = "M29204"
 # B-TECH UV-2501 second generation (2G) radios
 UV2501G2_fp = "BTG214"
-
+# B-TECH UV-2501 third generation (3G) radios
+UV2501G3_fp = "BTG324"
 
 # B-TECH UV-2501+220 pre-production units
 UV2501_220pp_fp = "M3C281"
@@ -269,7 +272,8 @@ UV2501_220pp_id = "      280528"
 UV2501_220_fp = "M3G201"
 # new variant, let's call it Generation 2
 UV2501_220G2_fp = "BTG211"
-
+# B-TECH UV-2501+220 third generation (3G)
+UV2501_220G3_fp = "BTG311"
 
 # B-TECH UV-5001 pre-production units + 1st Gen radios
 UV5001pp_fp = "V19204"
@@ -279,26 +283,42 @@ UV5001alpha_fp = "V28204"
 UV5001G2_fp = "BTG214"
 # B-TECH UV-5001 second generation (2G2)
 UV5001G22_fp = "V2G204"
+# B-TECH UV-5001 third generation (3G)
+UV5001G3_fp = "BTG304"
+
+# special var to know when we found a BTECH Gen 3
+BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
 
 
 # WACCOM Mini-8900
 MINI8900_fp = "M28854"
 
 
-# QYT KT-UV980 & JetStream JT2705M
+# QYT KT-UV980
 KTUV980_fp = "H28854"
 
-
-# QYT KT8900 & Juentai JT-6188
+# QYT KT8900
 KT8900_fp = "M29154"
+# New generations KT8900
+KT8900_fp1 = "M2C234"
+KT8900_fp2 = "M2G1F4"
+KT8900_fp3 = "M2G2F4"
+KT8900_fp4 = "M2G304"
 # this radio has an extra ID
 KT8900_id = "      303688"
 
+# KT8900R
+KT8900R_fp = "M3G1F4"
+# Second Generation
+KT8900R_fp1 = "M3G214"
+# another model
+KT8900R_fp2 = "M3C234"
+# this radio has an extra ID
+KT8900R_id = "280528"
 
-# Sainsonic GT890
-GT890_fp = "M2G1F4"
-# this need a second id
-# and is the same of the QYT KT8900
+
+# LUITON LT-588UV
+LT588UV_fp = "V2G1F4"
 
 
 #### MAGICS
@@ -306,8 +326,9 @@ GT890_fp = "M2G1F4"
 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
+# for the QYT KT8900 & R
 MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
+MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
 # magic string for all other models
 MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
 
@@ -318,7 +339,7 @@ def _clean_buffer(radio):
 
     # touching the serial timeout to optimize the flushing
     # restored at the end to the default value
-    radio.pipe.setTimeout(0.1)
+    radio.pipe.timeout = 0.1
     dump = "1"
     datacount = 0
 
@@ -333,7 +354,7 @@ def _clean_buffer(radio):
                 raise errors.RadioError(seriale)
 
         # restore the default serial timeout
-        radio.pipe.setTimeout(STIMEOUT)
+        radio.pipe.timeout = STIMEOUT
 
     except Exception:
         raise errors.RadioError("Unknown error cleaning the serial buffer")
@@ -463,12 +484,17 @@ def _start_clone_mode(radio, status):
 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")
+    radio.pipe.baudrate = 9600
+    radio.pipe.parity = "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")
+        msg = "Radio did not enter clone mode"
+        # warning about old versions of QYT KT8900
+        if radio.MODEL == "KT8900":
+            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
+            msg += " known variant of this radios that is a clone of it."
+        raise errors.RadioError(msg)
 
     # Ok, get the ident string
     ident = _rawrecv(radio, 49)
@@ -481,7 +507,12 @@ def _do_ident(radio, status, upload=False):
     itis = False
     for fp in radio._fileid:
         if fp in ident:
+            # got it!
             itis = True
+            # checking if we are dealing with a Gen 3 BTECH
+            if radio.VENDOR == "BTECH" and fp in BTECH3:
+                radio.btech3 = True
+
             break
 
     if itis is False:
@@ -492,7 +523,7 @@ def _do_ident(radio, status, upload=False):
     # has the check value in the _id2 var, others simply False
     if radio._id2 is not False:
         # lower the timeout here as this radios are reseting due to timeout
-        radio.pipe.setTimeout(0.05)
+        radio.pipe.timeout = 0.05
 
         # query & receive the extra ID
         _send(radio, _make_frame("S", 0x3DF0, 16))
@@ -537,7 +568,7 @@ def _do_ident(radio, status, upload=False):
                 raise errors.RadioError("Radio didn't ACK the upload")
 
             # restore the default serial timeout
-            radio.pipe.setTimeout(STIMEOUT)
+            radio.pipe.timeout = STIMEOUT
 
     # DEBUG
     LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
@@ -700,6 +731,7 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
     _magic = MSTRING
     _fileid = None
     _id2 = False
+    btech3 = False
 
     @classmethod
     def get_prompts(cls):
@@ -779,8 +811,8 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
         # bands
         rf.valid_bands = [self._vhf_range, self._uhf_range]
 
-        # 2501+220
-        if self.MODEL == "UV-2501+220":
+        # 2501+220 & KT8900R
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
             rf.valid_bands.append(self._220_range)
 
         return rf
@@ -806,9 +838,10 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
         ranges"""
 
         # setting the correct ranges for each radio type
-        if "+220" in self.MODEL:
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
             # the model 2501+220 has a segment in 220
             # and a different position in the memmap
+            # also the QYT KT8900R
             ranges = self._memobj.ranges220
         else:
             ranges = self._memobj.ranges
@@ -821,8 +854,8 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
         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:
+        # 220Mhz radios case
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
             vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
             LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
             self._220_range = vhf2
@@ -1214,6 +1247,18 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
                                      _mem.settings.rptdl]))
             basic.append(rptdl)
 
+        if str(_mem.fingerprint.fp) in BTECH3:
+
+            mgain = RadioSetting("settings.mgain", "Mic gain",
+                                 RadioSettingValueInteger(0, 120,
+                                     _mem.settings.mgain))
+            basic.append(mgain)
+
+            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
+                                 RadioSettingValueInteger(0, 60,
+                                     _mem.settings.dtmfg))
+            basic.append(dtmfg)
+
         # Advanced
         def _filter(name):
             filtered = ""
@@ -1260,7 +1305,7 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
                     break
             return limit
 
-        if "+220" in self.MODEL:
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
             _ranges = self._memobj.ranges220
             ranges = "ranges220"
         else:
@@ -1279,7 +1324,7 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
         vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
         other.append(vhf_high)
 
-        if "+220" in self.MODEL:
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
             _limit = convert_bytes_to_limit(_ranges.vhf2_low)
             val = RadioSettingValueString(0, 3, _limit)
             val.set_mutable(False)
@@ -1342,15 +1387,21 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
 
         def my_validate(value):
             value = chirp_common.parse_freq(value)
-            print value
-            if 180000000 <= value and value < 210000000:
-                msg = ("Can't be between 180.00000-210.00000")
-                raise InvalidValueError(msg)
-            elif 231000000 <= value and value < 400000000:
-                msg = ("Can't be between 231.00000-400.00000")
-                raise InvalidValueError(msg)
-            elif 210000000 <= value and value < 231000000 \
-                and "+220" not in self.MODEL:
+            if "+220" in self.MODEL:
+                if 180000000 <= value and value < 210000000:
+                    msg = ("Can't be between 180.00000-210.00000")
+                    raise InvalidValueError(msg)
+                elif 231000000 <= value and value < 400000000:
+                    msg = ("Can't be between 231.00000-400.00000")
+                    raise InvalidValueError(msg)
+            elif "8900R" in self.MODEL:
+                if 180000000 <= value and value < 240000000:
+                    msg = ("Can't be between 180.00000-240.00000")
+                    raise InvalidValueError(msg)
+                elif 271000000 <= value and value < 400000000:
+                    msg = ("Can't be between 271.00000-400.00000")
+                    raise InvalidValueError(msg)
+            elif 180000000 <= value and value < 400000000:
                 msg = ("Can't be between 180.00000-400.00000")
                 raise InvalidValueError(msg)
             return chirp_common.format_freq(value)
@@ -1543,11 +1594,41 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
             return False
 
 
+# Declaring Aliases (Clones of the real radios)
+class JT2705M(chirp_common.Alias):
+    VENDOR = "Jetstream"
+    MODEL = "JT2705M"
+
+
+class JT6188Mini(chirp_common.Alias):
+    VENDOR = "Juentai"
+    MODEL = "JT-6188 Mini"
+
+
+class JT6188Plus(chirp_common.Alias):
+    VENDOR = "Juentai"
+    MODEL = "JT-6188 Plus"
+
+
+class SSGT890(chirp_common.Alias):
+    VENDOR = "Sainsonic"
+    MODEL = "GT-890"
+
+
+class ZastoneMP300(chirp_common.Alias):
+    VENDOR = "Zastone"
+    MODEL = "MP-300"
+
+
+# real radios
 @directory.register
 class UV2501(BTech):
     """Baofeng Tech UV2501"""
     MODEL = "UV-2501"
-    _fileid = [UV2501G2_fp, UV2501pp2_fp, UV2501pp_fp]
+    _fileid = [UV2501G3_fp,
+               UV2501G2_fp,
+               UV2501pp2_fp,
+               UV2501pp_fp]
 
 
 @directory.register
@@ -1555,15 +1636,22 @@ class UV2501_220(BTech):
     """Baofeng Tech UV2501+220"""
     MODEL = "UV-2501+220"
     _magic = MSTRING_220
-    _fileid = [UV2501_220G2_fp, UV2501_220_fp, UV2501_220pp_fp]
     _id2 = UV2501_220pp_id
+    _fileid = [UV2501_220G3_fp,
+               UV2501_220G2_fp,
+               UV2501_220_fp,
+               UV2501_220pp_fp]
 
 
 @directory.register
 class UV5001(BTech):
     """Baofeng Tech UV5001"""
     MODEL = "UV-5001"
-    _fileid = [UV5001G22_fp, UV5001G2_fp, UV5001alpha_fp, UV5001pp_fp]
+    _fileid = [UV5001G3_fp,
+               UV5001G22_fp,
+               UV5001G2_fp,
+               UV5001alpha_fp,
+               UV5001pp_fp]
 
 
 @directory.register
@@ -1573,6 +1661,8 @@ class MINI8900(BTech):
     MODEL = "MINI-8900"
     _magic = MSTRING_MINI8900
     _fileid = [MINI8900_fp, ]
+    # Clones
+    ALIASES = [JT6188Plus, ]
 
 
 @directory.register
@@ -1584,8 +1674,11 @@ class KTUV980(BTech):
     _uhf_range = (400000000, 481000000)
     _magic = MSTRING_MINI8900
     _fileid = [KTUV980_fp, ]
+    # Clones
+    ALIASES = [JT2705M, ]
 
-
+# Please note that there is a version of this radios that is a clone of the
+# Waccom Mini8900, maybe an early version?
 @directory.register
 class KT9800(BTech):
     """QYT KT8900"""
@@ -1594,16 +1687,37 @@ class KT9800(BTech):
     _vhf_range = (136000000, 175000000)
     _uhf_range = (400000000, 481000000)
     _magic = MSTRING_KT8900
-    _fileid = [KT8900_fp, ]
+    _fileid = [KT8900_fp,
+               KT8900_fp1,
+               KT8900_fp2,
+               KT8900_fp3,
+               KT8900_fp4]
     _id2 = KT8900_id
+    # Clones
+    ALIASES = [JT6188Mini, SSGT890, ZastoneMP300]
 
 
 @directory.register
-class GT890(BTech):
-    """Sainsonic GT890"""
-    VENDOR = "Sainsonic"
-    MODEL = "GT-890"
-    # ranges are the same as btech's defaults
+class KT9800R(BTech):
+    """QYT KT8900R"""
+    VENDOR = "QYT"
+    MODEL = "KT8900R"
+    _vhf_range = (136000000, 175000000)
+    _220_range = (240000000, 271000000)
+    _uhf_range = (400000000, 481000000)
+    _magic = MSTRING_KT8900R
+    _fileid = [KT8900R_fp,
+               KT8900R_fp1,
+               KT8900R_fp2]
+    _id2 = KT8900R_id
+
+
+ at directory.register
+class LT588UV(BTech):
+    """LUITON LT-588UV"""
+    VENDOR = "LUITON"
+    MODEL = "LT-588UV"
+    _vhf_range = (136000000, 175000000)
+    _uhf_range = (400000000, 481000000)
     _magic = MSTRING_KT8900
-    _fileid = [GT890_fp, ]
-    _id2 = KT8900_id
+    _fileid = [LT588UV_fp, ]
diff --git a/chirp/drivers/ft2800.py b/chirp/drivers/ft2800.py
index b1a8928..9030e96 100644
--- a/chirp/drivers/ft2800.py
+++ b/chirp/drivers/ft2800.py
@@ -195,7 +195,7 @@ class FT2800Radio(YaesuCloneModeRadio):
         return rf
 
     def sync_in(self):
-        self.pipe.setParity("E")
+        self.pipe.parity = "E"
         start = time.time()
         try:
             self._mmap = _download(self)
@@ -208,7 +208,7 @@ class FT2800Radio(YaesuCloneModeRadio):
 
     def sync_out(self):
         self.pipe.timeout = 1
-        self.pipe.setParity("E")
+        self.pipe.parity = "E"
         start = time.time()
         try:
             _upload(self)
diff --git a/chirp/drivers/ft2900.py b/chirp/drivers/ft2900.py
index 9fc93dc..5712163 100644
--- a/chirp/drivers/ft2900.py
+++ b/chirp/drivers/ft2900.py
@@ -1232,22 +1232,10 @@ class FT2900Radio(YaesuCloneModeRadio):
 # the FT2900E is the European version of the radio, almost identical
 # to the R (USA) 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
+# NOTE: Disabled until detection is fixed
+#@directory.register
 class FT2900ERadio(FT2900Radio):
     """Yaesu FT-2900E"""
     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/hobbypcb.py b/chirp/drivers/hobbypcb.py
new file mode 100644
index 0000000..7d7779b
--- /dev/null
+++ b/chirp/drivers/hobbypcb.py
@@ -0,0 +1,327 @@
+# Copyright 2016 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 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 logging
+import time
+
+from chirp import chirp_common, directory, memmap, errors
+from chirp import bitwise
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+    RadioSettingValueInteger, RadioSettingValueList, \
+    RadioSettingValueBoolean, RadioSettingValueString, \
+    RadioSettings
+
+
+LOG = logging.getLogger(__name__)
+BAUDS = [1200, 4800, 9600, 19200, 38400, 57600]
+POWER_LEVELS = [chirp_common.PowerLevel('Low', dBm=10),
+                chirp_common.PowerLevel('High', dBm=24)]
+TONE_MODES = ['', 'Tone', 'TSQL', '']
+
+
+def detect_baudrate(radio):
+    bauds = list(BAUDS)
+    bauds.remove(radio.pipe.getBaudrate())
+    bauds.insert(0, radio.pipe.getBaudrate())
+    for baud in bauds:
+        radio.pipe.setBaudrate(baud)
+        radio.pipe.setTimeout(0.5)
+        radio.pipe.write('\rFW?\r')
+        resp = radio.pipe.read(2)
+        if resp.strip().startswith('FW'):
+            resp += radio.pipe.read(16)
+            LOG.info('HobbyPCB %s at baud rate %i' % (resp.strip(), baud))
+            return baud
+
+
+ at directory.register
+class HobbyPCBRSUV3Radio(chirp_common.LiveRadio):
+    """HobbyPCB RS-UV3"""
+    VENDOR = "HobbyPCB"
+    MODEL = "RS-UV3"
+    BAUD_RATE = 19200
+
+    def __init__(self, *args, **kwargs):
+        super(HobbyPCBRSUV3Radio, self).__init__(*args, **kwargs)
+        if self.pipe:
+            baud = detect_baudrate(self)
+            if not baud:
+                errors.RadioError('Radio did not respond')
+
+    def _cmd(self, command, rsize=None):
+        LOG.debug('> %s' % command)
+        self.pipe.write('%s\r' % command)
+        resp = ''
+
+        if rsize is None:
+            complete = lambda: False
+        elif rsize == 0:
+            rsize = 1
+            complete = lambda: resp.endswith('\r')
+        else:
+            complete = lambda: len(resp) >= rsize
+
+        while not complete():
+            chunk = self.pipe.read(rsize)
+            if not chunk:
+                break
+            resp += chunk
+        LOG.debug('< %r [%i]' % (resp, len(resp)))
+        return resp.strip()
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_bank = False
+        rf.has_name = False
+        rf.has_cross = False
+        rf.has_dtcs = False
+        rf.has_rx_dtcs = False
+        rf.has_dtcs_polarity = False
+        rf.has_tuning_step = False
+        rf.has_mode = False
+        rf.has_settings = True
+        rf.memory_bounds = (1, 9)  # This radio supports memories 0-9
+        rf.valid_bands = [(144000000, 148000000),
+                          (220000000, 222000000),
+                          (440000000, 450000000),
+                          ]
+        rf.valid_tmodes = TONE_MODES
+        rf.valid_power_levels = POWER_LEVELS
+        return rf
+
+    def get_memory(self, number):
+        _mem = self._cmd('CP%i' % number, 33).split('\r')
+        LOG.debug('Memory elements: %s' % _mem)
+        mem = chirp_common.Memory()
+        mem.number = number
+        mem.freq = int(_mem[0]) * 1000
+        txfreq = int(_mem[1]) * 1000
+        mem.offset = abs(txfreq - mem.freq)
+        if mem.freq < txfreq:
+            mem.duplex = '+'
+        elif mem.freq > txfreq:
+            mem.duplex = '-'
+        else:
+            mem.duplex = ''
+        mem.ctone = int(_mem[2]) / 100.0
+        mem.rtone = mem.ctone
+        mem.tmode = TONE_MODES[int(_mem[3])]
+        mem.power = POWER_LEVELS[int(_mem[5])]
+        return mem
+
+    def set_memory(self, mem):
+        if mem.tmode in ['', 'Tone']:
+            tone = mem.rtone * 100
+        else:
+            tone = mem.ctone * 100
+        if mem.duplex == '+':
+            self._cmd('FT%06i' % ((mem.freq + mem.offset) / 1000))
+            self._cmd('FR%06i' % (mem.freq / 1000))
+        elif mem.duplex == '-':
+            self._cmd('FT%06i' % ((mem.freq - mem.offset) / 1000))
+            self._cmd('FR%06i' % (mem.freq / 1000))
+        else:
+            self._cmd('FS%06i' % (mem.freq / 1000))
+        self._cmd('TM%i' % TONE_MODES.index(mem.tmode))
+        self._cmd('TF%05i' % tone)
+        self._cmd('PW%i' % POWER_LEVELS.index(mem.power))
+        time.sleep(1)
+        self._cmd('ST%i' % mem.number)
+
+    def get_settings(self):
+        def _get(cmd):
+            return self._cmd('%s?' % cmd, 0).split(':')[1].strip()
+
+        cw = RadioSettingGroup('beacon', 'Beacon Settings')
+        cl = RadioSetting('CL%15s', 'CW Callsign',
+                          RadioSettingValueString(0, 15,
+                                                  _get('CL')))
+        cw.append(cl)
+
+        cf = RadioSetting('CF%4i', 'CW Audio Frequency',
+                          RadioSettingValueInteger(400, 1300,
+                                                   int(_get('CF'))))
+        cw.append(cf)
+
+        cs = RadioSetting('CS%02i', 'CW Speed',
+                          RadioSettingValueInteger(5, 25,
+                                                   int(_get('CS'))))
+        cw.append(cs)
+
+        bc = RadioSetting('BC%03i', 'CW Beacon Timer',
+                          RadioSettingValueInteger(0, 600,
+                                                   int(_get('BC'))))
+        cw.append(bc)
+
+        bm = RadioSetting('BM%15s', 'Beacon Message',
+                          RadioSettingValueString(0, 15,
+                                                  _get('BM')))
+        cw.append(bm)
+
+        bt = RadioSetting('BT%03i', 'Beacon Timer',
+                          RadioSettingValueInteger(0, 600,
+                                                   int(_get('BT'))))
+        cw.append(bt)
+
+        it = RadioSetting('IT%03i', 'CW ID Timer',
+                          RadioSettingValueInteger(0, 500,
+                                                   int(_get('IT'))))
+        cw.append(it)
+
+        tg = RadioSetting('TG%7s', 'CW Timeout Message',
+                          RadioSettingValueString(0, 7,
+                                                   _get('TG')))
+        cw.append(tg)
+
+        io = RadioSettingGroup('io', 'IO')
+
+        af = RadioSetting('AF%i', 'Arduino LPF',
+                          RadioSettingValueBoolean(_get('AF') == 'ON'))
+        io.append(af)
+
+        input_pin = ['OFF', 'SQ OPEN', 'PTT']
+        ai = RadioSetting('AI%i', 'Arduino Input Pin',
+                          RadioSettingValueList(
+                              input_pin,
+                              input_pin[int(_get('AI'))]))
+        io.append(ai)
+
+        output_pin = ['LOW', 'SQ OPEN', 'DTMF DETECT', 'TX ON', 'CTCSS DET',
+                      'HIGH']
+        ao = RadioSetting('AO%i', 'Arduino Output Pin',
+                          RadioSettingValueList(
+                              output_pin,
+                              output_pin[int(_get('AO'))]))
+        io.append(ao)
+
+        bauds = [str(x) for x in BAUDS]
+        b1 = RadioSetting('B1%i', 'Arduino Baudrate',
+                          RadioSettingValueList(
+                              bauds,
+                              bauds[int(_get('B1'))]))
+        io.append(b1)
+
+        b2 = RadioSetting('B2%i', 'Main Baudrate',
+                          RadioSettingValueList(
+                              bauds,
+                              bauds[int(_get('B2'))]))
+        io.append(b2)
+
+        dtmf = RadioSettingGroup('dtmf', 'DTMF Settings')
+
+        dd = RadioSetting('DD%04i', 'DTMF Tone Duration',
+                          RadioSettingValueInteger(50, 2000,
+                                                   int(_get('DD'))))
+        dtmf.append(dd)
+
+        dr = RadioSetting('DR%i', 'DTMF Tone Detector',
+                          RadioSettingValueBoolean(_get('DR') == 'ON'))
+        dtmf.append(dr)
+
+        gt = RadioSetting('GT%02i', 'DTMF/CW Tone Gain',
+                          RadioSettingValueInteger(0, 15,
+                                                   int(_get('GT'))))
+        dtmf.append(gt)
+
+        sd = RadioSetting('SD%i', 'DTMF/CW Side Tone',
+                          RadioSettingValueBoolean(_get('SD') == 'ON'))
+        dtmf.append(sd)
+
+        general = RadioSettingGroup('general', 'General')
+
+        dp = RadioSetting('DP%i', 'Pre-Emphasis',
+                          RadioSettingValueBoolean(_get('DP') == 'ON'))
+        general.append(dp)
+
+        fw = RadioSetting('_fw', 'Firmware Version',
+                          RadioSettingValueString(0, 20,
+                                                  _get('FW')))
+        general.append(fw)
+
+        gm = RadioSetting('GM%02i', 'Mic Gain',
+                          RadioSettingValueInteger(0, 15,
+                                                   int(_get('GM'))))
+        general.append(gm)
+
+        hp = RadioSetting('HP%i', 'Audio High-Pass Filter',
+                          RadioSettingValueBoolean(_get('HP') == 'ON'))
+        general.append(hp)
+
+        ht = RadioSetting('HT%04i', 'Hang Time',
+                          RadioSettingValueInteger(0, 5000,
+                                                   int(_get('HT'))))
+        general.append(ht)
+
+        ledmode = ['OFF', 'ON', 'SQ OPEN', 'BATT CHG STAT']
+        ld = RadioSetting('LD%i', 'LED Mode',
+                          RadioSettingValueList(
+                              ledmode,
+                              ledmode[int(_get('LD'))]))
+        general.append(ld)
+
+        sq = RadioSetting('SQ%i', 'Squelch Level',
+                          RadioSettingValueInteger(0, 9,
+                                                   int(_get('SQ'))))
+        general.append(sq)
+
+        to = RadioSetting('TO%03i', 'Timeout Timer',
+                          RadioSettingValueInteger(0, 600,
+                                                   int(_get('TO'))))
+        general.append(to)
+
+        vu = RadioSetting('VU%02i', 'Receiver Audio Volume',
+                          RadioSettingValueInteger(0, 39,
+                                                   int(_get('VU'))))
+        general.append(vu)
+
+        rc = RadioSetting('RC%i', 'Current Channel',
+                          RadioSettingValueInteger(0, 9, 0))
+        rc.set_doc('Choosing one of these values causes the radio '
+                   'to change to the selected channel. The radio '
+                   'cannot tell CHIRP what channel is selected.')
+        general.append(rc)
+
+        return RadioSettings(general, cw, io, dtmf)
+
+    def set_settings(self, settings):
+        def _set(thing):
+            # Try to only set something if it's new
+            query = '%s?' % thing[:2]
+            cur = self._cmd(query, 0)
+            if cur.strip():
+                cur = cur.split()[1].strip()
+            new = thing[2:].strip()
+            if cur in ['ON', 'OFF']:
+                cur = int(cur == 'ON')
+                new = int(new)
+            elif cur.isdigit():
+                cur = int(cur)
+                new = int(new)
+            if new != cur:
+                LOG.info('Setting %s (%r != %r)' % (thing, cur, new))
+                self._cmd(thing)
+                time.sleep(1)
+
+        for group in settings:
+            for setting in group:
+                if setting.get_name().startswith('_'):
+                    LOG.debug('Skipping %s' % setting)
+                    continue
+                cmd = setting.get_name()
+                value = setting.value.get_value()
+                if hasattr(setting.value, '_options'):
+                    value = setting.value._options.index(value)
+                fullcmd = (cmd % value).strip()
+                _set(fullcmd)
diff --git a/chirp/drivers/ic9x_ll.py b/chirp/drivers/ic9x_ll.py
index c114ad7..a6a2e14 100644
--- a/chirp/drivers/ic9x_ll.py
+++ b/chirp/drivers/ic9x_ll.py
@@ -439,31 +439,31 @@ def _send_magic_38400(pipe):
 
 def send_magic(pipe):
     """Send the magic incantation to wake up an ic9x radio"""
-    if pipe.getBaudrate() == 38400:
+    if pipe.baudrate == 38400:
         resp = _send_magic_38400(pipe)
         if resp:
             return
         LOG.info("Switching from 38400 to 4800")
-        pipe.setBaudrate(4800)
+        pipe.baudrate = 4800
         resp = _send_magic_4800(pipe)
-        pipe.setBaudrate(38400)
+        pipe.baudrate = 38400
         if resp:
             return
         raise errors.RadioError("Radio not responding")
-    elif pipe.getBaudrate() == 4800:
+    elif pipe.baudrate == 4800:
         resp = _send_magic_4800(pipe)
         if resp:
             return
         LOG.info("Switching from 4800 to 38400")
-        pipe.setBaudrate(38400)
+        pipe.baudrate = 38400
         resp = _send_magic_38400(pipe)
         if resp:
             return
-        pipe.setBaudrate(4800)
+        pipe.baudrate = 4800
         raise errors.RadioError("Radio not responding")
     else:
         raise errors.InvalidDataError("Radio in unknown state (%i)" %
-                                      pipe.getBaudrate())
+                                      pipe.baudrate)
 
 
 def get_memory_frame(pipe, vfo, number):
diff --git a/chirp/drivers/icf.py b/chirp/drivers/icf.py
index b51d8cb..1feab74 100644
--- a/chirp/drivers/icf.py
+++ b/chirp/drivers/icf.py
@@ -251,7 +251,7 @@ def start_hispeed_clone(radio, cmd):
     LOG.debug("Response:\n%s" % util.hexprint(resp))
 
     LOG.info("Switching to 38400 baud")
-    radio.pipe.setBaudrate(38400)
+    radio.pipe.baudrate = 38400
 
     buf = ("\xFE" * 14) + \
         "\xEE\xEF" + \
diff --git a/chirp/drivers/ict8.py b/chirp/drivers/ict8.py
index 742bc73..168c657 100644
--- a/chirp/drivers/ict8.py
+++ b/chirp/drivers/ict8.py
@@ -19,8 +19,8 @@ from chirp import bitwise
 
 mem_format = """
 struct memory {
-  bbcd freq[4];
-  bbcd offset[2];
+  bbcd freq[3];
+  bbcd offset[3];
   u8 rtone;
   u8 ctone;
 };
@@ -35,6 +35,11 @@ struct flags {
 
 struct memory memory[100];
 
+#seekto 0x0400;
+struct {
+  char name[4];
+} names[100];
+
 #seekto 0x0600;
 struct flags flags[100];
 """
@@ -43,6 +48,16 @@ DUPLEX = ["", "", "-", "+"]
 TMODES = ["", "", "Tone", "TSQL"]
 
 
+def _get_freq(bcd_array):
+    lastnibble = bcd_array[2].get_bits(0x0F)
+    return (int(bcd_array) - lastnibble) * 1000 + lastnibble * 500
+
+
+def _set_freq(bcd_array, freq):
+    bitwise.int_to_bcd(bcd_array, freq / 1000)
+    bcd_array[2].set_raw(bcd_array[2].get_bits(0xF0) + freq % 10000 / 500)
+
+
 @directory.register
 class ICT8ARadio(icf.IcomCloneModeRadio):
     """Icom IC-T8A"""
@@ -65,7 +80,8 @@ class ICT8ARadio(icf.IcomCloneModeRadio):
         rf.valid_skips = ["", "S"]
         rf.valid_modes = ["FM"]
         rf.memory_bounds = (0, 99)
-        rf.has_name = False
+        rf.valid_name_length = 4
+        rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC
         rf.has_dtcs = False
         rf.has_dtcs_polarity = False
         rf.has_tuning_step = False
@@ -78,11 +94,13 @@ class ICT8ARadio(icf.IcomCloneModeRadio):
 
     def get_raw_memory(self, number):
         return (str(self._memobj.memory[number]) +
-                str(self._memobj.duptone[number]))
+                str(self._memobj.names[number]) +
+                str(self._memobj.flags[number]))
 
     def get_memory(self, number):
         _mem = self._memobj.memory[number]
         _flg = self._memobj.flags[number]
+        _name = self._memobj.names[number]
 
         mem = chirp_common.Memory()
         mem.number = number
@@ -91,19 +109,22 @@ class ICT8ARadio(icf.IcomCloneModeRadio):
             mem.empty = True
             return mem
 
-        mem.freq = int(_mem.freq) * 10
-        mem.offset = int(_mem.offset) * 1000
+        mem.freq = _get_freq(_mem.freq)
+        mem.offset = _get_freq(_mem.offset)
         mem.rtone = chirp_common.TONES[_mem.rtone - 1]
         mem.ctone = chirp_common.TONES[_mem.ctone - 1]
         mem.duplex = DUPLEX[_flg.duplex]
         mem.tmode = TMODES[_flg.tmode]
         mem.skip = _flg.skip and "S" or ""
+        if _name.name.get_raw() != "\xFF\xFF\xFF\xFF":
+            mem.name = str(_name.name).rstrip()
 
         return mem
 
     def set_memory(self, mem):
         _mem = self._memobj.memory[mem.number]
         _flg = self._memobj.flags[mem.number]
+        _name = self._memobj.names[mem.number]
 
         if mem.empty:
             _flg.empty = True
@@ -112,10 +133,15 @@ class ICT8ARadio(icf.IcomCloneModeRadio):
         _mem.set_raw("\x00" * 8)
         _flg.set_raw("\x00")
 
-        _mem.freq = mem.freq / 10
-        _mem.offset = mem.offset / 1000
+        _set_freq(_mem.freq, mem.freq)
+        _set_freq(_mem.offset, mem.offset)
         _mem.rtone = chirp_common.TONES.index(mem.rtone) + 1
         _mem.ctone = chirp_common.TONES.index(mem.ctone) + 1
         _flg.duplex = DUPLEX.index(mem.duplex)
         _flg.tmode = TMODES.index(mem.tmode)
         _flg.skip = mem.skip == "S"
+
+        if mem.name:
+            _name.name = mem.name.ljust(4)
+        else:
+            _name.name = "\xFF\xFF\xFF\xFF"
diff --git a/chirp/drivers/kenwood_live.py b/chirp/drivers/kenwood_live.py
index efda1d5..a30619a 100644
--- a/chirp/drivers/kenwood_live.py
+++ b/chirp/drivers/kenwood_live.py
@@ -103,7 +103,7 @@ def get_id(ser):
             LAST_DELIMITER = delimiter
             LOG.info("Trying ID at baud %i with delimiter \"%s\"" %
                      (i, repr(delimiter)))
-            ser.setBaudrate(i)
+            ser.baudrate = i
             ser.write(LAST_DELIMITER[0])
             ser.read(25)
             resp = command(ser, "ID")
@@ -1189,7 +1189,7 @@ class THK2Radio(KenwoodLiveRadio):
         rf.valid_bands = [(136000000, 173990000)]
         rf.valid_skips = ["", "S"]
         rf.valid_tuning_steps = [5.0]
-        rf.memory_bounds = (1, 50)
+        rf.memory_bounds = (0, 49)
         return rf
 
     def _cmd_get_memory(self, number):
diff --git a/chirp/drivers/kguv8d.py b/chirp/drivers/kguv8d.py
index bc2c299..785d2f5 100644
--- a/chirp/drivers/kguv8d.py
+++ b/chirp/drivers/kguv8d.py
@@ -311,6 +311,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
     def _read_record(self):
         # read 4 chars for the header
         _header = self.pipe.read(4)
+        if len(_header) != 4:
+            raise errors.RadioError('Radio did not respond')
         _length = ord(_header[3])
         _packet = self.pipe.read(_length)
         _cs = self._checksum(_header[1:])
@@ -391,6 +393,7 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         except errors.RadioError:
             raise
         except Exception, e:
+            LOG.exception('Unknown error during download process')
             raise errors.RadioError("Failed to communicate with radio: %s" % e)
 
     def _do_download(self, start, end, blocksize):
diff --git a/chirp/drivers/th9800.py b/chirp/drivers/th7800.py
similarity index 65%
copy from chirp/drivers/th9800.py
copy to chirp/drivers/th7800.py
index 1dbdaa7..231030f 100644
--- a/chirp/drivers/th9800.py
+++ b/chirp/drivers/th7800.py
@@ -1,6 +1,7 @@
 # Copyright 2014 Tom Hayward <tom at tomh.us>
 # Copyright 2014 Jens Jensen <af5mi at yahoo.com>
 # Copyright 2014 James Lee N1DDK <jml at jmlzone.com>
+# Copyright 2016 Nathan Crapo <nathan_crapo at yahoo.com>  (TH-7800 only)
 #
 # 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
@@ -20,7 +21,8 @@ import struct
 from chirp.settings import RadioSetting, RadioSettingGroup, \
     RadioSettingValueInteger, RadioSettingValueList, \
     RadioSettingValueBoolean, RadioSettingValueString, \
-    RadioSettingValueFloat, InvalidValueError, RadioSettings
+    RadioSettingValueFloat, InvalidValueError, RadioSettings, \
+    RadioSettingValueMap, zero_indexed_seq_map
 from chirp.chirp_common import format_freq
 import os
 import time
@@ -29,14 +31,14 @@ from datetime import date
 
 LOG = logging.getLogger(__name__)
 
-TH9800_MEM_FORMAT = """
+TH7800_MEM_FORMAT = """
 struct mem {
   lbcd rx_freq[4];
   lbcd tx_freq[4];
   lbcd ctcss[2];
   lbcd dtcs[2];
   u8 power:2,
-     BeatShift:1,
+     clk_sft:1,
      unknown0a:2,
      display:1,     // freq=0, name=1
      scan:2;
@@ -51,8 +53,7 @@ struct mem {
      talkaround:1,
      step:4;
   u8 dtcs_pol:2,
-     bclo:2,
-     unknown3:2,
+     unknown3:4,
      tmode:2;
   lbcd offset[4];
   u8 hsdtype:2,     // off, 2-tone, 5-tone, dtmf
@@ -88,13 +89,11 @@ struct {
         hyper_chan:1,
         right_func_key:1;
     u8  tbst_freq:2,
-        ani_display:1,
-        unk0xdc25_4:1
+        unk0xdc25_4:2,
         mute_mode:2,
         unk0xdc25_10:2;
-    u8  auto_xfer:1,
-        auto_contact:1,
-        unk0xdc26_54:2,
+    u8  ars:1,
+        unk0xdc26_54:3,
         auto_am:1,
         unk0xdc26_210:3;
     u8  unk0xdc27_76543:5,
@@ -103,16 +102,15 @@ struct {
         scan_resume:1;
     u16 scramb_freq;
     u16 scramb_freq1;
-    u8  exit_delay;
+    u8  unk0xdc2c;
     u8  unk0xdc2d;
     u8  unk0xdc2e:5,
         right_sql:3;
-    u8  unk0xdc2f:4,
-        beep_vol:4;
+    u8  unk0xdc2f:8;
     u8  tot;
-    u8  tot_alert;
-    u8  tot_rekey;
-    u8  tot_reset;
+    u8  unk0xdc30;
+    u8  unk0xdc31;
+    u8  unk0xdc32;
     u8  unk0xdc34;
     u8  unk0xdc35;
     u8  unk0xdc36;
@@ -170,8 +168,23 @@ STEPS = [2.5, 5.0, 6.25, 7.5, 8.33, 10.0, 12.5,
          15.0, 20.0, 25.0, 30.0, 50.0, 100.0]
 
 
-class TYTTH9800Base(chirp_common.Radio):
-    """Base class for TYT TH-9800"""
+def add_radio_setting(radio_setting_group, mem_field, ui_name, option_map,
+                      current, doc=None):
+    setting = RadioSetting(mem_field, ui_name,
+                           RadioSettingValueMap(option_map, current))
+    if doc is not None:
+        setting.set_doc(doc)
+    radio_setting_group.append(setting)
+
+
+def add_radio_bool(radio_setting_group, mem_field, ui_name, current, doc=None):
+    setting = RadioSetting(mem_field, ui_name,
+                           RadioSettingValueBoolean(bool(current)))
+    radio_setting_group.append(setting)
+
+
+class TYTTH7800Base(chirp_common.Radio):
+    """Base class for TYT TH-7800"""
     VENDOR = "TYT"
 
     def get_features(self):
@@ -186,12 +199,9 @@ class TYTTH9800Base(chirp_common.Radio):
         rf.has_ctone = False
         rf.valid_power_levels = POWER_LEVELS
         rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "#*-+"
-        rf.valid_bands = [(26000000,  33000000),
-                          (47000000,  54000000),
-                          (108000000, 180000000),
+        rf.valid_bands = [(108000000, 180000000),
                           (350000000, 399995000),
-                          (400000000, 512000000),
-                          (750000000, 950000000)]
+                          (400000000, 512000000)]
         rf.valid_skips = SCAN_MODES
         rf.valid_modes = MODES + ["AM"]
         rf.valid_name_length = 6
@@ -200,7 +210,7 @@ class TYTTH9800Base(chirp_common.Radio):
 
     def process_mmap(self):
         self._memobj = bitwise.parse(
-            TH9800_MEM_FORMAT %
+            TH7800_MEM_FORMAT %
             (self._mmap_offset, self._scanlimits_offset, self._settings_offset,
              self._chan_active_offset, self._info_offset), self._mmap)
 
@@ -282,48 +292,21 @@ class TYTTH9800Base(chirp_common.Radio):
 
         mem.extra = RadioSettingGroup("extra", "Extra")
 
-        opts = ["Frequency", "Name"]
-        display = RadioSetting(
-                "display", "Display",
-                RadioSettingValueList(opts, opts[_mem.display]))
-        mem.extra.append(display)
-
-        bclo = RadioSetting(
-                "bclo", "Busy Lockout",
-                RadioSettingValueList(BUSY_LOCK, BUSY_LOCK[_mem.bclo]))
-        bclo.set_doc("Busy Lockout")
-        mem.extra.append(bclo)
-
-        emphasis = RadioSetting(
-                "emphasis", "Emphasis",
-                RadioSettingValueBoolean(bool(_mem.emphasis)))
-        emphasis.set_doc("Boosts 300Hz to 2500Hz mic response")
-        mem.extra.append(emphasis)
-
-        compand = RadioSetting(
-                "compand", "Compand",
-                RadioSettingValueBoolean(bool(_mem.compand)))
-        compand.set_doc("Compress Audio")
-        mem.extra.append(compand)
-
-        BeatShift = RadioSetting(
-                "BeatShift", "BeatShift",
-                RadioSettingValueBoolean(bool(_mem.BeatShift)))
-        BeatShift.set_doc("Beat Shift")
-        mem.extra.append(BeatShift)
-
-        TalkAround = RadioSetting(
-                "talkaround", "Talk Around",
-                RadioSettingValueBoolean(bool(_mem.talkaround)))
-        TalkAround.set_doc("Simplex mode when out of range of repeater")
-        mem.extra.append(TalkAround)
-
-        scramb = RadioSetting(
-                "scramb", "Scramble",
-                RadioSettingValueBoolean(bool(_mem.scramb)))
-        scramb.set_doc("Frequency inversion Scramble")
-        mem.extra.append(scramb)
-
+        add_radio_setting(mem.extra, "display", "Display",
+                          zero_indexed_seq_map(["Frequency", "Name"]),
+                          _mem.display)
+        add_radio_setting(mem.extra, "hsdtype", "HSD TYPE",
+                          zero_indexed_seq_map(["OFF", "2TON", "5TON",
+                                                "DTMF"]),
+                          _mem.hsdtype)
+        add_radio_bool(mem.extra, "clk_sft", "CLK-SFT", _mem.clk_sft)
+        add_radio_bool(mem.extra, "compand", "Compand", _mem.compand,
+                       doc="Compress Audio")
+        add_radio_bool(mem.extra, "talkaround", "Talk Around", _mem.talkaround,
+                       doc="Simplex mode when out of range of repeater")
+
+        add_radio_bool(mem.extra, "scramb", "Scramble", _mem.scramb,
+                       doc="Frequency inversion Scramble")
         return mem
 
     def set_memory(self, mem):
@@ -412,107 +395,61 @@ class TYTTH9800Base(chirp_common.Radio):
         basic = RadioSettingGroup("basic", "Basic")
         info = RadioSettingGroup("info", "Model Info")
         top = RadioSettings(basic, info)
-        basic.append(RadioSetting(
-                "beep", "Beep",
-                RadioSettingValueBoolean(_settings.beep)))
-        basic.append(RadioSetting(
-                "beep_vol", "Beep Volume",
-                RadioSettingValueInteger(0, 15, _settings.beep_vol)))
-        basic.append(RadioSetting(
-                "keylock", "Key Lock",
-                RadioSettingValueBoolean(_settings.keylock)))
-        basic.append(RadioSetting(
-                "ani_display", "ANI Display",
-                RadioSettingValueBoolean(_settings.ani_display)))
-        basic.append(RadioSetting(
-                "auto_xfer", "Auto Transfer",
-                RadioSettingValueBoolean(_settings.auto_xfer)))
-        basic.append(RadioSetting(
-                "auto_contact", "Auto Contact Always Remind",
-                RadioSettingValueBoolean(_settings.auto_contact)))
-        basic.append(RadioSetting(
-                "auto_am", "Auto AM",
-                RadioSettingValueBoolean(_settings.auto_am)))
-        basic.append(RadioSetting(
-                "left_sql", "Left Squelch",
-                RadioSettingValueList(
-                    SQLPRESET, SQLPRESET[_settings.left_sql])))
-        basic.append(RadioSetting(
-                "right_sql", "Right Squelch",
-                RadioSettingValueList(
-                    SQLPRESET, SQLPRESET[_settings.right_sql])))
-#      basic.append(RadioSetting("apo", "Auto Power off (0.1h)",
-#              RadioSettingValueInteger(0, 20, _settings.apo)))
-        opts = ["Off"] + ["%0.1f" % (t / 10.0) for t in range(1, 21, 1)]
-        basic.append(RadioSetting(
-                "apo", "Auto Power off (Hours)",
-                RadioSettingValueList(opts, opts[_settings.apo])))
-        opts = ["Off", "1", "2", "3", "Full"]
-        basic.append(RadioSetting(
-                "backlight", "Display Backlight",
-                RadioSettingValueList(opts, opts[_settings.backlight])))
-        opts = ["Off", "Right", "Left", "Both"]
-        basic.append(RadioSetting(
-                "pttlock", "PTT Lock",
-                RadioSettingValueList(opts, opts[_settings.pttlock])))
-        opts = ["Manual", "Auto"]
-        basic.append(RadioSetting(
-                "hyper_chan", "Hyper Channel",
-                RadioSettingValueList(opts, opts[_settings.hyper_chan])))
-        opts = ["Key 1", "Key 2"]
-        basic.append(RadioSetting(
-                "right_func_key", "Right Function Key",
-                RadioSettingValueList(opts, opts[_settings.right_func_key])))
-        opts = ["1000Hz", "1450Hz", "1750Hz", "2100Hz"]
-        basic.append(RadioSetting(
-                "tbst_freq", "Tone Burst Frequency",
-                RadioSettingValueList(opts, opts[_settings.tbst_freq])))
-        opts = ["Off", "TX", "RX", "TX RX"]
-        basic.append(RadioSetting(
-                "mute_mode", "Mute Mode",
-                RadioSettingValueList(opts, opts[_settings.mute_mode])))
-        opts = ["MEM", "MSM"]
-        scanmode = RadioSetting(
-                "scan_mode", "Scan Mode",
-                RadioSettingValueList(opts, opts[_settings.scan_mode]))
-        scanmode.set_doc("MEM = Normal scan, bypass channels marked skip. "
-                         " MSM = Scan only channels marked priority.")
-        basic.append(scanmode)
-        opts = ["TO", "CO"]
-        basic.append(RadioSetting(
-                "scan_resume", "Scan Resume",
-                RadioSettingValueList(opts, opts[_settings.scan_resume])))
-        opts = ["%0.1f" % (t / 10.0) for t in range(0, 51, 1)]
-        basic.append(RadioSetting(
-                "exit_delay", "Span Transit Exit Delay",
-                RadioSettingValueList(opts, opts[_settings.exit_delay])))
+        add_radio_bool(basic, "beep", "Beep", _settings.beep)
+        add_radio_bool(basic, "ars", "Auto Repeater Shift", _settings.ars)
+        add_radio_setting(basic, "keylock", "Key Lock",
+                          zero_indexed_seq_map(["Manual", "Auto"]),
+                          _settings.keylock)
+        add_radio_bool(basic, "auto_am", "Auto AM", _settings.auto_am)
+        add_radio_setting(basic, "left_sql", "Left Squelch",
+                          zero_indexed_seq_map(SQLPRESET),
+                          _settings.left_sql)
+        add_radio_setting(basic, "right_sql", "Right Squelch",
+                          zero_indexed_seq_map(SQLPRESET),
+                          _settings.right_sql)
+        add_radio_setting(basic, "apo", "Auto Power off (Hours)",
+                          [("Off", 0), ("0.5", 5), ("1.0", 10), ("1.5", 15),
+                           ("2.0", 20)],
+                          _settings.apo)
+        add_radio_setting(basic, "backlight", "Display Backlight",
+                          zero_indexed_seq_map(["Off", "1", "2", "3", "Full"]),
+                          _settings.backlight)
+        add_radio_setting(basic, "pttlock", "PTT Lock",
+                          zero_indexed_seq_map(["Off", "Right", "Left",
+                                                "Both"]),
+                          _settings.pttlock)
+        add_radio_setting(basic, "hyper_chan", "Hyper Channel",
+                          zero_indexed_seq_map(["Manual", "Auto"]),
+                          _settings.hyper_chan)
+        add_radio_setting(basic, "right_func_key", "Right Function Key",
+                          zero_indexed_seq_map(["Key 1", "Key 2"]),
+                          _settings.right_func_key)
+        add_radio_setting(basic, "mute_mode", "Mute Mode",
+                          zero_indexed_seq_map(["Off", "TX", "RX", "TX RX"]),
+                          _settings.mute_mode)
+        add_radio_setting(basic, "scan_mode", "Scan Mode",
+                          zero_indexed_seq_map(["MEM", "MSM"]),
+                          _settings.scan_mode,
+                          doc="MEM = Normal scan, bypass channels marked "
+                          "skip. MSM = Scan only channels marked priority.")
+        add_radio_setting(basic, "scan_resume", "Scan Resume",
+                          zero_indexed_seq_map(["Time", "Busy"]),
+                          _settings.scan_resume)
         basic.append(RadioSetting(
                 "tot", "Time Out Timer (minutes)",
                 RadioSettingValueInteger(0, 30, _settings.tot)))
-        basic.append(RadioSetting(
-                "tot_alert", "Time Out Timer Pre Alert(seconds)",
-                RadioSettingValueInteger(0, 15, _settings.tot_alert)))
-        basic.append(RadioSetting(
-                "tot_rekey", "Time Out Rekey (seconds)",
-                RadioSettingValueInteger(0, 15, _settings.tot_rekey)))
-        basic.append(RadioSetting(
-                "tot_reset", "Time Out Reset(seconds)",
-                RadioSettingValueInteger(0, 15, _settings.tot_reset)))
-        basic.append(RadioSetting(
-                "p1", "P1 Function",
-                RadioSettingValueList(MICKEYFUNC, MICKEYFUNC[_settings.p1])))
-        basic.append(RadioSetting(
-                "p2", "P2 Function",
-                RadioSettingValueList(MICKEYFUNC, MICKEYFUNC[_settings.p2])))
-        basic.append(RadioSetting(
-                "p3", "P3 Function",
-                RadioSettingValueList(MICKEYFUNC, MICKEYFUNC[_settings.p3])))
-        basic.append(RadioSetting(
-                "p4", "P4 Function",
-                RadioSettingValueList(MICKEYFUNC, MICKEYFUNC[_settings.p4])))
-#      opts = ["0", "1"]
-#      basic.append(RadioSetting("x", "Desc",
-#            RadioSettingValueList(opts, opts[_settings.x])))
+        add_radio_setting(basic, "p1", "P1 Function",
+                          zero_indexed_seq_map(MICKEYFUNC),
+                          _settings.p1)
+        add_radio_setting(basic, "p2", "P2 Function",
+                          zero_indexed_seq_map(MICKEYFUNC),
+                          _settings.p2)
+        add_radio_setting(basic, "p3", "P3 Function",
+                          zero_indexed_seq_map(MICKEYFUNC),
+                          _settings.p3)
+        add_radio_setting(basic, "p4", "P4 Function",
+                          zero_indexed_seq_map(MICKEYFUNC),
+                          _settings.p4)
 
         def _filter(name):
             filtered = ""
@@ -545,39 +482,41 @@ class TYTTH9800Base(chirp_common.Radio):
         rs = RadioSetting("progdate", "Last Program Date", rsvs)
         info.append(rs)
 
-        # 9 band limits
-        for i in range(0, 9):
-            objname = BANDS[i] + "lorx"
-            objnamepp = BANDS[i] + " Rx Start"
-            # rsv = RadioSettingValueInteger(0, 100000000,
-            #              int(_bandlimits[i].lorx))
-            rsv = RadioSettingValueString(
-                    0, 10, format_freq(int(_bandlimits[i].lorx)*10))
-            rsv.set_mutable(False)
-            rs = RadioSetting(objname, objnamepp, rsv)
-            info.append(rs)
-            objname = BANDS[i] + "hirx"
-            objnamepp = BANDS[i] + " Rx end"
-            rsv = RadioSettingValueString(
-                    0, 10, format_freq(int(_bandlimits[i].hirx)*10))
-            rsv.set_mutable(False)
-            rs = RadioSetting(objname, objnamepp, rsv)
-            info.append(rs)
-            objname = BANDS[i] + "lotx"
-            objnamepp = BANDS[i] + " Tx Start"
-            rsv = RadioSettingValueString(
-                    0, 10, format_freq(int(_bandlimits[i].lotx)*10))
-            rsv.set_mutable(False)
-            rs = RadioSetting(objname, objnamepp, rsv)
-            info.append(rs)
-            objname = BANDS[i] + "hitx"
-            objnamepp = BANDS[i] + " Tx end"
-            rsv = RadioSettingValueString(
-                    0, 10, format_freq(int(_bandlimits[i].hitx)*10))
-            rsv.set_mutable(False)
-            rs = RadioSetting(objname, objnamepp, rsv)
-            info.append(rs)
-
+        # Band Limits
+        for i in range(0, len(BANDS)):
+            rx_start = int(_bandlimits[i].lorx) * 10
+            if not rx_start == 0:
+                objname = BANDS[i] + "lorx"
+                objnamepp = BANDS[i] + " Rx Start"
+                rsv = RadioSettingValueString(0, 10, format_freq(rx_start))
+                rsv.set_mutable(False)
+                rs = RadioSetting(objname, objnamepp, rsv)
+                info.append(rs)
+
+                rx_end = int(_bandlimits[i].hirx) * 10
+                objname = BANDS[i] + "hirx"
+                objnamepp = BANDS[i] + " Rx end"
+                rsv = RadioSettingValueString(0, 10, format_freq(rx_end))
+                rsv.set_mutable(False)
+                rs = RadioSetting(objname, objnamepp, rsv)
+                info.append(rs)
+
+            tx_start = int(_bandlimits[i].lotx) * 10
+            if not tx_start == 0:
+                objname = BANDS[i] + "lotx"
+                objnamepp = BANDS[i] + " Tx Start"
+                rsv = RadioSettingValueString(0, 10, format_freq(tx_start))
+                rsv.set_mutable(False)
+                rs = RadioSetting(objname, objnamepp, rsv)
+                info.append(rs)
+
+                tx_end = int(_bandlimits[i].hitx) * 10
+                objname = BANDS[i] + "hitx"
+                objnamepp = BANDS[i] + " Tx end"
+                rsv = RadioSettingValueString(0, 10, format_freq(tx_end))
+                rsv.set_mutable(False)
+                rs = RadioSetting(objname, objnamepp, rsv)
+                info.append(rs)
         return top
 
     def set_settings(self, settings):
@@ -603,9 +542,9 @@ class TYTTH9800Base(chirp_common.Radio):
 
 
 @directory.register
-class TYTTH9800File(TYTTH9800Base, chirp_common.FileBackedRadio):
-    """TYT TH-9800 .dat file"""
-    MODEL = "TH-9800 File"
+class TYTTH7800File(TYTTH7800Base, chirp_common.FileBackedRadio):
+    """TYT TH-7800 .dat file"""
+    MODEL = "TH-7800 File"
 
     FILE_EXTENSION = "dat"
 
@@ -634,7 +573,7 @@ class TYTTH9800File(TYTTH9800Base, chirp_common.FileBackedRadio):
 def _identify(radio):
     """Do identify handshake with TYT"""
     try:
-        radio.pipe.write("\x02PROGRA")
+        radio.pipe.write("\x02SPECPR")
         ack = radio.pipe.read(1)
         if ack != "A":
             util.hexprint(ack)
@@ -644,17 +583,17 @@ def _identify(radio):
         LOG.debug(util.hexprint(ack))
         raise errors.RadioError("Unable to communicate with the radio")
 
-    radio.pipe.write("M\x02")
+    radio.pipe.write("G\x02")
     ident = radio.pipe.read(16)
     radio.pipe.write("A")
-    r = radio.pipe.read(1)
+    r = radio.pipe.read(2)
     if r != "A":
         raise errors.RadioError("Ack failed")
     return ident
 
 
 def _download(radio, memsize=0x10000, blocksize=0x80):
-    """Download from TYT TH-9800"""
+    """Download from TYT TH-7800"""
     data = _identify(radio)
     LOG.info("ident:", util.hexprint(data))
     offset = 0x100
@@ -685,7 +624,7 @@ def _download(radio, memsize=0x10000, blocksize=0x80):
 
 
 def _upload(radio, memsize=0xF400, blocksize=0x80):
-    """Upload to TYT TH-9800"""
+    """Upload to TYT TH-7800"""
     data = _identify(radio)
 
     radio.pipe.timeout = 1
@@ -742,10 +681,10 @@ def _upload(radio, memsize=0xF400, blocksize=0x80):
 
 
 @directory.register
-class TYTTH9800Radio(TYTTH9800Base, chirp_common.CloneModeRadio,
+class TYTTH7800Radio(TYTTH7800Base, chirp_common.CloneModeRadio,
                      chirp_common.ExperimentalRadio):
     VENDOR = "TYT"
-    MODEL = "TH-9800"
+    MODEL = "TH-7800"
     BAUD_RATE = 38400
 
     _memsize = 65296
@@ -757,13 +696,27 @@ class TYTTH9800Radio(TYTTH9800Base, chirp_common.CloneModeRadio,
 
     @classmethod
     def match_model(cls, filedata, filename):
-        return len(filedata) == cls._memsize
+        if len(filedata) != cls._memsize:
+            return False
+        # TYT used TH9800 as model for TH-7800 _AND_ TH-9800.  Check
+        # for TH7800 in case they fix it or if users update the model
+        # in their own radio.
+        if not (filedata[0xfe18:0xfe1e] == "TH9800" or
+                filedata[0xfe18:0xfe1e] == "TH7800"):
+            return False
+        # TH-7800 bandlimits differ from TH-9800.  First band Invalid
+        # (zero).
+        first_bandlimit = struct.unpack("BBBBBBBBBBBBBBBB",
+                                        filedata[0xfe40:0xfe50])
+        if not all(v == 0 for v in first_bandlimit):
+            return False
+        return True
 
     @classmethod
     def get_prompts(cls):
         rp = chirp_common.RadioPrompts()
         rp.experimental = (
-         'This is experimental support for TH-9800 '
+         'This is experimental support for TH-7800 '
          'which is still under development.\n'
          'Please ensure you have a good backup with OEM software.\n'
          'Also please send in bug and enhancement requests!\n'
diff --git a/chirp/drivers/th9800.py b/chirp/drivers/th9800.py
index 1dbdaa7..9d21fd7 100644
--- a/chirp/drivers/th9800.py
+++ b/chirp/drivers/th9800.py
@@ -757,7 +757,18 @@ class TYTTH9800Radio(TYTTH9800Base, chirp_common.CloneModeRadio,
 
     @classmethod
     def match_model(cls, filedata, filename):
-        return len(filedata) == cls._memsize
+        if len(filedata) != cls._memsize:
+            return False
+        # TYT set this model for TH-7800 _AND_ TH-9800
+        if not filedata[0xfe18:0xfe1e] == "TH9800":
+            return False
+        # TH-9800 bandlimits differ from TH-7800.  First band is used
+        # (non-zero).
+        first_bandlimit = struct.unpack("BBBBBBBBBBBBBBBB",
+                                        filedata[0xfe40:0xfe50])
+        if all(v == 0 for v in first_bandlimit):
+            return False
+        return True
 
     @classmethod
     def get_prompts(cls):
diff --git a/chirp/drivers/thd72.py b/chirp/drivers/thd72.py
index 20a86b7..1cae042 100644
--- a/chirp/drivers/thd72.py
+++ b/chirp/drivers/thd72.py
@@ -231,7 +231,7 @@ class THD72Radio(chirp_common.CloneModeRadio):
 
     def _detect_baud(self):
         for baud in [9600, 19200, 38400, 57600]:
-            self.pipe.setBaudrate(baud)
+            self.pipe.baudrate = baud
             try:
                 self.pipe.write("\r\r")
             except:
@@ -422,9 +422,11 @@ class THD72Radio(chirp_common.CloneModeRadio):
             raise errors.RadioError("No response from self")
 
         allblocks = range(self._memsize/256)
-        self.pipe.setBaudrate(57600)
-        self.pipe.getCTS()
-        self.pipe.setRTS()
+        self.pipe.baudrate = 57600
+        try:
+            self.pipe.setRTS()
+        except AttributeError:
+            self.pipe.rts = True
         self.pipe.read(1)
         data = ""
         LOG.debug("reading blocks %d..%d" % (blocks[0], blocks[-1]))
@@ -458,9 +460,11 @@ class THD72Radio(chirp_common.CloneModeRadio):
         if self.command("0M PROGRAM") != "0M":
             raise errors.RadioError("No response from self")
 
-        self.pipe.setBaudrate(57600)
-        self.pipe.getCTS()
-        self.pipe.setRTS()
+        self.pipe.baudrate = 57600
+        try:
+            self.pipe.setRTS()
+        except AttributeError:
+            self.pipe.rts = True
         self.pipe.read(1)
         LOG.debug("writing blocks %d..%d" % (blocks[0], blocks[-1]))
         total = len(blocks)
diff --git a/chirp/drivers/tk270.py b/chirp/drivers/tk270.py
index 4481258..1b76027 100644
--- a/chirp/drivers/tk270.py
+++ b/chirp/drivers/tk270.py
@@ -194,10 +194,9 @@ def open_radio(radio):
     """Open the radio into program mode and check if it's the correct model"""
     # Set serial discipline
     try:
-        radio.pipe.setParity("N")
-        radio.pipe.setTimeout(TIMEOUT)
-        radio.pipe.flushOutput()
-        radio.pipe.flushInput()
+        radio.pipe.parity = "N"
+        radio.pipe.timeout = TIMEOUT
+        radio.pipe.flush()
     except:
         msg = "Serial error: Can't set serial line discipline"
         raise errors.RadioError(msg)
diff --git a/chirp/drivers/tk760.py b/chirp/drivers/tk760.py
index bfed6b1..760a3e3 100644
--- a/chirp/drivers/tk760.py
+++ b/chirp/drivers/tk760.py
@@ -188,10 +188,9 @@ def open_radio(radio):
     """Open the radio into program mode and check if it's the correct model"""
     # Set serial discipline
     try:
-        radio.pipe.setParity("N")
-        radio.pipe.setTimeout(TIMEOUT)
-        radio.pipe.flushOutput()
-        radio.pipe.flushInput()
+        radio.pipe.parity = "N"
+        radio.pipe.timeout = TIMEOUT
+        radio.pipe.flush()
         LOG.debug("Serial port open successful")
     except:
         msg = "Serial error: Can't set serial line discipline"
diff --git a/chirp/drivers/tk760g.py b/chirp/drivers/tk760g.py
index 923b8b0..00cd57d 100644
--- a/chirp/drivers/tk760g.py
+++ b/chirp/drivers/tk760g.py
@@ -441,8 +441,8 @@ def _recv(radio):
 def _open_radio(radio, status):
     """Open the radio into program mode and check if it's the correct model"""
     # 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")
+    radio.pipe.timeout = 0.25
+    radio.pipe.parity = "E"
 
     # DEBUG
     LOG.debug("Entering program mode.")
@@ -525,17 +525,17 @@ def do_download(radio):
     # set the timeout and if windows keep it bigger
     if sys.platform in ["win32", "cygwin"]:
         # bigger timeout
-        radio.pipe.setTimeout(0.55)
+        radio.pipe.timeout = 0.55
     else:
         # Linux can keep up, MAC?
-        radio.pipe.setTimeout(0.05)
+        radio.pipe.timeout = 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()
+        radio.pipe.flush()
         _send(radio, _make_frame("R", addr))
 
         # now we get the data
@@ -574,7 +574,7 @@ def do_upload(radio):
     radio.status_fn(status)
 
     # the default for the original soft as measured
-    radio.pipe.setTimeout(0.5)
+    radio.pipe.timeout = 0.5
 
     # DEBUG
     LOG.debug("Starting the upload to the radio")
diff --git a/chirp/drivers/tk8102.py b/chirp/drivers/tk8102.py
index 068314a..b4491c4 100644
--- a/chirp/drivers/tk8102.py
+++ b/chirp/drivers/tk8102.py
@@ -99,7 +99,7 @@ def do_ident(radio):
 
 
 def do_download(radio):
-    radio.pipe.setParity("E")
+    radio.pipe.parity = "E"
     radio.pipe.timeout = 1
     do_ident(radio)
 
@@ -129,7 +129,7 @@ def do_download(radio):
 
 
 def do_upload(radio):
-    radio.pipe.setParity("E")
+    radio.pipe.parity = "E"
     radio.pipe.timeout = 1
     do_ident(radio)
 
diff --git a/chirp/drivers/tmv71.py b/chirp/drivers/tmv71.py
index 7c85c38..03e0ef3 100644
--- a/chirp/drivers/tmv71.py
+++ b/chirp/drivers/tmv71.py
@@ -36,7 +36,7 @@ class TMV71ARadio(chirp_common.CloneModeRadio):
 
     def _detect_baud(self):
         for baud in [9600, 19200, 38400, 57600]:
-            self.pipe.setBaudrate(baud)
+            self.pipe.baudrate = baud
             self.pipe.write("\r\r")
             self.pipe.read(32)
             try:
diff --git a/chirp/drivers/uv5r.py b/chirp/drivers/uv5r.py
index 38dca8a..9510831 100644
--- a/chirp/drivers/uv5r.py
+++ b/chirp/drivers/uv5r.py
@@ -572,8 +572,6 @@ UV5R_CHARSET = chirp_common.CHARSET_UPPER_NUMERIC + \
     "!@#$%^&*()+-=[]:\";'<>?,./"
 
 
-# Uncomment this to actually register this radio in CHIRP
- at directory.register
 class BaofengUV5R(chirp_common.CloneModeRadio,
                   chirp_common.ExperimentalRadio):
     """Baofeng UV-5R"""
@@ -1571,6 +1569,16 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
                 raise
 
 
+class UV5XAlias(chirp_common.Alias):
+    VENDOR = "Baofeng"
+    MODEL = "UV-5X"
+
+
+ at directory.register
+class BaofengUV5RGeneric(BaofengUV5R):
+    ALIASES = [UV5XAlias]
+
+
 @directory.register
 class BaofengF11Radio(BaofengUV5R):
     VENDOR = "Baofeng"
diff --git a/chirp/drivers/vgc.py b/chirp/drivers/vgc.py
new file mode 100644
index 0000000..e47a057
--- /dev/null
+++ b/chirp/drivers/vgc.py
@@ -0,0 +1,1449 @@
+# Copyright 2016:
+# * Jim Unroe KC9HI, <rock.unroe at gmail.com>
+# * Pavel Milanes CO7WT <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
+# 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
+import re
+
+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, \
+    RadioSettingValueFloat, RadioSettings
+from textwrap import dedent
+
+MEM_FORMAT = """
+struct mem {
+  lbcd rxfreq[4];
+  lbcd txfreq[4];
+  lbcd rxtone[2];
+  lbcd txtone[2];
+  u8 unknown0:2,
+     txp:2,
+     wn:2,
+     unknown1:1,
+     bcl:1;
+  u8 unknown2:2,
+     revert:1,
+     dname:1,
+     unknown3:4;
+  u8 unknown4[2];
+};
+
+struct nam {
+  char name[6];
+  u8 unknown1[2];
+};
+
+#seekto 0x0000;
+struct mem left_memory[500];
+
+#seekto 0x2000;
+struct mem right_memory[500];
+
+#seekto 0x4000;
+struct nam left_names[500];
+
+#seekto 0x5000;
+struct nam right_names[500];
+
+#seekto 0x6000;
+u8 left_usedflags[64];
+
+#seekto 0x6040;
+u8 left_scanflags[64];
+
+#seekto 0x6080;
+u8 right_usedflags[64];
+
+#seekto 0x60C0;
+u8 right_scanflags[64];
+
+#seekto 0x6160;
+struct {
+  char line32[32];
+} embedded_msg;
+
+#seekto 0x6180;
+struct {
+  u8  sbmute:2,        // sub band mute
+      unknown1:1,
+      workmodb:1,      // work mode (right side)
+      dw:1,            // dual watch
+      audio:1,         // audio output mode (stereo/mono)
+      unknown2:1,
+      workmoda:1;      // work mode (left side)
+  u8  scansb:1,        // scan stop beep
+      aftone:3,        // af tone control
+      scand:1,         // scan directon
+      scanr:3;         // scan resume
+  u8  rxexp:1,         // rx expansion
+      ptt:1,           // ptt mode
+      display:1,       // display select (frequency/clock)
+      omode:1,         // operaton mode
+      beep:2,          // beep volume
+      spkr:2;          // speaker
+  u8  cpuclk:1,        // operating mode(cpu clock)
+      fkey:3,          // fkey function
+      mrscan:1,        // memory scan type
+      color:3;         // lcd backlight color
+  u8  vox:2,           // vox
+      voxs:3,          // vox sensitivity
+      mgain:3;         // mic gain
+  u8  wbandb:4,        // work band (right side)
+      wbanda:4;        // work band (left side)
+  u8  sqlb:4,          // squelch level (right side)
+      sqla:4;          // squelch level (left side)
+  u8  apo:4,           // auto power off
+      ars:1,           // automatic repeater shift
+      tot:3;           // time out timer
+  u8  stepb:4,         // auto step (right side)
+      stepa:4;         // auto step (left side)
+  u8  rxcoverm:1,      // rx coverage-memory
+      lcdc:3,          // lcd contrast
+      rxcoverv:1,      // rx coverage-vfo
+      lcdb:3;          // lcd brightness
+  u8  smode:1,         // smart function mode
+      timefmt:1,       // time format
+      datefmt:2,       // date format
+      timesig:1,       // time signal
+      keyb:3;          // key/led brightness
+  u8  dwstop:1,        // dual watch stop
+      unknown3:1,
+      sqlexp:1,        // sql expansion
+      decbandsel:1,    // decoding band select
+      dtmfmodenc:1,    // dtmf mode encode
+      bell:3;          // bell ringer
+  u8  unknown4:2,
+      btime:6;         // lcd backlight time
+  u8  unknown5:2,
+      tz:6;            // time zone
+  u8  unknown618E;
+  u8  unknown618F;
+  ul16  offseta;       // work offset (left side)
+  ul16  offsetb;       // work offset (right side)
+  ul16  mrcha;         // selected memory channel (left)
+  ul16  mrchb;         // selected memory channel (right)
+  ul16  wpricha;       // work priority channel (left)
+  ul16  wprichb;       // work priority channel (right)
+  u8  unknown6:3,
+      datasql:2,       // data squelch
+      dataspd:1,       // data speed
+      databnd:2;       // data band select
+  u8  unknown7:1,
+      pfkey2:3,        // mic p2 key
+      unknown8:1,
+      pfkey1:3;        // mic p1 key
+  u8  unknown9:1,
+      pfkey4:3,        // mic p4 key
+      unknowna:1,
+      pfkey3:3;        // mic p3 key
+  u8  unknownb:7,
+      dtmfmoddec:1;    // dtmf mode decode
+} settings;
+
+#seekto 0x61B0;
+struct {
+  char line16[16];
+} poweron_msg;
+
+#seekto 0x6300;
+struct {
+  u8  unknown1:3,
+      ttdgt:5;         // dtmf digit time
+  u8  unknown2:3,
+      ttint:5;         // dtmf interval time
+  u8  unknown3:3,
+      tt1stdgt:5;      // dtmf 1st digit time
+  u8  unknown4:3,
+      tt1stdly:5;      // dtmf 1st digit delay
+  u8  unknown5:3,
+      ttdlyqt:5;       // dtmf delay when use qt
+  u8  unknown6:3,
+      ttdkey:5;        // dtmf d key function
+  u8  unknown7;
+  u8  unknown8:4,
+      ttautod:4;       // dtmf auto dial group
+} dtmf;
+
+#seekto 0x6330;
+struct {
+  u8  unknown1:7,
+      ttsig:1;         // dtmf signal
+  u8  unknown2:4,
+      ttintcode:4;     // dtmf interval code
+  u8  unknown3:5,
+      ttgrpcode:3;     // dtmf group code
+  u8  unknown4:4,
+      ttautorst:4;     // dtmf auto reset time
+  u8  unknown5:5,
+      ttalert:3;       // dtmf alert tone/transpond
+} dtmf2;
+
+#seekto 0x6360;
+struct {
+  u8 code1[8];         // dtmf code
+  u8 code1_len;        // dtmf code length
+  u8 unknown1[7];
+  u8 code2[8];         // dtmf code
+  u8 code2_len;        // dtmf code length
+  u8 unknown2[7];
+  u8 code3[8];         // dtmf code
+  u8 code3_len;        // dtmf code length
+  u8 unknown3[7];
+  u8 code4[8];         // dtmf code
+  u8 code4_len;        // dtmf code length
+  u8 unknown4[7];
+  u8 code5[8];         // dtmf code
+  u8 code5_len;        // dtmf code length
+  u8 unknown5[7];
+  u8 code6[8];         // dtmf code
+  u8 code6_len;        // dtmf code length
+  u8 unknown6[7];
+  u8 code7[8];         // dtmf code
+  u8 code7_len;        // dtmf code length
+  u8 unknown7[7];
+  u8 code8[8];         // dtmf code
+  u8 code8_len;        // dtmf code length
+  u8 unknown8[7];
+  u8 code9[8];         // dtmf code
+  u8 code9_len;        // dtmf code length
+  u8 unknown9[7];
+} dtmfcode;
+
+"""
+
+MEM_SIZE = 0x8000
+BLOCK_SIZE = 0x40
+MODES = ["FM", "Auto", "NFM", "AM"]
+SKIP_VALUES = ["", "S"]
+TONES = chirp_common.TONES
+DTCS_CODES = chirp_common.DTCS_CODES
+NAME_LENGTH = 6
+DTMF_CHARS = list("0123456789ABCD*#")
+STIMEOUT = 1
+
+# Basic settings lists
+LIST_AFTONE = ["Low-3", "Low-2", "Low-1", "Normal", "High-1", "High-2"]
+LIST_SPKR = ["Off", "Front", "Rear", "Front + Rear"]
+LIST_AUDIO = ["Monaural", "Stereo"]
+LIST_SBMUTE = ["Off", "TX", "RX", "Both"]
+LIST_MLNHM = ["Min", "Low", "Normal", "High", "Max"]
+LIST_PTT = ["Momentary", "Toggle"]
+LIST_RXEXP = ["General", "Wide coverage"]
+LIST_VOX = ["Off", "Internal mic", "Front hand-mic", "Rear hand-mic"]
+LIST_DISPLAY = ["Frequency", "Timer/Clock"]
+LIST_MINMAX = ["Min"] + ["%s" % x for x in range(2, 8)] + ["Max"]
+LIST_COLOR = ["White-Blue", "Sky-Blue", "Marine-Blue", "Green",
+              "Yellow-Green", "Orange", "Amber", "White"]
+LIST_BTIME = ["Continuous"] + ["%s" % x for x in range(1, 61)]
+LIST_MRSCAN = ["All", "Selected"]
+LIST_DWSTOP = ["Auto", "Hold"]
+LIST_SCAND = ["Down", "Up"]
+LIST_SCANR = ["Busy", "Hold", "1 sec", "3 sec", "5 sec"]
+LIST_APO = ["Off", ".5", "1", "1.5"] + ["%s" % x for x in range(2, 13)]
+LIST_BEEP = ["Off", "Low", "High"]
+LIST_FKEY = ["MHz/AD-F", "AF Dual 1(line-in)", "AF Dual 2(AM)", "AF Dual 3(FM)",
+             "PA", "SQL off", "T-call", "WX"]
+LIST_PFKEY = ["Off", "SQL off", "TX power", "Scan", "RPT shift", "Reverse",
+              "T-Call"]
+LIST_AB = ["A", "B"]
+LIST_COVERAGE = ["In band", "All"]
+LIST_TOT = ["Off"] + ["%s" % x for x in range(5, 25, 5)] + ["30"]
+LIST_DATEFMT = ["yyyy/mm/dd", "yyyy/dd/mm", "mm/dd/yyyy", "dd/mm/yyyy"]
+LIST_TIMEFMT = ["24H", "12H"]
+LIST_TZ = ["-12 INT DL W",
+           "-11 MIDWAY",
+           "-10 HAST",
+           "-9 AKST",
+           "-8 PST",
+           "-7 MST",
+           "-6 CST",
+           "-5 EST",
+           "-4:30 CARACAS",
+           "-4 AST",
+           "-3:30 NST",
+           "-3 BRASILIA",
+           "-2 MATLANTIC",
+           "-1 AZORES",
+           "-0 LONDON",
+           "+0 LONDON",
+           "+1 ROME",
+           "+2 ATHENS",
+           "+3 MOSCOW",
+           "+3:30 REHRW",
+           "+4 ABUDNABI",
+           "+4:30 KABUL",
+           "+5 ISLMABAD",
+           "+5:30 NEWDELHI",
+           "+6 DHAKA",
+           "+6:30 YANGON",
+           "+7 BANKOK",
+           "+8 BEIJING",
+           "+9 TOKYO",
+           "+10 ADELAIDE",
+           "+10 SYDNET",
+           "+11 NWCLDNIA",
+           "+12 FIJI",
+           "+13 NUKALOFA"
+           ]
+LIST_BELL = ["Off", "1 time", "3 times", "5 times", "8 times", "Continuous"]
+LIST_DATABND = ["Main band", "Sub band", "Left band-fixed", "Right band-fixed"]
+LIST_DATASPD = ["1200 bps", "9600 bps"]
+LIST_DATASQL = ["Busy/TX", "Busy", "TX"]
+
+# Other settings lists
+LIST_CPUCLK = ["Clock frequency 1", "Clock frequency 2"]
+
+# Work mode settings lists
+LIST_WORK = ["VFO", "Memory System"]
+LIST_WBANDB = ["Air", "H-V", "GR1-V", "GR1-U", "H-U", "GR2"]
+LIST_WBANDA = ["Line-in", "AM", "FM"] + LIST_WBANDB
+LIST_SQL = ["Open"] + ["%s" % x for x in range(1, 10)]
+LIST_STEP = ["Auto", "2.50 KHz", "5.00 KHz", "6.25 KHz", "8.33 KHz",
+             "9.00 KHz", "10.00 KHz", "12.50 KHz", "15.00 KHz", "20.00 KHz",
+             "25.00 KHz", "50.00 KHz", "100.00 KHz", "200.00 KHz"]
+LIST_SMODE = ["F-1", "F-2"]
+
+# DTMF settings lists
+LIST_TTDKEY = ["D code"] + ["Send delay %s s" % x for x in range(1, 17)]
+LIST_TT200 = ["%s ms" % x for x in range(50, 210, 10)]
+LIST_TT1000 = ["%s ms" % x for x in range(100, 1050, 50)]
+LIST_TTSIG = ["Code squelch", "Select call"]
+LIST_TTAUTORST = ["Off"] + ["%s s" % x for x in range(1, 16)]
+LIST_TTGRPCODE = ["Off"] + list("ABCD*#")
+LIST_TTINTCODE = DTMF_CHARS
+LIST_TTALERT = ["Off", "Alert tone", "Transpond", "Transpond-ID code",
+                "Transpond-transpond code"]
+LIST_TTAUTOD = ["%s" % x for x in range(1, 10)]
+
+# valid chars on the LCD
+VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
+    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
+
+# Power Levels
+POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=5),
+                chirp_common.PowerLevel("Mid", watts=20),
+                chirp_common.PowerLevel("High", watts=50)]
+
+# B-TECH UV-50X3 id string
+UV50X3_id  = "VGC6600MD"
+
+
+def _clean_buffer(radio):
+    radio.pipe.timeout = 0.005
+    junk = radio.pipe.read(256)
+    radio.pipe.timeout = STIMEOUT
+    if junk:
+        Log.debug("Got %i bytes of junk before starting" % len(junk))
+
+
+def _check_for_double_ack(radio):
+    radio.pipe.timeout = 0.005
+    c = radio.pipe.read(1)
+    radio.pipe.timeout = STIMEOUT
+    if c and c != '\x06':
+        _exit_program_mode(radio)
+        raise errors.RadioError('Expected nothing or ACK, got %r' % c)
+
+
+def _rawrecv(radio, amount):
+    """Raw read from the radio device"""
+    data = ""
+    try:
+        data = radio.pipe.read(amount)
+    except:
+        _exit_program_mode(radio)
+        msg = "Generic error reading data from radio; check your cable."
+        raise errors.RadioError(msg)
+
+    if len(data) != amount:
+        _exit_program_mode(radio)
+        msg = "Error reading data from radio: not the amount of data we want."
+        raise errors.RadioError(msg)
+
+    return data
+
+
+def _rawsend(radio, data):
+    """Raw send to the radio device"""
+    try:
+        radio.pipe.write(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 = struct.pack(">BHB", ord(cmd), addr, length)
+    # add the data if set
+    if len(data) != 0:
+        frame += data
+    # return the data
+    return frame
+
+
+def _recv(radio, addr, length=BLOCK_SIZE):
+    """Get data from the radio """
+    # read 4 bytes of header
+    hdr = _rawrecv(radio, 4)
+
+    # check for unexpected extra command byte
+    c, a, l = struct.unpack(">BHB", hdr)
+    if hdr[0:2] == "WW" and a != addr:
+        # extra command byte detected
+        # throw away the 1st byte and add the next byte in the buffer
+        hdr = hdr[1:] + _rawrecv(radio, 1)
+
+    # read 64 bytes (0x40) of data
+    data = _rawrecv(radio, (BLOCK_SIZE))
+
+    # DEBUG
+    LOG.info("Response:")
+    LOG.debug(util.hexprint(hdr + data))
+
+    c, a, l = struct.unpack(">BHB", hdr)
+    if a != addr or l != length or c != ord("W"):
+        _exit_program_mode(radio)
+        LOG.error("Invalid answer for block 0x%04x:" % addr)
+        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
+        raise errors.RadioError("Unknown response from the radio")
+
+    return data
+
+
+def _do_ident(radio):
+    """Put the radio in PROGRAM mode & identify it"""
+    #  set the serial discipline
+    radio.pipe.baudrate = 115200
+    radio.pipe.parity = "N"
+    radio.pipe.timeout = STIMEOUT
+
+    # flush input buffer
+    _clean_buffer(radio)
+
+    magic = "V66LINK"
+
+    _rawsend(radio, magic)
+
+    # Ok, get the ident string
+    ident = _rawrecv(radio, 9)
+
+    # check if ident is OK
+    if ident != radio.IDENT:
+        # bad ident
+        msg = "Incorrect model ID, got this:"
+        msg +=  util.hexprint(ident)
+        LOG.debug(msg)
+        raise errors.RadioError("Radio identification failed.")
+
+    # DEBUG
+    LOG.info("Positive ident, got this:")
+    LOG.debug(util.hexprint(ident))
+
+    return True
+
+
+def _exit_program_mode(radio):
+    endframe = "\x45"
+    _rawsend(radio, endframe)
+
+
+def _download(radio):
+    """Get the memory map"""
+
+    # put radio in program mode and identify it
+    _do_ident(radio)
+
+    # UI progress
+    status = chirp_common.Status()
+    status.cur = 0
+    status.max = MEM_SIZE / BLOCK_SIZE
+    status.msg = "Cloning from radio..."
+    radio.status_fn(status)
+
+    data = ""
+    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
+        frame = _make_frame("R", addr, BLOCK_SIZE)
+        # DEBUG
+        LOG.info("Request sent:")
+        LOG.debug(util.hexprint(frame))
+
+        # sending the read request
+        _rawsend(radio, frame)
+
+        # now we 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)
+
+    _exit_program_mode(radio)
+
+    return data
+
+
+def _upload(radio):
+    """Upload procedure"""
+
+    MEM_SIZE = 0x7000
+
+    # put radio in program mode and identify it
+    _do_ident(radio)
+
+    # UI progress
+    status = chirp_common.Status()
+    status.cur = 0
+    status.max = MEM_SIZE / BLOCK_SIZE
+    status.msg = "Cloning to radio..."
+    radio.status_fn(status)
+
+    # the fun start here
+    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
+        # sending the data
+        data = radio.get_mmap()[addr:addr + BLOCK_SIZE]
+
+        frame = _make_frame("W", addr, BLOCK_SIZE, data)
+
+        _rawsend(radio, frame)
+
+        # receiving the response
+        ack = _rawrecv(radio, 1)
+        if ack != "\x06":
+            _exit_program_mode(radio)
+            msg = "Bad ack writing block 0x%04x" % addr
+            raise errors.RadioError(msg)
+
+        _check_for_double_ack(radio)
+
+        # UI Update
+        status.cur = addr / BLOCK_SIZE
+        status.msg = "Cloning to radio..."
+        radio.status_fn(status)
+
+    _exit_program_mode(radio)
+
+
+def model_match(cls, data):
+    """Match the opened/downloaded image to the correct version"""
+    rid = data[0x6140:0x6148]
+
+    #if rid in cls._fileid:
+    if rid in cls.IDENT:
+        return True
+
+    return False
+
+
+class VGCStyleRadio(chirp_common.CloneModeRadio,
+                    chirp_common.ExperimentalRadio):
+    """BTECH's UV-50X3"""
+    VENDOR = "BTECH"
+    _air_range = (108000000, 136000000)
+    _vhf_range = (136000000, 174000000)
+    _vhf2_range = (174000000, 250000000)
+    _220_range = (222000000, 225000000)
+    _gen1_range = (300000000, 400000000)
+    _uhf_range = (400000000, 480000000)
+    _gen2_range = (480000000, 520000000)
+    _upper = 499
+    MODEL = ""
+    IDENT = ""
+
+    @classmethod
+    def get_prompts(cls):
+        rp = chirp_common.RadioPrompts()
+        rp.experimental = \
+            ('The UV-50X3 driver is a beta version.\n'
+             '\n'
+             'Please save an unedited copy of your first successful\n'
+             'download to a CHIRP Radio Images(*.img) file.'
+             )
+        rp.pre_download = _(dedent("""\
+            Follow this 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 this 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):
+        rf = chirp_common.RadioFeatures()
+        rf.has_settings = True
+        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.has_sub_devices = self.VARIANT == ""
+        rf.valid_modes = MODES
+        rf.valid_characters = VALID_CHARS
+        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_power_levels = POWER_LEVELS
+        rf.valid_skips = SKIP_VALUES
+        rf.valid_name_length = NAME_LENGTH
+        rf.valid_dtcs_codes = DTCS_CODES
+        rf.valid_bands = [self._air_range,
+                          self._vhf_range,
+                          self._vhf2_range,
+                          self._220_range,
+                          self._gen1_range,
+                          self._uhf_range,
+                          self._gen2_range]
+        rf.memory_bounds = (0, self._upper)
+        return rf
+
+    def get_sub_devices(self):
+        return [UV50X3Left(self._mmap), UV50X3Right(self._mmap)]
+
+    def sync_in(self):
+        """Download from radio"""
+        try:
+            data = _download(self)
+        except errors.RadioError:
+            # Pass through any real errors we raise
+            raise
+        except:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during download')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+        self._mmap = memmap.MemoryMap(data)
+        self.process_mmap()
+
+    def sync_out(self):
+        """Upload to radio"""
+        try:
+            _upload(self)
+        except:
+            # If anything unexpected happens, make sure we raise
+            # a RadioError and log the problem
+            LOG.exception('Unexpected error during upload')
+            raise errors.RadioError('Unexpected error communicating '
+                                    'with the radio')
+
+    def process_mmap(self):
+        """Process the mem map into the mem object"""
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+    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)"""
+        if val.get_raw() == "\xFF\xFF":
+            return '', None, None
+
+        val = int(val)
+        if val >= 12000:
+            a = val - 12000
+            return 'DTCS', a, 'R'
+        elif val >= 8000:
+            a = val - 8000
+            return 'DTCS', a, 'N'
+        else:
+            a = val / 10.0
+            return 'Tone', a, None
+
+    def encode_tone(self, memval, mode, value, pol):
+        """Parse the tone data to encode from UI to mem"""
+        if mode == '':
+            memval[0].set_raw(0xFF)
+            memval[1].set_raw(0xFF)
+        elif mode == 'Tone':
+            memval.set_value(int(value * 10))
+        elif mode == 'DTCS':
+            flag = 0x80 if pol == 'N' else 0xC0
+            memval.set_value(value)
+            memval[1].set_bits(flag)
+        else:
+            raise Exception("Internal error: invalid mode `%s'" % mode)
+
+    def _memory_obj(self, suffix=""):
+        return getattr(self._memobj, "%s_memory%s" % (self._vfo, suffix))
+
+    def _name_obj(self, suffix=""):
+        return getattr(self._memobj, "%s_names%s" % (self._vfo, suffix))
+
+    def _scan_obj(self, suffix=""):
+        return getattr(self._memobj, "%s_scanflags%s" % (self._vfo, suffix))
+
+    def _used_obj(self, suffix=""):
+        return getattr(self._memobj, "%s_usedflags%s" % (self._vfo, suffix))
+
+    def get_memory(self, number):
+        """Get the mem representation from the radio image"""
+        bitpos = (1 << (number % 8))
+        bytepos = (number / 8)
+
+        _mem = self._memory_obj()[number]
+        _names = self._name_obj()[number]
+        _scn = self._scan_obj()[bytepos]
+        _usd = self._used_obj()[bytepos]
+
+        isused = bitpos & int(_usd)
+        isscan = bitpos & int(_scn)
+
+        # Create a high-level memory object to return to the UI
+        mem = chirp_common.Memory()
+
+        # Memory number
+        mem.number = number
+
+        if not isused:
+            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 feq set
+            offset = (int(_mem.txfreq) * 10) - mem.freq
+            if offset < 0:
+                mem.offset = abs(offset)
+                mem.duplex = "-"
+            elif offset > 0:
+                mem.offset = offset
+                mem.duplex = "+"
+            else:
+                mem.offset = 0
+
+        # skip
+        if not isscan:
+            mem.skip = "S"
+
+        # name TAG of the channel
+        mem.name = str(_names.name).strip("\xFF")
+
+        # power
+        mem.power = POWER_LEVELS[int(_mem.txp)]
+
+        # wide/narrow
+        mem.mode = MODES[int(_mem.wn)]
+
+        # 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")
+
+        bcl = RadioSetting("bcl", "Busy channel lockout",
+                              RadioSettingValueBoolean(bool(_mem.bcl)))
+        mem.extra.append(bcl)
+
+        revert = RadioSetting("revert", "Revert",
+                              RadioSettingValueBoolean(bool(_mem.revert)))
+        mem.extra.append(revert)
+
+        dname = RadioSetting("dname", "Display name",
+                             RadioSettingValueBoolean(bool(_mem.dname)))
+        mem.extra.append(dname)
+
+        return mem
+
+    def set_memory(self, mem):
+        """Set the memory data in the eeprom img from the UI"""
+        bitpos = (1 << (mem.number % 8))
+        bytepos = (mem.number / 8)
+
+        _mem = self._memory_obj()[mem.number]
+        _names = self._name_obj()[mem.number]
+        _scn = self._scan_obj()[bytepos]
+        _usd = self._used_obj()[bytepos]
+
+        if mem.empty:
+            _usd &= ~bitpos
+            _scn &= ~bitpos
+            _mem.set_raw("\xFF" * 16)
+            _names.name = ("\xFF" * 6)
+            return
+        else:
+            _usd |= bitpos
+
+        # 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
+        _names.name = mem.name.ljust(6, "\xFF")
+
+        # power level, # default power level is low
+        _mem.txp = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
+
+        # wide/narrow
+        _mem.wn = MODES.index(mem.mode)
+
+        if mem.skip == "S":
+            _scn &= ~bitpos
+        else:
+            _scn |= bitpos
+
+        # autoset display to display name if filled
+        if mem.extra:
+            # mem.extra only seems to be populated when called from edit panel
+            dname = mem.extra["dname"]
+        else:
+            dname = None
+        if mem.name:
+            _mem.dname = True
+            if dname and not dname.changed():
+                dname.value = True
+        else:
+            _mem.dname = False
+            if dname and not dname.changed():
+                dname.value = False
+
+        # reseting unknowns, this has to be set by hand
+        _mem.unknown0 = 0
+        _mem.unknown1 = 0
+        _mem.unknown2 = 0
+        _mem.unknown3 = 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 are no extra settings, load defaults
+            _mem.bcl = 0
+            _mem.revert = 0
+            _mem.dname = 1
+
+    def _bbcd2dtmf(self, bcdarr, strlen=16):
+        # doing bbcd, but with support for ABCD*#
+        LOG.debug(bcdarr.get_value())
+        string = ''.join("%02X" % b for b in bcdarr)
+        LOG.debug("@_bbcd2dtmf, received: %s" % string)
+        string = string.replace('E', '*').replace('F', '#')
+        if strlen <= 16:
+            string = string[:strlen]
+        return string
+
+    def _dtmf2bbcd(self, value):
+        dtmfstr = value.get_value()
+        dtmfstr = dtmfstr.replace('*', 'E').replace('#', 'F')
+        dtmfstr = str.ljust(dtmfstr.strip(), 16, "F")
+        bcdarr = list(bytearray.fromhex(dtmfstr))
+        LOG.debug("@_dtmf2bbcd, sending: %s" % bcdarr)
+        return bcdarr
+
+    def get_settings(self):
+        """Translate the bit in the mem_struct into settings in the UI"""
+        _mem = self._memobj
+        basic = RadioSettingGroup("basic", "Basic Settings")
+        other = RadioSettingGroup("other", "Other Settings")
+        work = RadioSettingGroup("work", "Work Mode Settings")
+        dtmf = RadioSettingGroup("dtmf", "DTMF Settings")
+        top = RadioSettings(basic, other, work, dtmf)
+
+        # Basic
+
+        # Audio: A01-A04
+
+        aftone = RadioSetting("settings.aftone", "AF tone control",
+                              RadioSettingValueList(LIST_AFTONE, LIST_AFTONE[
+                                  _mem.settings.aftone]))
+        basic.append(aftone)
+
+        spkr = RadioSetting("settings.spkr", "Speaker",
+                            RadioSettingValueList(LIST_SPKR,LIST_SPKR[
+                                _mem.settings.spkr]))
+        basic.append(spkr)
+
+        audio = RadioSetting("settings.audio", "Stereo/Mono",
+                             RadioSettingValueList(LIST_AUDIO, LIST_AUDIO[
+                                 _mem.settings.audio]))
+        basic.append(audio)
+
+        sbmute = RadioSetting("settings.sbmute", "Sub band mute",
+                              RadioSettingValueList(LIST_SBMUTE, LIST_SBMUTE[
+                                  _mem.settings.sbmute]))
+        basic.append(sbmute)
+
+        # TX/RX: B01-B08
+
+        mgain = RadioSetting("settings.mgain", "Mic gain",
+                             RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
+                                 _mem.settings.mgain]))
+        basic.append(mgain)
+
+        ptt = RadioSetting("settings.ptt", "PTT mode",
+                           RadioSettingValueList(LIST_PTT,LIST_PTT[
+                               _mem.settings.ptt]))
+        basic.append(ptt)
+
+        # B03 (per channel)
+        # B04 (per channel)
+
+        rxexp = RadioSetting("settings.rxexp", "RX expansion",
+                             RadioSettingValueList(LIST_RXEXP,LIST_RXEXP[
+                                 _mem.settings.rxexp]))
+        basic.append(rxexp)
+
+        vox = RadioSetting("settings.vox", "Vox",
+                           RadioSettingValueList(LIST_VOX, LIST_VOX[
+                               _mem.settings.vox]))
+        basic.append(vox)
+
+        voxs = RadioSetting("settings.voxs", "Vox sensitivity",
+                            RadioSettingValueList(LIST_MLNHM, LIST_MLNHM[
+                                _mem.settings.voxs]))
+        basic.append(voxs)
+
+        # B08 (per channel)
+
+        # Display: C01-C06
+
+        display = RadioSetting("settings.display", "Display select",
+                               RadioSettingValueList(LIST_DISPLAY,
+                                   LIST_DISPLAY[_mem.settings.display]))
+        basic.append(display)
+
+        lcdb = RadioSetting("settings.lcdb", "LCD brightness",
+                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
+                                _mem.settings.lcdb]))
+        basic.append(lcdb)
+
+        color = RadioSetting("settings.color", "LCD color",
+                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
+                                 _mem.settings.color]))
+        basic.append(color)
+
+        lcdc = RadioSetting("settings.lcdc", "LCD contrast",
+                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
+                                _mem.settings.lcdc]))
+        basic.append(lcdc)
+
+        btime = RadioSetting("settings.btime", "LCD backlight time",
+                             RadioSettingValueList(LIST_BTIME, LIST_BTIME[
+                                 _mem.settings.btime]))
+        basic.append(btime)
+
+        keyb = RadioSetting("settings.keyb", "Key brightness",
+                            RadioSettingValueList(LIST_MINMAX, LIST_MINMAX[
+                                _mem.settings.keyb]))
+        basic.append(keyb)
+
+        # Memory: D01-D04
+
+        # D01 (per channel)
+        # D02 (per channel)
+
+        mrscan = RadioSetting("settings.mrscan", "Memory scan type",
+                              RadioSettingValueList(LIST_MRSCAN, LIST_MRSCAN[
+                                  _mem.settings.mrscan]))
+        basic.append(mrscan)
+
+        # D04 (per channel)
+
+        # Scan: E01-E04
+
+        dwstop = RadioSetting("settings.dwstop", "Dual watch stop",
+                              RadioSettingValueList(LIST_DWSTOP, LIST_DWSTOP[
+                                  _mem.settings.dwstop]))
+        basic.append(dwstop)
+
+        scand = RadioSetting("settings.scand", "Scan direction",
+                             RadioSettingValueList(LIST_SCAND,LIST_SCAND[
+                                 _mem.settings.scand]))
+        basic.append(scand)
+
+        scanr = RadioSetting("settings.scanr", "Scan resume",
+                             RadioSettingValueList(LIST_SCANR,LIST_SCANR[
+                                 _mem.settings.scanr]))
+        basic.append(scanr)
+
+        scansb = RadioSetting("settings.scansb", "Scan stop beep",
+                              RadioSettingValueBoolean(_mem.settings.scansb))
+        basic.append(scansb)
+
+        # System: F01-F09
+
+        apo = RadioSetting("settings.apo", "Automatic power off [hours]",
+                           RadioSettingValueList(LIST_APO, LIST_APO[
+                               _mem.settings.apo]))
+        basic.append(apo)
+
+        ars = RadioSetting("settings.ars", "Automatic repeater shift",
+                           RadioSettingValueBoolean(_mem.settings.ars))
+        basic.append(ars)
+
+        beep = RadioSetting("settings.beep", "Beep volume",
+                            RadioSettingValueList(LIST_BEEP,LIST_BEEP[
+                                _mem.settings.beep]))
+        basic.append(beep)
+
+        fkey = RadioSetting("settings.fkey", "F key",
+                            RadioSettingValueList(LIST_FKEY,LIST_FKEY[
+                                _mem.settings.fkey]))
+        basic.append(fkey)
+
+        pfkey1 = RadioSetting("settings.pfkey1", "Mic P1 key",
+                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
+                                  _mem.settings.pfkey1]))
+        basic.append(pfkey1)
+
+        pfkey2 = RadioSetting("settings.pfkey2", "Mic P2 key",
+                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
+                                  _mem.settings.pfkey2]))
+        basic.append(pfkey2)
+
+        pfkey3 = RadioSetting("settings.pfkey3", "Mic P3 key",
+                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
+                                  _mem.settings.pfkey3]))
+        basic.append(pfkey3)
+
+        pfkey4 = RadioSetting("settings.pfkey4", "Mic P4 key",
+                              RadioSettingValueList(LIST_PFKEY, LIST_PFKEY[
+                                  _mem.settings.pfkey4]))
+        basic.append(pfkey4)
+
+        omode = RadioSetting("settings.omode", "Operation mode",
+                             RadioSettingValueList(LIST_AB,LIST_AB[
+                                 _mem.settings.omode]))
+        basic.append(omode)
+
+        rxcoverm = RadioSetting("settings.rxcoverm", "RX coverage - memory",
+                                RadioSettingValueList(LIST_COVERAGE, 
+                                    LIST_COVERAGE[_mem.settings.rxcoverm]))
+        basic.append(rxcoverm)
+
+        rxcoverv = RadioSetting("settings.rxcoverv", "RX coverage - VFO",
+                                RadioSettingValueList(LIST_COVERAGE, 
+                                    LIST_COVERAGE[_mem.settings.rxcoverv]))
+        basic.append(rxcoverv)
+
+        tot = RadioSetting("settings.tot", "Time out timer [min]",
+                           RadioSettingValueList(LIST_TOT, LIST_TOT[
+                               _mem.settings.tot]))
+        basic.append(tot)
+
+        # Timer/Clock: G01-G04
+
+        # G01
+        datefmt = RadioSetting("settings.datefmt", "Date format",
+                               RadioSettingValueList(LIST_DATEFMT,
+                                   LIST_DATEFMT[_mem.settings.datefmt]))
+        basic.append(datefmt)
+
+        timefmt = RadioSetting("settings.timefmt", "Time format",
+                               RadioSettingValueList(LIST_TIMEFMT,
+                                   LIST_TIMEFMT[_mem.settings.timefmt]))
+        basic.append(timefmt)
+
+        timesig = RadioSetting("settings.timesig", "Time signal",
+                               RadioSettingValueBoolean(_mem.settings.timesig))
+        basic.append(timesig)
+
+        tz = RadioSetting("settings.tz", "Time zone",
+                          RadioSettingValueList(LIST_TZ, LIST_TZ[
+                              _mem.settings.tz]))
+        basic.append(tz)
+
+        # Signaling: H01-H06
+
+        bell = RadioSetting("settings.bell", "Bell ringer",
+                            RadioSettingValueList(LIST_BELL, LIST_BELL[
+                                _mem.settings.bell]))
+        basic.append(bell)
+
+        # H02 (per channel)
+
+        dtmfmodenc = RadioSetting("settings.dtmfmodenc", "DTMF mode encode",
+                                  RadioSettingValueBoolean(
+                                      _mem.settings.dtmfmodenc))
+        basic.append(dtmfmodenc)
+
+        dtmfmoddec = RadioSetting("settings.dtmfmoddec", "DTMF mode decode",
+                                  RadioSettingValueBoolean(
+                                      _mem.settings.dtmfmoddec))
+        basic.append(dtmfmoddec)
+
+        # H04 (per channel)
+
+        decbandsel = RadioSetting("settings.decbandsel", "DTMF band select",
+                                  RadioSettingValueList(LIST_AB,LIST_AB[
+                                      _mem.settings.decbandsel]))
+        basic.append(decbandsel)
+
+        sqlexp = RadioSetting("settings.sqlexp", "SQL expansion",
+                              RadioSettingValueBoolean(_mem.settings.sqlexp))
+        basic.append(sqlexp)
+
+        # Pkt: I01-I03
+
+        databnd = RadioSetting("settings.databnd", "Packet data band",
+                               RadioSettingValueList(LIST_DATABND,LIST_DATABND[
+                                   _mem.settings.databnd]))
+        basic.append(databnd)
+
+        dataspd = RadioSetting("settings.dataspd", "Packet data speed",
+                               RadioSettingValueList(LIST_DATASPD,LIST_DATASPD[
+                                   _mem.settings.dataspd]))
+        basic.append(dataspd)
+
+        datasql = RadioSetting("settings.datasql", "Packet data squelch",
+                               RadioSettingValueList(LIST_DATASQL,LIST_DATASQL[
+                                   _mem.settings.datasql]))
+        basic.append(datasql)
+
+        # Other
+
+        dw = RadioSetting("settings.dw", "Dual watch",
+                          RadioSettingValueBoolean(_mem.settings.dw))
+        other.append(dw)
+
+        cpuclk = RadioSetting("settings.cpuclk", "CPU clock frequency",
+                              RadioSettingValueList(LIST_CPUCLK,LIST_CPUCLK[
+                                  _mem.settings.cpuclk]))
+        other.append(cpuclk)
+
+        def _filter(name):
+            filtered = ""
+            for char in str(name):
+                if char in VALID_CHARS:
+                    filtered += char
+                else:
+                    filtered += " "
+            return filtered
+
+        line16 = RadioSetting("poweron_msg.line16", "Power-on message",
+                              RadioSettingValueString(0, 16, _filter(
+                                  _mem.poweron_msg.line16)))
+        other.append(line16)
+
+        line32 = RadioSetting("embedded_msg.line32", "Embedded message",
+                              RadioSettingValueString(0, 32, _filter(
+                                  _mem.embedded_msg.line32)))
+        other.append(line32)
+
+        # Work
+
+        workmoda = RadioSetting("settings.workmoda", "Work mode A",
+                                RadioSettingValueList(LIST_WORK,LIST_WORK[
+                                    _mem.settings.workmoda]))
+        work.append(workmoda)
+
+        workmodb = RadioSetting("settings.workmodb", "Work mode B",
+                                RadioSettingValueList(LIST_WORK,LIST_WORK[
+                                    _mem.settings.workmodb]))
+        work.append(workmodb)
+
+        wbanda = RadioSetting("settings.wbanda", "Work band A",
+                              RadioSettingValueList(LIST_WBANDA, LIST_WBANDA[
+                                  (_mem.settings.wbanda) - 1]))
+        work.append(wbanda)
+
+        wbandb = RadioSetting("settings.wbandb", "Work band B",
+                              RadioSettingValueList(LIST_WBANDB, LIST_WBANDB[
+                                  (_mem.settings.wbandb) - 4]))
+        work.append(wbandb)
+
+        sqla = RadioSetting("settings.sqla", "Squelch A",
+                            RadioSettingValueList(LIST_SQL, LIST_SQL[
+                                _mem.settings.sqla]))
+        work.append(sqla)
+
+        sqlb = RadioSetting("settings.sqlb", "Squelch B",
+                            RadioSettingValueList(LIST_SQL, LIST_SQL[
+                                _mem.settings.sqlb]))
+        work.append(sqlb)
+
+        stepa = RadioSetting("settings.stepa", "Auto step A",
+                             RadioSettingValueList(LIST_STEP,LIST_STEP[
+                                 _mem.settings.stepa]))
+        work.append(stepa)
+
+        stepb = RadioSetting("settings.stepb", "Auto step B",
+                             RadioSettingValueList(LIST_STEP,LIST_STEP[
+                                 _mem.settings.stepb]))
+        work.append(stepb)
+
+        mrcha = RadioSetting("settings.mrcha", "Current channel A",
+                             RadioSettingValueInteger(0, 499,
+                                 _mem.settings.mrcha))
+        work.append(mrcha)
+
+        mrchb = RadioSetting("settings.mrchb", "Current channel B",
+                             RadioSettingValueInteger(0, 499,
+                                 _mem.settings.mrchb))
+        work.append(mrchb)
+
+        val = _mem.settings.offseta / 100.00
+        offseta = RadioSetting("settings.offseta", "Offset A (0-37.95)",
+                               RadioSettingValueFloat(0, 38.00, val, 0.05, 2))
+        work.append(offseta)
+
+        val = _mem.settings.offsetb / 100.00
+        offsetb = RadioSetting("settings.offsetb", "Offset B (0-79.95)",
+                               RadioSettingValueFloat(0, 80.00, val, 0.05, 2))
+        work.append(offsetb)
+
+        wpricha = RadioSetting("settings.wpricha", "Priority channel A",
+                               RadioSettingValueInteger(0, 499,
+                                   _mem.settings.wpricha))
+        work.append(wpricha)
+
+        wprichb = RadioSetting("settings.wprichb", "Priority channel B",
+                               RadioSettingValueInteger(0, 499,
+                                   _mem.settings.wprichb))
+        work.append(wprichb)
+
+        smode = RadioSetting("settings.smode", "Smart function mode",
+                             RadioSettingValueList(LIST_SMODE,LIST_SMODE[
+                                 _mem.settings.smode]))
+        work.append(smode)
+
+        # dtmf
+
+        ttdkey = RadioSetting("dtmf.ttdkey", "D key function",
+                              RadioSettingValueList(LIST_TTDKEY, LIST_TTDKEY[
+                                  _mem.dtmf.ttdkey]))
+        dtmf.append(ttdkey)
+
+        ttdgt = RadioSetting("dtmf.ttdgt", "Digit time",
+                              RadioSettingValueList(LIST_TT200, LIST_TT200[
+                                  (_mem.dtmf.ttdgt) - 5]))
+        dtmf.append(ttdgt)
+
+        ttint = RadioSetting("dtmf.ttint", "Interval time",
+                              RadioSettingValueList(LIST_TT200, LIST_TT200[
+                                  (_mem.dtmf.ttint) - 5]))
+        dtmf.append(ttint)
+
+        tt1stdgt = RadioSetting("dtmf.tt1stdgt", "1st digit time",
+                                RadioSettingValueList(LIST_TT200, LIST_TT200[
+                                    (_mem.dtmf.tt1stdgt) - 5]))
+        dtmf.append(tt1stdgt)
+
+        tt1stdly = RadioSetting("dtmf.tt1stdly", "1st digit delay time",
+                                RadioSettingValueList(LIST_TT1000, LIST_TT1000[
+                                    (_mem.dtmf.tt1stdly) - 2]))
+        dtmf.append(tt1stdly)
+
+        ttdlyqt = RadioSetting("dtmf.ttdlyqt", "Digit delay when use qt",
+                               RadioSettingValueList(LIST_TT1000, LIST_TT1000[
+                                   (_mem.dtmf.ttdlyqt) - 2]))
+        dtmf.append(ttdlyqt)
+
+        ttsig = RadioSetting("dtmf2.ttsig", "Signal",
+                             RadioSettingValueList(LIST_TTSIG, LIST_TTSIG[
+                                 _mem.dtmf2.ttsig]))
+        dtmf.append(ttsig)
+
+        ttautorst = RadioSetting("dtmf2.ttautorst", "Auto reset time",
+                                 RadioSettingValueList(LIST_TTAUTORST,
+                                     LIST_TTAUTORST[_mem.dtmf2.ttautorst]))
+        dtmf.append(ttautorst)
+
+        if _mem.dtmf2.ttgrpcode > 0x06:
+            val = 0x00
+        else:
+            val = _mem.dtmf2.ttgrpcode
+        ttgrpcode = RadioSetting("dtmf2.ttgrpcode", "Group code",
+                                 RadioSettingValueList(LIST_TTGRPCODE,
+                                     LIST_TTGRPCODE[val]))
+        dtmf.append(ttgrpcode)
+
+        ttintcode = RadioSetting("dtmf2.ttintcode", "Interval code",
+                                 RadioSettingValueList(LIST_TTINTCODE,
+                                     LIST_TTINTCODE[_mem.dtmf2.ttintcode]))
+        dtmf.append(ttintcode)
+
+        if _mem.dtmf2.ttalert > 0x04:
+            val = 0x00
+        else:
+            val = _mem.dtmf2.ttalert
+        ttalert = RadioSetting("dtmf2.ttalert", "Alert tone/transpond",
+                               RadioSettingValueList(LIST_TTALERT,
+                                   LIST_TTALERT[val]))
+        dtmf.append(ttalert)
+
+        ttautod = RadioSetting("dtmf.ttautod", "Auto dial group",
+                               RadioSettingValueList(LIST_TTAUTOD,
+                                   LIST_TTAUTOD[_mem.dtmf.ttautod]))
+        dtmf.append(ttautod)
+
+        # setup 9 dtmf autodial entries
+        for i in map(str, range(1, 10)):
+            objname = "code" + i
+            strname = "Code " + str(i)
+            dtmfsetting = getattr(_mem.dtmfcode, objname)
+            dtmflen = getattr(_mem.dtmfcode, objname + "_len")
+            dtmfstr = self._bbcd2dtmf(dtmfsetting, dtmflen)
+            code = RadioSettingValueString(0, 16, dtmfstr)
+            code.set_charset(DTMF_CHARS + list(" "))
+            rs = RadioSetting("dtmfcode." + objname, strname, code)
+            dtmf.append(rs)
+        return top
+
+    def set_settings(self, settings):
+        _settings = self._memobj.settings
+        _mem = self._memobj
+        for element in settings:
+            if not isinstance(element, RadioSetting):
+                self.set_settings(element)
+                continue
+            else:
+                try:
+                    name = element.get_name()
+                    if "." in name:
+                        bits = name.split(".")
+                        obj = self._memobj
+                        for bit in bits[:-1]:
+                            if "/" in bit:
+                                bit, index = bit.split("/", 1)
+                                index = int(index)
+                                obj = getattr(obj, bit)[index]
+                            else:
+                                obj = getattr(obj, bit)
+                        setting = bits[-1]
+                    else:
+                        obj = _settings
+                        setting = element.get_name()
+
+                    if element.has_apply_callback():
+                        LOG.debug("Using apply callback")
+                        element.run_apply_callback()
+                    elif setting == "line16":
+                        setattr(obj, setting, str(element.value).rstrip(
+                            " ").ljust(16, "\xFF"))
+                    elif setting == "line32":
+                        setattr(obj, setting, str(element.value).rstrip(
+                            " ").ljust(32, "\xFF"))
+                    elif setting == "wbanda":
+                        setattr(obj, setting, int(element.value) + 1)
+                    elif setting == "wbandb":
+                        setattr(obj, setting, int(element.value) + 4)
+                    elif setting in ["offseta", "offsetb"]:
+                        val = element.value
+                        value = int(val.get_value() * 100)
+                        setattr(obj, setting, value)
+                    elif setting in ["ttdgt", "ttint", "tt1stdgt"]:
+                        setattr(obj, setting, int(element.value) + 5)
+                    elif setting in ["tt1stdly", "ttdlyqt"]:
+                        setattr(obj, setting, int(element.value) + 2)
+                    elif re.match('code\d', setting):
+                        # set dtmf length field and then get bcd dtmf
+                        dtmfstrlen = len(str(element.value).strip())
+                        setattr(_mem.dtmfcode, setting + "_len", dtmfstrlen)
+                        dtmfstr = self._dtmf2bbcd(element.value)
+                        setattr(_mem.dtmfcode, setting, dtmfstr)
+                    elif element.value.get_mutable():
+                        LOG.debug("Setting %s = %s" % (setting, element.value))
+                        setattr(obj, setting, element.value)
+                except Exception, e:
+                    LOG.debug(element.get_name())
+                    raise
+
+
+    @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 UV50X3(VGCStyleRadio):
+    """BTech UV-50X3"""
+    MODEL = "UV-50X3"
+    IDENT = UV50X3_id
+
+
+class UV50X3Left(UV50X3):
+    VARIANT = "Left"
+    _vfo = "left"
+
+
+class UV50X3Right(UV50X3):
+    VARIANT = "Right"
+    _vfo = "right"
diff --git a/chirp/platform.py b/chirp/platform.py
index 3b18d41..0b9696f 100644
--- a/chirp/platform.py
+++ b/chirp/platform.py
@@ -303,6 +303,8 @@ class UnixPlatform(Platform):
                  "/dev/ttyUSB*",
                  "/dev/ttyAMA*",
                  "/dev/cu.*",
+                 "/dev/cuaU*",
+                 "/dev/cua0*",
                  "/dev/term/*",
                  "/dev/tty.KeySerial*"]
         return natural_sorted(sum([glob.glob(x) for x in ports], []))
diff --git a/chirp/settings.py b/chirp/settings.py
index 55571a3..b03cd4f 100644
--- a/chirp/settings.py
+++ b/chirp/settings.py
@@ -206,6 +206,63 @@ class RadioSettingValueString(RadioSettingValue):
         return self._current
 
 
+class RadioSettingValueMap(RadioSettingValueList):
+    """Map User Options to Radio Memory Values
+
+    Provides User Option list for GUI, maintains state, verifies new values,
+    and allows {setting,getting} by User Option OR Memory Value.  External
+    conversions not needed.
+
+    """
+    def __init__(self, map_entries, mem_val=None, user_option=None):
+        """Create new map
+
+        Pass in list of 2 member tuples, typically of type (str, int),
+        for each Radio Setting.  First member of each tuple is the
+        User Option Name, second is the Memory Value that corresponds.
+        An example is APO: ("Off", 0), ("0.5", 5), ("1.0", 10).
+
+        """
+        # Catch bugs early by testing tuple geometry
+        for map_entry in map_entries:
+            if not len(map_entry) == 2:
+                raise InvalidValueError("map_entries must be 2 el tuples "
+                                        "instead of: %s" % str(map_entry))
+        user_options = [e[0] for e in map_entries]
+        self._mem_vals = [e[1] for e in map_entries]
+        RadioSettingValueList.__init__(self, user_options, user_options[0])
+        if mem_val is not None:
+            self.set_mem_val(mem_val)
+        elif user_option is not None:
+            self.set_value(user_option)
+
+    def set_mem_val(self, mem_val):
+        """Change setting to User Option that corresponds to 'mem_val'"""
+        if mem_val in self._mem_vals:
+            index = self._mem_vals.index(mem_val)
+            self.set_value(self._options[index])
+        else:
+            raise InvalidValueError("%s is not valid for this setting" % value)
+
+    def __trunc__(self):
+        """Return memory value that matches current user option"""
+        index = self._options.index(self._current)
+        value = self._mem_vals[index]
+        return value
+
+
+def zero_indexed_seq_map(user_options):
+    """RadioSettingValueMap factory method
+
+    Radio Setting Maps commonly use a list of strings that map to a sequence
+    that starts with 0.  Pass in a list of User Options and this function
+    returns a list of tuples of form (str, int).
+
+    """
+    mem_vals = range(0, len(user_options))
+    return zip(user_options, mem_vals)
+
+
 class RadioSettings(list):
     def __init__(self, *groups):
         list.__init__(self, groups)
diff --git a/chirp/ui/clone.py b/chirp/ui/clone.py
index f20c1d2..c5bf888 100644
--- a/chirp/ui/clone.py
+++ b/chirp/ui/clone.py
@@ -13,6 +13,7 @@
 # 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 collections
 import threading
 import logging
 import os
@@ -72,16 +73,15 @@ class CloneSettingsDialog(gtk.Dialog):
         return miscwidgets.make_choice([], False)
 
     def __make_vendor(self, model):
-        vendors = {}
+        vendors = collections.defaultdict(list)
         for rclass in sorted(directory.DRV_TO_RADIO.values()):
             if not issubclass(rclass, chirp_common.CloneModeRadio) and \
                     not issubclass(rclass, chirp_common.LiveRadio):
                 continue
 
-            if rclass.VENDOR not in vendors:
-                vendors[rclass.VENDOR] = []
-
             vendors[rclass.VENDOR].append(rclass)
+            for alias in rclass.ALIASES:
+                vendors[alias.VENDOR].append(alias)
 
         self.__vendors = vendors
 
@@ -187,6 +187,17 @@ class CloneSettingsDialog(gtk.Dialog):
                 if rclass.MODEL == model:
                     cs.radio_class = rclass
                     break
+                alias_match = None
+                for alias in rclass.ALIASES:
+                    if alias.MODEL == model:
+                        alias_match = rclass
+                        break
+                if alias_match:
+                    cs.radio_class = rclass
+                    LOG.debug(
+                        'Chose %s alias for %s because model %s selected' % (
+                            alias_match, cs.radio_class, model))
+                    break
             if not cs.radio_class:
                 common.show_error(
                     _("Internal error: Unable to upload to {model}").format(
diff --git a/chirp/ui/mainapp.py b/chirp/ui/mainapp.py
index 6f850ba..b3ca4ea 100644
--- a/chirp/ui/mainapp.py
+++ b/chirp/ui/mainapp.py
@@ -14,6 +14,7 @@
 # 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 datetime import datetime
 import os
 import tempfile
 import urllib
@@ -24,6 +25,7 @@ import time
 import logging
 import gtk
 import gobject
+import sys
 
 from chirp.ui import inputdialog, common
 from chirp import platform, directory, util
@@ -39,7 +41,6 @@ gobject.threads_init()
 LOG = logging.getLogger(__name__)
 
 if __name__ == "__main__":
-    import sys
     sys.path.insert(0, "..")
 
 try:
@@ -433,6 +434,14 @@ of file.
             vendor=eset.radio.VENDOR,
             model=eset.radio.MODEL)
 
+        defname_format = CONF.get("default_filename", "global") or \
+            "{vendor}_{model}_{date}"
+        defname = defname_format.format(
+            vendor=eset.radio.VENDOR,
+            model=eset.radio.MODEL,
+            date=datetime.now().strftime('%Y%m%d')
+        ).replace('/', '_')
+
         types = [(label + " (*.%s)" % eset.radio.FILE_EXTENSION,
                  eset.radio.FILE_EXTENSION)]
 
@@ -445,7 +454,8 @@ of file.
             types += [(_("VX5 Commander") + " (*.vx5)", "vx5")]
 
         while True:
-            fname = platform.get_platform().gui_save_file(types=types)
+            fname = platform.get_platform().gui_save_file(default_name=defname,
+                                                          types=types)
             if not fname:
                 return
 
@@ -922,6 +932,16 @@ of file.
             VENDOR = "RepeaterBook"
             MODEL = ""
 
+            def _clean_comment(self, headers, line, mem):
+                "Converts iso-8859-1 encoded comments to unicode for pyGTK."
+                mem.comment = unicode(mem.comment, 'iso-8859-1')
+                return mem
+
+            def _clean_name(self, headers, line, mem):
+                "Converts iso-8859-1 encoded names to unicode for pyGTK."
+                mem.name = unicode(mem.name, 'iso-8859-1')
+                return mem
+
         try:
             # Validate CSV
             radio = RBRadio(filename)
@@ -1164,7 +1184,6 @@ of file.
 
     def do_export(self):
         types = [(_("CSV Files") + " (*.csv)", "csv"),
-                 (_("CHIRP Files") + " (*.chirp)", "chirp"),
                  ]
 
         eset = self.get_current_editorset()
@@ -1539,6 +1558,11 @@ of file.
   </menubar>
 </ui>
 """
+        ALT_KEY = "<Alt>"
+        CTRL_KEY = "<Ctrl>"
+        if sys.platform == 'darwin':
+            ALT_KEY = "<Meta>"
+            CTRL_KEY = "<Meta>"
         actions = [
             ('file', None, _("_File"), None, None, self.mh),
             ('new', gtk.STOCK_NEW, None, None, None, self.mh),
@@ -1551,34 +1575,36 @@ of file.
             ('close', gtk.STOCK_CLOSE, None, None, None, self.mh),
             ('quit', gtk.STOCK_QUIT, None, None, None, self.mh),
             ('edit', None, _("_Edit"), None, None, self.mh),
-            ('cut', None, _("_Cut"), "<Ctrl>x", None, self.mh),
-            ('copy', None, _("_Copy"), "<Ctrl>c", None, self.mh),
-            ('paste', None, _("_Paste"), "<Ctrl>v", None, self.mh),
+            ('cut', None, _("_Cut"), "%sx" % CTRL_KEY, None, self.mh),
+            ('copy', None, _("_Copy"), "%sc" % CTRL_KEY, None, self.mh),
+            ('paste', None, _("_Paste"),
+             "%sv" % CTRL_KEY, None, self.mh),
             ('delete', None, _("_Delete"), "Delete", None, self.mh),
             ('all', None, _("Select _All"), None, None, self.mh),
             ('move_up', None, _("Move _Up"),
-             "<Control>Up", None, self.mh),
+             "%sUp" % CTRL_KEY, None, self.mh),
             ('move_dn', None, _("Move Dow_n"),
-             "<Control>Down", None, self.mh),
+             "%sDown" % CTRL_KEY, None, self.mh),
             ('exchange', None, _("E_xchange"),
-             "<Control><Shift>x", None, self.mh),
+             "%s<Shift>x" % CTRL_KEY, None, self.mh),
             ('properties', None, _("P_roperties"), None, None, self.mh),
             ('view', None, _("_View"), None, None, self.mh),
             ('columns', None, _("Columns"), None, None, self.mh),
             ('viewdeveloper', None, _("Developer"), None, None, self.mh),
             ('devshowraw', None, _('Show raw memory'),
-             "<Control><Shift>r", None, self.mh),
+             "%s<Shift>r" % CTRL_KEY, None, self.mh),
             ('devdiffraw', None, _("Diff raw memories"),
-             "<Control><Shift>d", None, self.mh),
+             "%s<Shift>d" % CTRL_KEY, None, self.mh),
             ('devdifftab', None, _("Diff tabs"),
-             "<Control><Shift>t", None, self.mh),
+             "%s<Shift>t" % CTRL_KEY, None, self.mh),
             ('language', None, _("Change language"), None, None, self.mh),
             ('radio', None, _("_Radio"), None, None, self.mh),
             ('download', None, _("Download From Radio"),
-             "<Alt>d", None, self.mh),
-            ('upload', None, _("Upload To Radio"), "<Alt>u", None, self.mh),
-            ('import', None, _("Import"), "<Alt>i", None, self.mh),
-            ('export', None, _("Export"), "<Alt>x", None, self.mh),
+             "%sd" % ALT_KEY, None, self.mh),
+            ('upload', None, _("Upload To Radio"),
+             "%su" % ALT_KEY, None, self.mh),
+            ('import', None, _("Import"), "%si" % ALT_KEY, None, self.mh),
+            ('export', None, _("Export"), "%se" % ALT_KEY, None, self.mh),
             ('importsrc', None, _("Import from data source"),
              None, None, self.mh),
             ('iradioreference', None, _("RadioReference.com"),
diff --git a/chirp/ui/memedit.py b/chirp/ui/memedit.py
index c3d33af..8a2b51e 100644
--- a/chirp/ui/memedit.py
+++ b/chirp/ui/memedit.py
@@ -1181,7 +1181,7 @@ class MemoryEditor(common.Editor):
         lostart = self._config.is_defined(lokey) and \
             self._config.get_int(lokey) or min
         histart = self._config.is_defined(hikey) and \
-            self._config.get_int(hikey) or 25
+            self._config.get_int(hikey) or 999
 
         self.lo_limit_adj = gtk.Adjustment(lostart, min, max-1, 1, 10)
         lo = gtk.SpinButton(self.lo_limit_adj)
@@ -1335,7 +1335,7 @@ class MemoryEditor(common.Editor):
         self.count = 100
         self.show_special = self._config.get_bool("show_special")
         self.show_empty = not self._config.get_bool("hide_empty")
-        self.hide_unused = self._config.get_bool("hide_unused")
+        self.hide_unused = self._config.get_bool("hide_unused", default=True)
         self.read_only = False
 
         self.need_refresh = False
@@ -1421,8 +1421,9 @@ class MemoryEditor(common.Editor):
                 self._set_memory(iter, mem)
 
         result = pickle.dumps((self._features, selection))
-        clipboard = gtk.Clipboard(selection="PRIMARY")
+        clipboard = gtk.Clipboard(selection="CLIPBOARD")
         clipboard.set_text(result)
+        clipboard.store()
 
         return cut  # Only changed if we did a cut
 
@@ -1515,7 +1516,7 @@ class MemoryEditor(common.Editor):
             self.rthread.submit(job)
 
     def paste_selection(self):
-        clipboard = gtk.Clipboard(selection="PRIMARY")
+        clipboard = gtk.Clipboard(selection="CLIPBOARD")
         clipboard.request_text(self._paste_selection)
 
     def select_all(self):
diff --git a/chirp/ui/shiftdialog.py b/chirp/ui/shiftdialog.py
index e971611..b975dbf 100644
--- a/chirp/ui/shiftdialog.py
+++ b/chirp/ui/shiftdialog.py
@@ -47,8 +47,6 @@ class ShiftDialog(gtk.Dialog):
 
         self.thread = None
 
-        self.set_response_sensitive(gtk.RESPONSE_OK, False)
-
     def _status(self, msg, prog):
         self.__labl.set_text(msg)
         self.__prog.set_fraction(prog)

-- 
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