[hamradio-commits] [chirp] 01/03: New upstream version 20161123

Iain R. Learmonth irl at moszumanska.debian.org
Sat Dec 3 15:47:11 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 b048c8e58e1d3c879a022b081c017aa0bc1d5f47
Author: Iain R. Learmonth <irl at fsfe.org>
Date:   Sat Dec 3 15:42:16 2016 +0000

    New upstream version 20161123
---
 PKG-INFO                                  |   2 +-
 chirp/__init__.py                         |   2 +-
 chirp/drivers/alinco.py                   | 211 +++++++
 chirp/drivers/btech.py                    | 916 ++++++++++++++++++++++++++++++
 chirp/drivers/ft2900.py                   |  37 +-
 chirp/drivers/ft60.py                     |  64 ++-
 chirp/drivers/ft817.py                    |   9 +-
 chirp/drivers/kguv8d.py                   |  48 +-
 chirp/drivers/kyd.py                      |  67 ++-
 chirp/drivers/leixen.py                   |  88 ++-
 chirp/drivers/{kyd.py => retevis_rt22.py} | 269 ++++++---
 chirp/drivers/thd72.py                    |   6 +-
 chirp/drivers/uv5r.py                     |  30 +-
 chirp/drivers/uv5x3.py                    |  29 +
 chirp/settings.py                         |  15 +-
 chirp/ui/mainapp.py                       |   3 +-
 chirp/ui/settingsedit.py                  |  10 +-
 17 files changed, 1615 insertions(+), 191 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index cfa0b9c..f336a43 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: chirp
-Version: daily-20161102
+Version: daily-20161123
 Summary: UNKNOWN
 Home-page: UNKNOWN
 Author: UNKNOWN
diff --git a/chirp/__init__.py b/chirp/__init__.py
index 9cd6026..33863f9 100644
--- a/chirp/__init__.py
+++ b/chirp/__init__.py
@@ -17,7 +17,7 @@ import os
 import sys
 from glob import glob
 
-CHIRP_VERSION="daily-20161102"
+CHIRP_VERSION="daily-20161123"
 
 module_dir = os.path.dirname(sys.modules["chirp"].__file__)
 __all__ = []
diff --git a/chirp/drivers/alinco.py b/chirp/drivers/alinco.py
index 5662aaa..65ff70f 100644
--- a/chirp/drivers/alinco.py
+++ b/chirp/drivers/alinco.py
@@ -1,4 +1,5 @@
 # Copyright 2011 Dan Smith <dsmith at danplanet.com>
+#           2016 Matt Weyland <lt-betrieb at hb9uf.ch>
 #
 # 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
@@ -566,3 +567,213 @@ class DJ175Radio(DRx35Radio):
             raise Exception("Radio returned less than 16 bytes")
 
         return data
+
+
+DJG7EG_MEM_FORMAT = """
+#seekto 0x200;
+ul16 bank[50];
+ul16 special_bank[7];
+#seekto 0x1200;
+struct {
+    u8   unknown;
+    ul32 freq;
+    u8   mode;
+    u8   step;
+    ul32 offset;
+    u8   duplex;
+    u8   squelch_type;
+    u8   tx_tone;
+    u8   rx_tone;
+    u8   dcs;
+#seek 3;
+    u8   skip;
+#seek 12;
+    char name[32];
+} memory[1000];
+"""
+
+ at directory.register
+class AlincoDJG7EG(AlincoStyleRadio):
+    """Alinco DJ-G7EG"""
+    VENDOR = "Alinco"
+    MODEL = "DJ-G7EG"
+    BAUD_RATE = 57600
+
+    # Those are different from the other Alinco radios.
+    STEPS = [5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0, 100.0, 125.0, 150.0, 200.0, 500.0, 1000.0]
+    DUPLEX = ["", "+", "-"]
+    MODES = ["NFM", "FM", "AM", "WFM"]
+    TMODES = ["", "??1", "Tone", "TSQL", "TSQL-R", "DTCS"]
+
+    _model = "AL~DJ-G7EG" # This is a bit of a hack to avoid overwriting _identify()
+    _memsize = 0x1a7c0
+    _range = [(500000, 1300000000)]
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_dtcs_polarity = False
+        rf.has_bank = False
+        rf.has_settings =  False
+
+        rf.valid_modes = self.MODES
+        rf.valid_tmodes = ["", "Tone", "TSQL", "Cross", "TSQL-R", "DTCS"]
+        rf.valid_tuning_steps = self.STEPS
+        rf.valid_bands = self._range
+        rf.valid_skips = ["", "S"]
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_name_length = 16
+        rf.memory_bounds = (0, 999)
+
+        return rf
+
+    def _download_chunk(self, addr):
+        if addr % 0x40:
+            raise Exception("Addr 0x%04x not on 64-byte boundary" % addr)
+
+        cmd = "AL~F%05XR\r" % addr
+        self._send(cmd)
+
+        # Response: "\r\n[ ... data ... ]\r\n
+        # data is encoded in hex, hence we read two chars per byte
+        _data = self._read(2+2*64+2).strip()
+        if len(_data) == 0:
+            raise errors.RadioError("No response from radio")
+
+        data = ""
+        for i in range(0, len(_data), 2):
+            data += chr(int(_data[i:i+2], 16))
+
+        if len(data) != 64:
+            LOG.debug("Response was:")
+            LOG.debug("|%s|")
+            LOG.debug("Which I converted to:")
+            LOG.debug(util.hexprint(data))
+            raise Exception("Chunk from radio has wrong size")
+
+        return data
+
+    def _download(self, limit):
+        self._identify()
+
+        data = "\x00"*0x200
+
+        for addr in range(0x200, limit, 0x40):
+            data += self._download_chunk(addr)
+            # Other Alinco drivers delay here, but doesn't seem to be necessary
+            # for this model.
+
+            if self.status_fn:
+                status = chirp_common.Status()
+                status.cur = addr
+                status.max = limit
+                status.msg = "Downloading from radio"
+                self.status_fn(status)
+        return memmap.MemoryMap(data)
+
+    def _upload_chunk(self, addr):
+        if addr % 0x40:
+            raise Exception("Addr 0x%04x not on 64-byte boundary" % addr)
+
+        _data = self._mmap[addr:addr+0x40]
+        data = "".join(["%02X" % ord(x) for x in _data])
+
+        cmd = "AL~F%05XW%s\r" % (addr, data)
+        self._send(cmd)
+
+        resp = self._read(6)
+        if resp.strip() != "OK":
+            raise Exception("Unexpected response from radio: %s" % resp)
+
+    def _upload(self, limit):
+        if not self._identify():
+            raise Exception("I can't talk to this model")
+
+        for addr in range(0x200, self._memsize, 0x40):
+            self._upload_chunk(addr)
+            # Other Alinco drivers delay here, but doesn't seem to be necessary
+            # for this model.
+
+            if self.status_fn:
+                status = chirp_common.Status()
+                status.cur = addr
+                status.max = self._memsize
+                status.msg = "Uploading to radio"
+                self.status_fn(status)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(DJG7EG_MEM_FORMAT, self._mmap)
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number]
+        mem = chirp_common.Memory()
+        mem.number = number
+        if _mem.unknown == 0:
+            mem.empty = True
+        else:
+            mem.freq = int(_mem.freq)
+            mem.mode = self.MODES[_mem.mode]
+            mem.tuning_step = self.STEPS[_mem.step]
+            mem.offset = int(_mem.offset)
+            mem.duplex = self.DUPLEX[_mem.duplex]
+            if self.TMODES[_mem.squelch_type] == "TSQL" and _mem.tx_tone != _mem.rx_tone:
+                mem.tmode = "Cross"
+                mem.cross_mode = "Tone->Tone"
+            else:
+                mem.tmode = self.TMODES[_mem.squelch_type]
+            mem.rtone = ALINCO_TONES[_mem.tx_tone-1]
+            mem.ctone = ALINCO_TONES[_mem.rx_tone-1]
+            mem.dtcs = DCS_CODES[self.VENDOR][_mem.dcs]
+            if _mem.skip:
+                mem.skip = "S"
+            # FIXME find out what every other byte is used for. Japanese?
+            mem.name = str(_mem.name.get_raw()[::2]).rstrip('\0')
+        return mem
+
+    def set_memory(self, mem):
+        # Get a low-level memory object mapped to the image
+        _mem = self._memobj.memory[mem.number]
+        if mem.empty:
+            _mem.unknown = 0x00 # Maybe 0 is empty, 2 is used?
+        else:
+            _mem.unknown = 0x02
+            _mem.freq = mem.freq
+            _mem.mode = self.MODES.index(mem.mode)
+            _mem.step = self.STEPS.index(mem.tuning_step)
+            _mem.offset = mem.offset
+            _mem.duplex = self.DUPLEX.index(mem.duplex)
+            if mem.tmode == "Cross":
+                _mem.squelch_type = self.TMODES.index("TSQL")
+                try:
+                    _mem.tx_tone = ALINCO_TONES.index(mem.rtone)+1
+                except ValueError:
+                    raise errors.UnsupportedToneError("This radio does not support " +
+                                              "tone %.1fHz" % mem.rtone)
+                try:
+                    _mem.rx_tone = ALINCO_TONES.index(mem.ctone)+1
+                except ValueError:
+                    raise errors.UnsupportedToneError("This radio does not support " +
+                                              "tone %.1fHz" % mem.ctone)
+            elif mem.tmode == "TSQL":
+                _mem.squelch_type = self.TMODES.index("TSQL")
+                # Note how the same TSQL tone is copied to both memory locaations
+                try:
+                    _mem.tx_tone = ALINCO_TONES.index(mem.ctone)+1
+                    _mem.rx_tone = ALINCO_TONES.index(mem.ctone)+1
+                except ValueError:
+                    raise errors.UnsupportedToneError("This radio does not support " +
+                                              "tone %.1fHz" % mem.ctone)
+            else:
+                _mem.squelch_type = self.TMODES.index(mem.tmode)
+                try:
+                    _mem.tx_tone = ALINCO_TONES.index(mem.rtone)+1
+                except ValueError:
+                    raise errors.UnsupportedToneError("This radio does not support " +
+                                              "tone %.1fHz" % mem.rtone)
+                try:
+                    _mem.rx_tone = ALINCO_TONES.index(mem.ctone)+1
+                except ValueError:
+                    raise errors.UnsupportedToneError("This radio does not support " +
+                                              "tone %.1fHz" % mem.ctone)
+            _mem.dcs = DCS_CODES[self.VENDOR].index(mem.dtcs)
+            _mem.skip = (mem.skip == "S")
+            _mem.name = "\x00".join(mem.name).ljust(32,"\x00")
diff --git a/chirp/drivers/btech.py b/chirp/drivers/btech.py
index 3d634fa..249f29c 100644
--- a/chirp/drivers/btech.py
+++ b/chirp/drivers/btech.py
@@ -155,6 +155,129 @@ struct {
   u8 unknown1[10];
 } names[200];
 
+#seekto 0x2400;
+struct {
+  u8 period; // one out of LIST_5TONE_STANDARD_PERIODS
+  u8 group_tone;
+  u8 repeat_tone;
+  u8 unused[13];
+} _5tone_std_settings[15];
+
+#seekto 0x2500;
+struct {
+  u8 frame1[5];
+  u8 frame2[5];
+  u8 frame3[5];
+  u8 standard;   // one out of LIST_5TONE_STANDARDS
+} _5tone_codes[15];
+
+#seekto 0x25F0;
+struct {
+  u8 _5tone_delay1; // * 10ms
+  u8 _5tone_delay2; // * 10ms
+  u8 _5tone_delay3; // * 10ms
+  u8 _5tone_first_digit_ext_length;
+  u8 unknown1;
+  u8 unknown2;
+  u8 unknown3;
+  u8 unknown4;
+  u8 decode_standard;
+  u8 unknown5:5,
+     _5tone_decode_call_frame3:1,
+     _5tone_decode_call_frame2:1,
+     _5tone_decode_call_frame1:1;
+  u8 unknown6:5,
+     _5tone_decode_disp_frame3:1,
+     _5tone_decode_disp_frame2:1,
+     _5tone_decode_disp_frame1:1;
+  u8 decode_reset_time; // * 100 + 100ms
+} _5tone_settings;
+
+#seekto 0x2900;
+struct {
+  u8 code[16]; // 0=x0A, A=0x0D, B=0x0E, C=0x0F, D=0x00, #=0x0C *=0x0B
+} dtmf_codes[15];
+
+#seekto 0x29F0;
+struct {
+  u8 dtmfspeed_on;  //list with 50..2000ms in steps of 10
+  u8 dtmfspeed_off; //list with 50..2000ms in steps of 10
+  u8 unknown0[14];
+  u8 inspection[16];
+  u8 monitor[16];
+  u8 alarmcode[16];
+  u8 stun[16];
+  u8 kill[16];
+  u8 revive[16];
+  u8 unknown1[16];
+  u8 unknown2[16];
+  u8 unknown3[16];
+  u8 unknown4[16];
+  u8 unknown5[16];
+  u8 unknown6[16];
+  u8 unknown7[16];
+  u8 masterid[16];
+  u8 viceid[16];
+  u8 unused01:7,
+     mastervice:1;
+  u8 unused02:3,
+     mrevive:1,
+     mkill:1,
+     mstun:1,
+     mmonitor:1,
+     minspection:1;
+  u8 unused03:3,
+     vrevive:1,
+     vkill:1,
+     vstun:1,
+     vmonitor:1,
+     vinspection:1;
+  u8 unused04:6,
+     txdisable:1,
+     rxdisable:1;
+  u8 groupcode;
+  u8 spacecode;
+  u8 delayproctime; // * 100 + 100ms
+  u8 resettime;     // * 100 + 100ms
+} dtmf_settings;
+
+#seekto 0x2D00;
+struct {
+  struct {
+    ul16 freq1;
+    u8 unused01[6];
+    ul16 freq2;
+    u8 unused02[6];
+  } _2tone_encode[15];
+  u8 duration_1st_tone; // *10ms
+  u8 duration_2nd_tone; // *10ms
+  u8 duration_gap;      // *10ms
+  u8 unused03[13];
+  struct {
+    struct {
+      u8 dec;      // one out of LIST_2TONE_DEC
+      u8 response; // one out of LIST_2TONE_RESPONSE
+      u8 alert;    // 1-16
+    } decs[4];
+    u8 unused04[4];
+  } _2tone_decode[15];
+  u8 unused05[16];
+
+  struct {
+    ul16 freqA;
+    ul16 freqB;
+    ul16 freqC;
+    ul16 freqD;
+    // unknown what those values mean, but they are
+    // derived from configured frequencies
+    ul16 derived_from_freqA; // 2304000/freqA
+    ul16 derived_from_freqB; // 2304000/freqB
+    ul16 derived_from_freqC; // 2304000/freqC
+    ul16 derived_from_freqD; // 2304000/freqD
+  }freqs[15];
+  u8 reset_time;  // * 100 + 100ms - 100-8000ms
+} _2tone;
+
 #seekto 0x3000;
 struct {
   u8 freq[8];
@@ -237,6 +360,34 @@ LIST_TXP = ["High", "Low"]
 LIST_WIDE = ["Wide", "Narrow"]
 STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
 LIST_STEP = [str(x) for x in STEPS]
+LIST_5TONE_STANDARDS = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1", "ZVEI2", "ZVEI3",
+                        "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA", "EURO",
+                        "CCITT", "NATEL", "MODAT", "none"]
+LIST_5TONE_STANDARDS_without_none = ["CCIR1", "CCIR2", "PCCIR", "ZVEI1",
+                                     "ZVEI2", "ZVEI3",
+                                     "PZVEI", "DZVEI", "PDZVEI", "EEA", "EIA",
+                                     "EURO", "CCITT", "NATEL", "MODAT"]
+LIST_5TONE_STANDARD_PERIODS = ["20", "30", "40", "50", "60", "70", "80", "90",
+                               "100", "110", "120", "130", "140", "150", "160",
+                               "170", "180", "190", "200"]
+LIST_5TONE_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
+                     "B", "C", "D", "E", "F"]
+LIST_5TONE_DELAY = ["%s ms" % x for x in range(0, 1010, 10)]
+LIST_5TONE_RESET = ["%s ms" % x for x in range(100, 8100, 100)]
+LIST_DTMF_SPEED = ["%s ms" % x for x in range(50, 2010, 10)]
+LIST_DTMF_DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B",
+                    "C", "D", "#", "*"]
+LIST_DTMF_VALUES = [0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+                    0x0D, 0x0E, 0x0F, 0x00, 0x0C, 0x0B ]
+LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
+LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
+LIST_DTMF_DELAY = ["%s ms" % x for x in range(100, 4100, 100)]
+CHARSET_DTMF_DIGITS = "0123456789AaBbCcDd#*"
+LIST_2TONE_DEC = ["A-B", "A-C", "A-D",
+                  "B-A", "B-C", "B-D",
+                  "C-A", "C-B", "C-D",
+                  "D-A", "D-B", "D-C"]
+LIST_2TONE_RESPONSE = ["None", "Alert", "Transpond", "Alert+Transpond"]
 
 # This is a general serial timeout for all serial read functions.
 # Practice has show that about 0.7 sec will be enough to cover all radios.
@@ -1613,7 +1764,772 @@ class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
             fm_presets.append(fmfreq)
             
             i = i + 1
+
+        # DTMF-Setting
+        dtmf_enc_settings = RadioSettingGroup ("dtmf_enc_settings",
+                                               "DTMF Encoding Settings")
+        dtmf_dec_settings = RadioSettingGroup ("dtmf_dec_settings",
+                                               "DTMF Decoding Settings")
+        top.append(dtmf_enc_settings)
+        top.append(dtmf_dec_settings)
+        txdisable = RadioSetting("dtmf_settings.txdisable", 
+                                 "TX-Disable",
+                                 RadioSettingValueBoolean(
+                                     _mem.dtmf_settings.txdisable))
+        dtmf_enc_settings.append(txdisable)
+
+        rxdisable = RadioSetting("dtmf_settings.rxdisable", 
+                                 "RX-Disable",
+                                 RadioSettingValueBoolean(
+                                     _mem.dtmf_settings.rxdisable))
+        dtmf_enc_settings.append(rxdisable)
+
+        dtmfspeed_on = RadioSetting(
+            "dtmf_settings.dtmfspeed_on",
+            "DTMF Speed (On Time)",
+            RadioSettingValueList(LIST_DTMF_SPEED,
+                                  LIST_DTMF_SPEED[
+                                      _mem.dtmf_settings.dtmfspeed_on]))
+        dtmf_enc_settings.append(dtmfspeed_on)
+
+        dtmfspeed_off = RadioSetting(
+            "dtmf_settings.dtmfspeed_off",
+            "DTMF Speed (Off Time)",
+            RadioSettingValueList(LIST_DTMF_SPEED,
+                                  LIST_DTMF_SPEED[
+                                      _mem.dtmf_settings.dtmfspeed_off]))
+        dtmf_enc_settings.append(dtmfspeed_off)
+
+        def memory2string(dmtf_mem):
+            dtmf_string = ""
+            for digit in dmtf_mem:
+                if digit != 255:
+                    index = LIST_DTMF_VALUES.index(digit)
+                    dtmf_string = dtmf_string + LIST_DTMF_DIGITS[index]
+            return dtmf_string
+
+        def apply_dmtf_frame(setting, obj):
+            LOG.debug("Setting DTMF-Code: " + str(setting.value) )
+            val_string = str(setting.value)
+            for i in range(0,16):
+                obj[i] = 255
+            i = 0
+            for current_char in val_string:
+                current_char = current_char.upper()
+                index = LIST_DTMF_DIGITS.index(current_char)
+                obj[i] = LIST_DTMF_VALUES[ index ]
+                i = i + 1
+
+        codes = self._memobj.dtmf_codes
+        i = 1
+        for dtmfcode in codes:
+            val = RadioSettingValueString(0, 16, 
+                                          memory2string(dtmfcode.code),
+                                          False, CHARSET_DTMF_DIGITS)
+            line = RadioSetting("dtmf_code_" + str(i) + "_code",
+                                "DMTF Code " + str(i), val)
+            line.set_apply_callback(apply_dmtf_frame, dtmfcode.code)
+            dtmf_enc_settings.append(line)
+            i = i + 1
+
+        line = RadioSetting("dtmf_settings.mastervice", 
+                            "Master and Vice ID",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.mastervice))
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.masterid),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.masterid",
+                            "Master Control ID ", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.masterid)
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.minspection", 
+                            "Master Inspection",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.minspection))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.mmonitor", 
+                            "Master Monitor",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.mmonitor))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.mstun", 
+                            "Master Stun",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.mstun))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.mkill", 
+                            "Master Kill",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.mkill))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.mrevive", 
+                            "Master Revive",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.mrevive))
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.viceid),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.viceid",
+                            "Vice Control ID ", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.viceid)
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.vinspection", 
+                            "Vice Inspection",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.vinspection))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.vmonitor", 
+                            "Vice Monitor",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.vmonitor))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.vstun", 
+                            "Vice Stun",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.vstun))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.vkill", 
+                            "Vice Kill",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.vkill))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting("dtmf_settings.vrevive", 
+                            "Vice Revive",
+                            RadioSettingValueBoolean(
+                                _mem.dtmf_settings.vrevive))
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.inspection),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.inspection",
+                            "Inspection", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.inspection)
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.alarmcode),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.alarmcode",
+                            "Alarm", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.alarmcode)
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.kill),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.kill",
+                            "Kill", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.kill)
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.monitor),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.monitor",
+                            "Monitor", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.monitor)
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.stun),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.stun",
+                            "Stun", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.stun)
+        dtmf_dec_settings.append(line)
+
+        val = RadioSettingValueString(0, 16, 
+                                      memory2string(
+                                          _mem.dtmf_settings.revive),
+                                          False, CHARSET_DTMF_DIGITS)
+        line = RadioSetting("dtmf_settings.revive",
+                            "Revive", val)
+        line.set_apply_callback(apply_dmtf_frame,
+                                _mem.dtmf_settings.revive)
+        dtmf_dec_settings.append(line)
+
+        def apply_dmtf_listvalue(setting, obj):
+            LOG.debug("Setting value: "+ str(setting.value) + " from list")
+            val = str(setting.value)
+            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
+            val = LIST_DTMF_SPECIAL_VALUES[index]
+            obj.set_value(val)
+
+        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.groupcode)
+        line = RadioSetting(
+            "dtmf_settings.groupcode",
+            "Group Code",
+            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
+        line.set_apply_callback(apply_dmtf_listvalue,
+                                _mem.dtmf_settings.groupcode)
+        dtmf_dec_settings.append(line)
+
+        idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.dtmf_settings.spacecode)
+        line = RadioSetting(
+            "dtmf_settings.spacecode",
+            "Space Code",
+            RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+                                  LIST_DTMF_SPECIAL_DIGITS[idx]))
+        line.set_apply_callback(apply_dmtf_listvalue,
+                                _mem.dtmf_settings.spacecode)
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting(
+            "dtmf_settings.resettime",
+            "Reset time",
+            RadioSettingValueList(LIST_5TONE_RESET,
+                                  LIST_5TONE_RESET[
+                                      _mem.dtmf_settings.resettime]))
+        dtmf_dec_settings.append(line)
+
+        line = RadioSetting(
+            "dtmf_settings.delayproctime",
+            "Delay processing time",
+            RadioSettingValueList(LIST_DTMF_DELAY,
+                                  LIST_DTMF_DELAY[
+                                      _mem.dtmf_settings.delayproctime]))
+        dtmf_dec_settings.append(line)
+
+
+        # 5 Tone Settings
+        stds_5tone = RadioSettingGroup ("stds_5tone", "Standards")
+        codes_5tone = RadioSettingGroup ("codes_5tone", "Codes")
+
+        group_5tone = RadioSettingGroup ("group_5tone", "5 Tone Settings")
+        group_5tone.append(stds_5tone)
+        group_5tone.append(codes_5tone)
+
+        top.append(group_5tone)
+
+        def apply_list_value(setting, obj):
+            options = setting.value.get_options()
+            obj.set_value ( options.index(str(setting.value)) )
+
+        _5tone_standards = self._memobj._5tone_std_settings
+        i = 0
+        for standard in _5tone_standards:
+            std_5tone = RadioSettingGroup ("std_5tone_" + str(i), 
+                                           LIST_5TONE_STANDARDS[i])
+            stds_5tone.append(std_5tone)
+ 
+            period = standard.period
+            if period == 255:
+                LOG.debug("Period for " + LIST_5TONE_STANDARDS[i] + 
+                          " is not yet configured. Setting to 70ms.")
+                period = 5
+
+            if period <= len( LIST_5TONE_STANDARD_PERIODS ):
+                line = RadioSetting(
+                    "_5tone_std_settings_" + str(i) + "_period",
+                    "Period (ms)", RadioSettingValueList
+                    (LIST_5TONE_STANDARD_PERIODS,
+                     LIST_5TONE_STANDARD_PERIODS[period]))
+                line.set_apply_callback(apply_list_value, standard.period)
+                std_5tone.append(line)
+            else:
+                LOG.debug("Invalid value for 5tone period! Disabling.")
+
+            group_tone = standard.group_tone
+            if group_tone == 255:
+                LOG.debug("Group-Tone for " + LIST_5TONE_STANDARDS[i] +
+                          " is not yet configured. Setting to A.")
+                group_tone = 10
+
+            if group_tone <= len( LIST_5TONE_DIGITS ):
+                line = RadioSetting(
+                    "_5tone_std_settings_" + str(i) + "_grouptone",
+                    "Group Tone",
+                    RadioSettingValueList(LIST_5TONE_DIGITS,
+                                          LIST_5TONE_DIGITS[
+                                              group_tone]))
+                line.set_apply_callback(apply_list_value,
+                                        standard.group_tone)
+                std_5tone.append(line)
+            else:
+                LOG.debug("Invalid value for 5tone digit! Disabling.")
+
+            repeat_tone = standard.repeat_tone
+            if repeat_tone == 255:
+                LOG.debug("Repeat-Tone for " + LIST_5TONE_STANDARDS[i] + 
+                          " is not yet configured. Setting to E.")
+                repeat_tone = 14
+
+            if repeat_tone <= len( LIST_5TONE_DIGITS ):
+                line = RadioSetting(
+                    "_5tone_std_settings_" + str(i) + "_repttone",
+                    "Repeat Tone",
+                    RadioSettingValueList(LIST_5TONE_DIGITS,
+                                          LIST_5TONE_DIGITS[
+                                              repeat_tone]))
+                line.set_apply_callback(apply_list_value,
+                                        standard.repeat_tone)
+                std_5tone.append(line)
+            else:
+                LOG.debug("Invalid value for 5tone digit! Disabling.")
+            i = i + 1
+
+        def my_apply_5tonestdlist_value(setting, obj):
+            if LIST_5TONE_STANDARDS.index(str(setting.value)) == 15:
+                obj.set_value(0xFF)
+            else:
+                obj.set_value( LIST_5TONE_STANDARDS.
+                              index(str(setting.value)) )
+
+        def apply_5tone_frame(setting, obj):
+            LOG.debug("Setting 5 Tone: " + str(setting.value) )
+            valstring = str(setting.value)
+            if len(valstring) == 0:
+                for i in range(0,5):
+                    obj[i] = 255
+            else:
+                validFrame = True
+                for i in range(0,5):
+                    currentChar = valstring[i].upper()
+                    if currentChar in LIST_5TONE_DIGITS:
+                        obj[i] = LIST_5TONE_DIGITS.index(currentChar)
+                    else:
+                        validFrame = False
+                        LOG.debug("invalid char: " + str(currentChar))
+                if not validFrame:
+                    LOG.debug("setting whole frame to FF" )
+                    for i in range(0,5):
+                        obj[i] = 255
+
+        def validate_5tone_frame(value):
+            if (len(str(value)) != 5) and (len(str(value)) != 0) :
+                msg = ("5 Tone must have 5 digits or 0 digits")
+                raise InvalidValueError(msg)
+            for digit in str(value):
+                if digit.upper() not in LIST_5TONE_DIGITS:
+                    msg = (str(digit) + " is not a valid digit for 5tones")
+                    raise InvalidValueError(msg)
+            return value
+
+        def frame2string(frame):
+            frameString = ""
+            for digit in frame:
+                if digit != 255:
+                    frameString = frameString + LIST_5TONE_DIGITS[digit]
+            return frameString
+
+        _5tone_codes = self._memobj._5tone_codes
+        i = 1
+        for code in _5tone_codes:
+            code_5tone = RadioSettingGroup ("code_5tone_" + str(i),
+                                            "5 Tone code " + str(i))
+            codes_5tone.append(code_5tone)
+            if (code.standard == 255 ):
+                currentVal = 15
+            else:
+                currentVal = code.standard
+            line = RadioSetting("_5tone_code_" + str(i) + "_std", 
+                                " Standard",
+                                RadioSettingValueList(LIST_5TONE_STANDARDS,
+                                                      LIST_5TONE_STANDARDS[
+                                                          currentVal]) )
+            line.set_apply_callback(my_apply_5tonestdlist_value,
+                                    code.standard)
+            code_5tone.append(line)
+
+            val = RadioSettingValueString(0, 6,
+                                          frame2string(code.frame1), False)
+            line = RadioSetting("_5tone_code_" + str(i) + "_frame1", 
+                                " Frame 1", val)
+            val.set_validate_callback(validate_5tone_frame)
+            line.set_apply_callback(apply_5tone_frame, code.frame1)
+            code_5tone.append(line)
+
+            val = RadioSettingValueString(0, 6,
+                                          frame2string(code.frame2), False)
+            line = RadioSetting("_5tone_code_" + str(i) + "_frame2",
+                                " Frame 2", val)
+            val.set_validate_callback(validate_5tone_frame)
+            line.set_apply_callback(apply_5tone_frame, code.frame2)
+            code_5tone.append(line)
+
+            val = RadioSettingValueString(0, 6,
+                                          frame2string(code.frame3), False)
+            line = RadioSetting("_5tone_code_" + str(i) + "_frame3",
+                                " Frame 3", val)
+            val.set_validate_callback(validate_5tone_frame)
+            line.set_apply_callback(apply_5tone_frame, code.frame3)
+            code_5tone.append(line)
+            i = i + 1
+
+        _5_tone_decode1 = RadioSetting(
+            "_5tone_settings._5tone_decode_call_frame1",
+            "5 Tone decode call Frame 1",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_call_frame1))
+        group_5tone.append(_5_tone_decode1)
+
+        _5_tone_decode2 = RadioSetting(
+            "_5tone_settings._5tone_decode_call_frame2",
+            "5 Tone decode call Frame 2",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_call_frame2))
+        group_5tone.append(_5_tone_decode2)
+
+        _5_tone_decode3 = RadioSetting(
+            "_5tone_settings._5tone_decode_call_frame3",
+            "5 Tone decode call Frame 3",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_call_frame3))
+        group_5tone.append(_5_tone_decode3)
+
+        _5_tone_decode_disp1 = RadioSetting(
+            "_5tone_settings._5tone_decode_disp_frame1",
+            "5 Tone decode disp Frame 1",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_disp_frame1))
+        group_5tone.append(_5_tone_decode_disp1)
+
+        _5_tone_decode_disp2 = RadioSetting(
+            "_5tone_settings._5tone_decode_disp_frame2",
+            "5 Tone decode disp Frame 2",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_disp_frame2))
+        group_5tone.append(_5_tone_decode_disp2)
+
+        _5_tone_decode_disp3 = RadioSetting(
+            "_5tone_settings._5tone_decode_disp_frame3",
+            "5 Tone decode disp Frame 3",
+            RadioSettingValueBoolean(
+                _mem._5tone_settings._5tone_decode_disp_frame3))
+        group_5tone.append(_5_tone_decode_disp3)
+
+        decode_standard = _mem._5tone_settings.decode_standard
+        if decode_standard == 255:
+            decode_standard = 0
+        if decode_standard <= len (LIST_5TONE_STANDARDS_without_none) :
+            line = RadioSetting("_5tone_settings.decode_standard", 
+                                "5 Tone-decode Standard",
+                                RadioSettingValueList(
+                                    LIST_5TONE_STANDARDS_without_none,
+                                    LIST_5TONE_STANDARDS_without_none[
+                                        decode_standard]))
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid decode std...")
             
+        _5tone_delay1 = _mem._5tone_settings._5tone_delay1
+        if _5tone_delay1 == 255:
+            _5tone_delay1 = 20
+
+        if _5tone_delay1 <= len( LIST_5TONE_DELAY ):
+            list = RadioSettingValueList(LIST_5TONE_DELAY, 
+                                         LIST_5TONE_DELAY[
+                                             _5tone_delay1])
+            line = RadioSetting("_5tone_settings._5tone_delay1",
+                                "5 Tone Delay Frame 1", list)
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid value for 5tone delay (frame1) ! Disabling.")
+
+        _5tone_delay2 = _mem._5tone_settings._5tone_delay2
+        if _5tone_delay2 == 255:
+            _5tone_delay2 = 20
+            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
+
+        if _5tone_delay2 <= len( LIST_5TONE_DELAY ):
+            list = RadioSettingValueList(LIST_5TONE_DELAY,
+                                         LIST_5TONE_DELAY[
+                                             _5tone_delay2])
+            line = RadioSetting("_5tone_settings._5tone_delay2",
+                                "5 Tone Delay Frame 2", list)
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid value for 5tone delay (frame2)! Disabling.")
+
+        _5tone_delay3 = _mem._5tone_settings._5tone_delay3
+        if _5tone_delay3 == 255:
+            _5tone_delay3 = 20
+            LOG.debug("5 Tone delay unconfigured! Resetting to 200ms.")
+
+        if _5tone_delay3 <= len( LIST_5TONE_DELAY ):
+            list = RadioSettingValueList(LIST_5TONE_DELAY,
+                                         LIST_5TONE_DELAY[
+                                             _5tone_delay3])
+            line = RadioSetting("_5tone_settings._5tone_delay3",
+                                "5 Tone Delay Frame 3", list )
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid value for 5tone delay (frame3)! Disabling.")
+
+        ext_length = _mem._5tone_settings._5tone_first_digit_ext_length
+        if ext_length == 255:
+            ext_length = 0
+            LOG.debug("1st Tone ext lenght unconfigured! Resetting to 0")
+
+        if ext_length <= len(
+            LIST_5TONE_DELAY ):
+            list = RadioSettingValueList(
+                LIST_5TONE_DELAY, 
+                LIST_5TONE_DELAY[
+                    ext_length])
+            line = RadioSetting(
+                "_5tone_settings._5tone_first_digit_ext_length",
+                "First digit extend length", list)
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid value for 5tone ext length! Disabling.")
+
+        decode_reset_time = _mem._5tone_settings.decode_reset_time
+        if decode_reset_time == 255:
+            decode_reset_time = 59
+            LOG.debug("Decode reset time unconfigured. resetting.")
+        if decode_reset_time <= len(LIST_5TONE_RESET):
+            list = RadioSettingValueList(
+                LIST_5TONE_RESET,
+                LIST_5TONE_RESET[
+                    decode_reset_time])
+            line = RadioSetting("_5tone_settings.decode_reset_time",
+                                "Decode reset time", list)
+            group_5tone.append(line)
+        else:
+            LOG.debug("Invalid value decode reset time! Disabling.")
+
+        # 2 Tone
+        encode_2tone = RadioSettingGroup ("encode_2tone", "2 Tone Encode")
+        decode_2tone = RadioSettingGroup ("decode_2tone", "2 Code Decode")
+
+        top.append(encode_2tone)
+        top.append(decode_2tone)
+
+        duration_1st_tone = self._memobj._2tone.duration_1st_tone
+        if duration_1st_tone == 255:
+            LOG.debug("Duration of first 2 Tone digit is not yet " +
+                      "configured. Setting to 600ms")
+            duration_1st_tone = 60
+
+        if duration_1st_tone <= len( LIST_5TONE_DELAY ):
+            line = RadioSetting("_2tone.duration_1st_tone", 
+                                "Duration 1st Tone",
+                                RadioSettingValueList(LIST_5TONE_DELAY,
+                                                      LIST_5TONE_DELAY[
+                                                          duration_1st_tone]))
+            encode_2tone.append(line)
+
+        duration_2nd_tone = self._memobj._2tone.duration_2nd_tone
+        if duration_2nd_tone == 255:
+            LOG.debug("Duration of second 2 Tone digit is not yet " +
+                      "configured. Setting to 600ms")
+            duration_2nd_tone = 60
+
+        if duration_2nd_tone <= len( LIST_5TONE_DELAY ):
+            line = RadioSetting("_2tone.duration_2nd_tone", 
+                                "Duration 2nd Tone",
+                                RadioSettingValueList(LIST_5TONE_DELAY,
+                                                      LIST_5TONE_DELAY[
+                                                          duration_2nd_tone]))
+            encode_2tone.append(line)
+
+        duration_gap = self._memobj._2tone.duration_gap
+        if duration_gap == 255:
+            LOG.debug("Duration of gap is not yet " +
+                      "configured. Setting to 300ms")
+            duration_gap = 30
+
+        if duration_gap <= len( LIST_5TONE_DELAY ):
+            line = RadioSetting("_2tone.duration_gap", "Duration of gap",
+                                RadioSettingValueList(LIST_5TONE_DELAY,
+                                                      LIST_5TONE_DELAY[
+                                                          duration_gap]))
+            encode_2tone.append(line)
+
+        def _2tone_validate(value):
+            if value == 0:
+                return 65535
+            if value == 65535:
+                return value
+            if not (300 <= value and value <= 3000):
+                msg = ("2 Tone Frequency: Must be between 300 and 3000 Hz")
+                raise InvalidValueError(msg)
+            return value
+
+        def apply_2tone_freq(setting, obj):
+            val = int(setting.value)
+            if (val == 0) or (val == 65535):
+                obj.set_value(65535)
+            else:
+                obj.set_value(val)
+
+        i = 1
+        for code in  self._memobj._2tone._2tone_encode:
+            code_2tone = RadioSettingGroup ("code_2tone_" + str(i), 
+                                           "Encode Code " + str(i))
+            encode_2tone.append(code_2tone)
+
+            tmp = code.freq1
+            if tmp == 65535:
+                tmp = 0
+            val1 = RadioSettingValueInteger(0, 65535, tmp)
+            freq1 = RadioSetting("2tone_code_"+ str(i) + "_freq1", 
+                                 "Frequency 1", val1)
+            val1.set_validate_callback(_2tone_validate)
+            freq1.set_apply_callback(apply_2tone_freq, code.freq1)
+            code_2tone.append(freq1)
+
+            tmp = code.freq2
+            if tmp == 65535:
+                tmp = 0
+            val2 = RadioSettingValueInteger(0, 65535, tmp)
+            freq2 = RadioSetting("2tone_code_"+ str(i) + "_freq2", 
+                                 "Frequency 2", val2)
+            val2.set_validate_callback(_2tone_validate)
+            freq2.set_apply_callback(apply_2tone_freq, code.freq2)
+            code_2tone.append(freq2)
+
+            i = i + 1
+
+        decode_reset_time = _mem._2tone.reset_time
+        if decode_reset_time == 255:
+            decode_reset_time = 59
+            LOG.debug("Decode reset time unconfigured. resetting.")
+        if decode_reset_time <= len(LIST_5TONE_RESET):
+            list = RadioSettingValueList(
+                LIST_5TONE_RESET,
+                LIST_5TONE_RESET[
+                    decode_reset_time])
+            line = RadioSetting("_2tone.reset_time",
+                                "Decode reset time", list)
+            decode_2tone.append(line)
+        else:
+            LOG.debug("Invalid value decode reset time! Disabling.")
+
+        def apply_2tone_freq_pair(setting, obj):
+            val = int(setting.value)
+            derived_val = 65535
+            frqname = str(setting._name[-5:])
+            derivedname = "derived_from_" + frqname
+
+            if (val == 0):
+                val = 65535
+                derived_val = 65535
+            else:
+                derived_val = int(round(2304000.0/val))
+
+            obj[frqname].set_value( val )
+            obj[derivedname].set_value( derived_val )
+
+            LOG.debug("Apply " + frqname + ": " + str(val) + " | " 
+                      + derivedname + ": " + str(derived_val))
+
+        i = 1
+        for decode_code in  self._memobj._2tone._2tone_decode:
+            _2tone_dec_code = RadioSettingGroup ("code_2tone_" + str(i),
+                                           "Decode Code " + str(i))
+            decode_2tone.append(_2tone_dec_code)
+
+            j = 1
+            for dec in decode_code.decs:
+                val = dec.dec
+                if val == 255:
+                    LOG.debug("Dec for Code " + str(i) + " Dec " + str(j) +  
+                              " is not yet configured. Setting to 0.")
+                    val = 0
+
+                if val <= len( LIST_2TONE_DEC ):
+                    line = RadioSetting(
+                        "_2tone_dec_settings_" + str(i) + "_dec_" + str(j),
+                        "Dec " + str(j), RadioSettingValueList
+                        (LIST_2TONE_DEC,
+                         LIST_2TONE_DEC[val]))
+                    line.set_apply_callback(apply_list_value, dec.dec)
+                    _2tone_dec_code.append(line)
+                else:
+                    LOG.debug("Invalid value for 2tone dec! Disabling.")
+
+                val = dec.response
+                if val == 255:
+                    LOG.debug("Response for Code " + str(i) + " Dec " + str(j)+
+                              " is not yet configured. Setting to 0.")
+                    val = 0
+
+                if val <= len( LIST_2TONE_RESPONSE ):
+                    line = RadioSetting(
+                        "_2tone_dec_settings_" + str(i) + "_resp_" + str(j),
+                        "Response " + str(j), RadioSettingValueList
+                        (LIST_2TONE_RESPONSE,
+                         LIST_2TONE_RESPONSE[val]))
+                    line.set_apply_callback(apply_list_value, dec.response)
+                    _2tone_dec_code.append(line)
+                else:
+                    LOG.debug("Invalid value for 2tone response! Disabling.")
+
+                val = dec.alert
+                if val == 255:
+                    LOG.debug("Alert for Code " + str(i) + " Dec " + str(j) +  
+                              " is not yet configured. Setting to 0.")
+                    val = 0
+
+                if val <= len( PTTIDCODE_LIST ):
+                    line = RadioSetting(
+                        "_2tone_dec_settings_" + str(i) + "_alert_" + str(j),
+                        "Alert " + str(j), RadioSettingValueList
+                        (PTTIDCODE_LIST,
+                         PTTIDCODE_LIST[val]))
+                    line.set_apply_callback(apply_list_value, dec.alert)
+                    _2tone_dec_code.append(line)
+                else:
+                    LOG.debug("Invalid value for 2tone alert! Disabling.")
+                j = j + 1
+
+            freq = self._memobj._2tone.freqs[i-1]
+            for char in ['A', 'B', 'C', 'D']:
+                setting_name = "freq" + str(char)
+
+                tmp = freq[setting_name]
+                if tmp == 65535:
+                    tmp = 0
+                if tmp != 0:
+                    expected = int(round(2304000.0/tmp))
+                    from_mem = freq["derived_from_" + setting_name]
+                    if expected != from_mem:
+                        LOG.error("Expected " + str(expected) + 
+                                  " but read " + str(from_mem ) + 
+                                  ". Disabling 2Tone Decode Freqs!")
+                        break
+                val = RadioSettingValueInteger(0, 65535, tmp)
+                frq = RadioSetting("2tone_dec_"+ str(i) + "_freq" + str(char),
+                                         ("Decode Frequency " +str(char)), val)
+                val.set_validate_callback(_2tone_validate)
+                frq.set_apply_callback(apply_2tone_freq_pair, freq)
+                _2tone_dec_code.append(frq)
+
+            i = i + 1
+
         return top
 
     def set_settings(self, settings):
diff --git a/chirp/drivers/ft2900.py b/chirp/drivers/ft2900.py
index 7175733..a7ad33d 100644
--- a/chirp/drivers/ft2900.py
+++ b/chirp/drivers/ft2900.py
@@ -494,6 +494,7 @@ class FT2900BankModel(chirp_common.BankModel):
 
 @directory.register
 class FT2900Radio(YaesuCloneModeRadio):
+
     """Yaesu FT-2900"""
     VENDOR = "Yaesu"
     MODEL = "FT-2900R/1900R"
@@ -778,7 +779,8 @@ class FT2900Radio(YaesuCloneModeRadio):
         # 7 BELL
         opts = ["Off", "1", "3", "5", "8", "Continuous"]
         ctcss.append(RadioSetting("bell", "Bell Repetitions",
-                                  RadioSettingValueList(opts, opts[_settings.bell])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.bell])))
 
         # 8 BNK.LNK
         for i in range(0, 8):
@@ -802,7 +804,8 @@ class FT2900Radio(YaesuCloneModeRadio):
         # 11 CW.ID
         opts = ["Off", "On"]
         arts.append(RadioSetting("cw_id", "CW ID Enable",
-                                 RadioSettingValueList(opts, opts[_settings.cw_id])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.cw_id])))
 
         cw_id_text = ""
         for i in _settings.cw_id_string:
@@ -832,14 +835,16 @@ class FT2900Radio(YaesuCloneModeRadio):
                 "10WPM", "11WPM", "12WPM", "13WPM", "15WPM", "17WPM",
                 "20WPM", "24WPM", "30WPM", "40WPM"]
         misc.append(RadioSetting("cw_trng", "CW Training",
-                                 RadioSettingValueList(opts, opts[_settings.cw_trng])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.cw_trng])))
 
         # todo: make the setting of the units here affect the display
         # of the speed.  Not critical, but would be slick.
         opts = ["CPM", "WPM"]
         misc.append(RadioSetting("cw_trng_units", "CW Training Units",
                                  RadioSettingValueList(opts,
-                                                       opts[_settings.cw_trng_units])))
+                                                       opts[_settings.
+                                                            cw_trng_units])))
 
         # 13 DC VLT - a read-only status, so nothing to do here
 
@@ -920,7 +925,8 @@ class FT2900Radio(YaesuCloneModeRadio):
         # 22 INT.CD
         opts = ["DTMF %X" % (x) for x in range(0, 16)]
         wires.append(RadioSetting("int_cd", "Access Number for WiRES(TM)",
-                                  RadioSettingValueList(opts, opts[_settings.int_cd])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.int_cd])))
 
         # 23 ING MD
         opts = ["Sister Radio Group", "Friends Radio Group"]
@@ -1103,24 +1109,28 @@ class FT2900Radio(YaesuCloneModeRadio):
         # 41 RESUME
         opts = ["3 Sec", "5 Sec", "10 Sec", "Busy", "Hold"]
         scan.append(RadioSetting("resume", "Scan Resume Mode",
-                                 RadioSettingValueList(opts, opts[_settings.resume])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.resume])))
 
         # 42 RF.SQL
         opts = ["Off"] + ["S-%d" % (x) for x in range(1, 10)]
         misc.append(RadioSetting("rf_sql", "RF Squelch Threshold",
-                                 RadioSettingValueList(opts, opts[_settings.rf_sql])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.rf_sql])))
 
         # 43 RPT - per channel attribute, nothing to do here
 
         # 44 RVRT
         opts = ["Off", "On"]
         misc.append(RadioSetting("revert", "Priority Revert",
-                                 RadioSettingValueList(opts, opts[_settings.revert])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.revert])))
 
         # 45 S.SRCH
         opts = ["Single", "Continuous"]
         misc.append(RadioSetting("s_search", "Smart Search Sweep Mode",
-                                 RadioSettingValueList(opts, opts[_settings.s_search])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.s_search])))
 
         # 46 SHIFT - per channel setting, nothing to do here
 
@@ -1166,12 +1176,14 @@ class FT2900Radio(YaesuCloneModeRadio):
         # 57 WX.ALT
         opts = ["Off", "On"]
         misc.append(RadioSetting("wx_alert", "Weather Alert Scan",
-                                 RadioSettingValueList(opts, opts[_settings.wx_alert])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.wx_alert])))
 
         # 58 WX.VOL
         opts = ["Normal", "Maximum"]
         misc.append(RadioSetting("wx_vol_max", "Weather Alert Volume",
-                                 RadioSettingValueList(opts, opts[_settings.wx_vol_max])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.wx_vol_max])))
 
         # 59 W/N DV - this is a per-channel attribute, nothing to do here
 
@@ -1235,8 +1247,9 @@ class FT2900Radio(YaesuCloneModeRadio):
 # 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
 # NOTE: Disabled until detection is fixed
-#@directory.register
+# @directory.register
 class FT2900ERadio(FT2900Radio):
+
     """Yaesu FT-2900E"""
     MODEL = "FT-2900E/1900E"
     VARIANT = "E"
diff --git a/chirp/drivers/ft60.py b/chirp/drivers/ft60.py
index 39c8f44..3c00d84 100644
--- a/chirp/drivers/ft60.py
+++ b/chirp/drivers/ft60.py
@@ -340,6 +340,7 @@ class FT60BankModel(chirp_common.BankModel):
 
 @directory.register
 class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
+
     """Yaesu FT-60"""
     BAUD_RATE = 9600
     VENDOR = "Yaesu"
@@ -487,27 +488,32 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # BELL
         opts = ["OFF", "1T", "3T", "5T", "8T", "CONT"]
         ctcss.append(RadioSetting("bell", "Bell Repetitions",
-                                  RadioSettingValueList(opts, opts[_settings.bell])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.bell])))
 
         # BSY.LED
         opts = ["ON", "OFF"]
         misc.append(RadioSetting("bsy_led", "Busy LED",
-                                 RadioSettingValueList(opts, opts[_settings.bsy_led])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.bsy_led])))
 
         # DCS.NR
         opts = ["TR/X N", "RX R", "TX R", "T/RX R"]
         ctcss.append(RadioSetting("dcs_nr", "\"Inverted\" DCS Code Decoding",
-                                  RadioSettingValueList(opts, opts[_settings.dcs_nr])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.dcs_nr])))
 
         # DT.DLY
         opts = ["50 MS", "100 MS", "250 MS", "450 MS", "750 MS", "1000 MS"]
         ctcss.append(RadioSetting("dt_dly", "DTMF Autodialer Delay Time",
-                                  RadioSettingValueList(opts, opts[_settings.dt_dly])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.dt_dly])))
 
         # DT.SPD
         opts = ["50 MS", "100 MS"]
         ctcss.append(RadioSetting("dt_spd", "DTMF Autodialer Sending Speed",
-                                  RadioSettingValueList(opts, opts[_settings.dt_spd])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.dt_spd])))
 
         # DT.WRT
         for i in range(0, 9):
@@ -535,7 +541,8 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # EDG.BEP
         opts = ["OFF", "ON"]
         misc.append(RadioSetting("edg_bep", "Band Edge Beeper",
-                                 RadioSettingValueList(opts, opts[_settings.edg_bep])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.edg_bep])))
 
         # I.NET
         opts = ["OFF", "COD", "MEM"]
@@ -553,7 +560,8 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
                 "CODE 5", "CODE 6", "CODE 7", "CODE 8", "CODE 9",
                 "CODE A", "CODE B", "CODE C", "CODE D", "CODE E", "CODE F"]
         wires.append(RadioSetting("int_cd", "Access Number for WiRES(TM)",
-                                  RadioSettingValueList(opts, opts[_settings.int_cd])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.int_cd])))
 
         # INT.MR
         opts = ["d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9"]
@@ -564,7 +572,8 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # LAMP
         opts = ["KEY", "5SEC", "TOGGLE"]
         switch.append(RadioSetting("lamp", "Lamp Mode",
-                                   RadioSettingValueList(opts, opts[_settings.lamp])))
+                                   RadioSettingValueList(opts, opts[
+                                                         _settings.lamp])))
 
         # LOCK
         opts = ["LK KEY", "LKDIAL", "LK K+D", "LK PTT",
@@ -581,33 +590,39 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # M/T-CL
         opts = ["MONI", "T-CALL"]
         switch.append(RadioSetting("mt_cl", "MONI Switch Function",
-                                   RadioSettingValueList(opts, opts[_settings.mt_cl])))
+                                   RadioSettingValueList(opts, opts[
+                                                         _settings.mt_cl])))
 
         # PAG.ABK
         opts = ["OFF", "ON"]
         eai.append(RadioSetting("pag_abk", "Paging Answer Back",
-                                RadioSettingValueList(opts, opts[_settings.pag_abk])))
+                                RadioSettingValueList(opts, opts[
+                                                      _settings.pag_abk])))
 
         # RESUME
         opts = ["TIME", "HOLD", "BUSY"]
         scan.append(RadioSetting("resume", "Scan Resume Mode",
-                                 RadioSettingValueList(opts, opts[_settings.resume])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.resume])))
 
         # REV/HM
         opts = ["REV", "HOME"]
         switch.append(RadioSetting("rev_hm", "HM/RV Key Function",
-                                   RadioSettingValueList(opts, opts[_settings.rev_hm])))
+                                   RadioSettingValueList(opts, opts[
+                                                         _settings.rev_hm])))
 
         # RF.SQL
         opts = ["OFF", "S-1", "S-2", "S-3", "S-4", "S-5", "S-6",
                 "S-7", "S-8", "S-FULL"]
         misc.append(RadioSetting("rf_sql", "RF Squelch Threshold",
-                                 RadioSettingValueList(opts, opts[_settings.rf_sql])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.rf_sql])))
 
         # PRI.RVT
         opts = ["OFF", "ON"]
         scan.append(RadioSetting("pri_rvt", "Priority Revert",
-                                 RadioSettingValueList(opts, opts[_settings.pri_rvt])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.pri_rvt])))
 
         # RXSAVE
         opts = ["OFF", "200 MS", "300 MS", "500 MS", "1 S", "2 S"]
@@ -618,7 +633,8 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # S.SRCH
         opts = ["SINGLE", "CONT"]
         misc.append(RadioSetting("ssrch", "Smart Search Sweep Mode",
-                                 RadioSettingValueList(opts, opts[_settings.ssrch])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.ssrch])))
 
         # SCN.MD
         opts = ["MEM", "ONLY"]
@@ -629,32 +645,38 @@ class FT60Radio(yaesu_clone.YaesuCloneModeRadio):
         # SCN.LMP
         opts = ["OFF", "ON"]
         scan.append(RadioSetting("scn_lmp", "Scan Lamp",
-                                 RadioSettingValueList(opts, opts[_settings.scn_lmp])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.scn_lmp])))
 
         # TOT
         opts = ["OFF"] + ["%dMIN" % (x) for x in range(1, 30 + 1)]
         misc.append(RadioSetting("tot", "Timeout Timer",
-                                 RadioSettingValueList(opts, opts[_settings.tot])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.tot])))
 
         # TX.LED
         opts = ["ON", "OFF"]
         misc.append(RadioSetting("tx_led", "TX LED",
-                                 RadioSettingValueList(opts, opts[_settings.tx_led])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.tx_led])))
 
         # TXSAVE
         opts = ["OFF", "ON"]
         power.append(RadioSetting("txsave", "Transmitter Battery Saver",
-                                  RadioSettingValueList(opts, opts[_settings.txsave])))
+                                  RadioSettingValueList(opts, opts[
+                                                        _settings.txsave])))
 
         # VFO.BND
         opts = ["BAND", "ALL"]
         misc.append(RadioSetting("vfo_bnd", "VFO Band Edge Limiting",
-                                 RadioSettingValueList(opts, opts[_settings.vfo_bnd])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.vfo_bnd])))
 
         # WX.ALT
         opts = ["OFF", "ON"]
         scan.append(RadioSetting("wx_alt", "Weather Alert Scan",
-                                 RadioSettingValueList(opts, opts[_settings.wx_alt])))
+                                 RadioSettingValueList(opts, opts[
+                                                       _settings.wx_alt])))
 
         # MBS
         for i in range(0, 10):
diff --git a/chirp/drivers/ft817.py b/chirp/drivers/ft817.py
index 28cd2a2..f34169f 100644
--- a/chirp/drivers/ft817.py
+++ b/chirp/drivers/ft817.py
@@ -33,6 +33,7 @@ CMD_ACK = 0x06
 
 @directory.register
 class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
+
     """Yaesu FT-817"""
     BAUD_RATE = 9600
     MODEL = "FT-817"
@@ -1007,7 +1008,8 @@ class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
         antenna.append(rs)
 
         st = RadioSettingValueString(0, 7, ''.join([self._CALLSIGN_CHARSET[x]
-                                                    for x in self._memobj.callsign]))
+                                                   for x in self._memobj.
+                                                   callsign]))
         st.set_charset(self._CALLSIGN_CHARSET)
         rs = RadioSetting("callsign", "Callsign", st)
         cw.append(rs)
@@ -1084,7 +1086,8 @@ class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
                     setting = element.get_name()
                 try:
                     LOG.debug("Setting %s(%s) <= %s" % (setting,
-                                                        getattr(obj, setting), element.value))
+                                                        getattr(obj, setting),
+                                                        element.value))
                 except AttributeError:
                     LOG.debug("Setting %s <= %s" % (setting, element.value))
                 if setting == "contrast":
@@ -1102,6 +1105,7 @@ class FT817Radio(yaesu_clone.YaesuCloneModeRadio):
 
 @directory.register
 class FT817NDRadio(FT817Radio):
+
     """Yaesu FT-817ND"""
     MODEL = "FT-817ND"
 
@@ -1113,6 +1117,7 @@ class FT817NDRadio(FT817Radio):
 
 @directory.register
 class FT817NDUSRadio(FT817Radio):
+
     """Yaesu FT-817ND (US version)"""
     # seems that radios configured for 5MHz operations send one paket
     # more than others so we have to distinguish sub models
diff --git a/chirp/drivers/kguv8d.py b/chirp/drivers/kguv8d.py
index a119c86..bf92856 100644
--- a/chirp/drivers/kguv8d.py
+++ b/chirp/drivers/kguv8d.py
@@ -276,6 +276,7 @@ _MEM_FORMAT = """
 @directory.register
 class KGUV8DRadio(chirp_common.CloneModeRadio,
                   chirp_common.ExperimentalRadio):
+
     """Wouxun KG-UV8D"""
     VENDOR = "Wouxun"
     MODEL = "KG-UV8D"
@@ -341,7 +342,7 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
 #  31:34    VHF rx upper limit
 #  35:38    VHF tx lower limit
 #  39:42    VHF tx upper limit
-##
+#
     @classmethod
     def match_model(cls, filedata, filename):
         return cls._file_ident in filedata[0x400:0x408]
@@ -475,7 +476,7 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         rf.valid_modes = ["FM", "NFM"]
         rf.valid_power_levels = self.POWER_LEVELS
         rf.valid_name_length = 8
-        rf.valid_duplexes = ["", "+", "-", "split"]
+        rf.valid_duplexes = ["", "-", "+", "split", "off"]
         rf.valid_bands = [(134000000, 175000000),  # supports 2m
                           (400000000, 520000000)]  # supports 70cm
         rf.valid_characters = chirp_common.CHARSET_ASCII
@@ -554,7 +555,11 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
 
         mem.freq = int(_mem.rxfreq) * 10
 
-        if int(_mem.rxfreq) == int(_mem.txfreq):
+        if _mem.txfreq == 0xFFFFFFFF:
+            # TX freq not set
+            mem.duplex = "off"
+            mem.offset = 0
+        elif int(_mem.rxfreq) == int(_mem.txfreq):
             mem.duplex = ""
             mem.offset = 0
         elif abs(int(_mem.rxfreq) * 10 - int(_mem.txfreq) * 10) > 70000000:
@@ -629,7 +634,9 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
             return
 
         _mem.rxfreq = int(mem.freq / 10)
-        if mem.duplex == "split":
+        if mem.duplex == "off":
+            _mem.txfreq = 0xFFFFFFFF
+        elif mem.duplex == "split":
             _mem.txfreq = int(mem.offset / 10)
         elif mem.duplex == "off":
             for i in range(0, 4):
@@ -689,7 +696,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("language", "Language",
                           RadioSettingValueList(LANGUAGE_LIST,
-                                                LANGUAGE_LIST[_settings.language]))
+                                                LANGUAGE_LIST[_settings.
+                                                              language]))
         cfg_grp.append(rs)
         rs = RadioSetting("timeout", "Timeout Timer",
                           RadioSettingValueInteger(15, 900,
@@ -718,11 +726,13 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("backlight", "Backlight",
                           RadioSettingValueList(BACKLIGHT_LIST,
-                                                BACKLIGHT_LIST[_settings.backlight]))
+                                                BACKLIGHT_LIST[_settings.
+                                                               backlight]))
         cfg_grp.append(rs)
         rs = RadioSetting("dtmf_st", "DTMF Sidetone",
                           RadioSettingValueList(DTMFST_LIST,
-                                                DTMFST_LIST[_settings.dtmf_st]))
+                                                DTMFST_LIST[_settings.
+                                                            dtmf_st]))
         cfg_grp.append(rs)
         rs = RadioSetting("ani-id_sw", "ANI-ID Switch",
                           RadioSettingValueBoolean(_settings.ani_sw))
@@ -737,7 +747,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("scan_rev", "Scan Mode",
                           RadioSettingValueList(SCANMODE_LIST,
-                                                SCANMODE_LIST[_settings.scan_rev]))
+                                                SCANMODE_LIST[_settings.
+                                                              scan_rev]))
         cfg_grp.append(rs)
         rs = RadioSetting("vox", "VOX",
                           RadioSettingValueList(LIST_10,
@@ -751,11 +762,13 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("rpt_mode", "Radio Mode",
                           RadioSettingValueList(RPTMODE_LIST,
-                                                RPTMODE_LIST[_settings.rpt_mode]))
+                                                RPTMODE_LIST[_settings.
+                                                             rpt_mode]))
         cfg_grp.append(rs)
         rs = RadioSetting("rpt_set", "Repeater Setting",
                           RadioSettingValueList(RPTSET_LIST,
-                                                RPTSET_LIST[_settings.rpt_set]))
+                                                RPTSET_LIST[_settings.
+                                                            rpt_set]))
         cfg_grp.append(rs)
         rs = RadioSetting("rpt_spk", "Repeater Mode Speaker",
                           RadioSettingValueBoolean(_settings.rpt_spk))
@@ -765,11 +778,13 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("dtmf_tx_time", "DTMF Tx Duration",
                           RadioSettingValueList(DTMF_TIMES,
-                                                DTMF_TIMES[_settings.dtmf_tx_time]))
+                                                DTMF_TIMES[_settings.
+                                                           dtmf_tx_time]))
         cfg_grp.append(rs)
         rs = RadioSetting("dtmf_interval", "DTMF Interval",
                           RadioSettingValueList(DTMF_TIMES,
-                                                DTMF_TIMES[_settings.dtmf_interval]))
+                                                DTMF_TIMES[_settings.
+                                                           dtmf_interval]))
         cfg_grp.append(rs)
         rs = RadioSetting("alert", "Alert Tone",
                           RadioSettingValueList(ALERTS_LIST,
@@ -780,7 +795,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("rpt_hold", "Repeater Hold Time",
                           RadioSettingValueList(HOLD_TIMES,
-                                                HOLD_TIMES[_settings.rpt_hold]))
+                                                HOLD_TIMES[_settings.
+                                                           rpt_hold]))
         cfg_grp.append(rs)
         rs = RadioSetting("scan_det", "Scan DET",
                           RadioSettingValueBoolean(_settings.scan_det))
@@ -791,7 +807,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         cfg_grp.append(rs)
         rs = RadioSetting("smuteset", "SubFreq Mute",
                           RadioSettingValueList(SMUTESET_LIST,
-                                                SMUTESET_LIST[_settings.smuteset]))
+                                                SMUTESET_LIST[_settings.
+                                                              smuteset]))
         cfg_grp.append(rs)
         _pwd = "".join(map(chr, _settings.mode_sw_pwd))
         val = RadioSettingValueString(0, 6, _pwd)
@@ -808,7 +825,8 @@ class KGUV8DRadio(chirp_common.CloneModeRadio,
         #
         rs = RadioSetting("vfoa_mode", "VFO A Workmode",
                           RadioSettingValueList(WORKMODE_LIST,
-                                                WORKMODE_LIST[_settings.workmode_a]))
+                                                WORKMODE_LIST[_settings.
+                                                              workmode_a]))
         vfoa_grp.append(rs)
         rs = RadioSetting("vfoa_chan", "VFO A Channel",
                           RadioSettingValueInteger(1, 999, _settings.work_cha))
diff --git a/chirp/drivers/kyd.py b/chirp/drivers/kyd.py
index 87c46cc..01ec7b3 100644
--- a/chirp/drivers/kyd.py
+++ b/chirp/drivers/kyd.py
@@ -63,7 +63,6 @@ u8 skipflags[2];  // SCAN_ADD
 """
 
 CMD_ACK = "\x06"
-BLOCK_SIZE = 0x08
 
 NC630A_POWER_LEVELS = [chirp_common.PowerLevel("Low",  watts=1.00),
                        chirp_common.PowerLevel("High", watts=5.00)]
@@ -91,8 +90,6 @@ def _nc630a_enter_programming_mode(radio):
     serial = radio.pipe
 
     try:
-        serial.write("\x02")
-        time.sleep(0.1)
         serial.write("PROGRAM")
         ack = serial.read(1)
     except:
@@ -109,7 +106,7 @@ def _nc630a_enter_programming_mode(radio):
     except:
         raise errors.RadioError("Error communicating with radio")
 
-    if not ident.startswith("P32073"):
+    if not ident.startswith(radio._fileid):
         LOG.debug(util.hexprint(ident))
         raise errors.RadioError("Radio returned unknown identification string")
 
@@ -123,24 +120,16 @@ def _nc630a_enter_programming_mode(radio):
         raise errors.RadioError("Radio refused to enter programming mode")
 
 
-def _nc630a_exit_programming_mode(radio):
-    serial = radio.pipe
-    try:
-        serial.write("E")
-    except:
-        raise errors.RadioError("Radio refused to exit programming mode")
-
-
 def _nc630a_read_block(radio, block_addr, block_size):
     serial = radio.pipe
 
-    cmd = struct.pack(">cHb", 'R', block_addr, BLOCK_SIZE)
+    cmd = struct.pack(">cHb", 'R', block_addr, block_size)
     expectedresponse = "W" + cmd[1:]
     LOG.debug("Reading block %04x..." % (block_addr))
 
     try:
         serial.write(cmd)
-        response = serial.read(4 + BLOCK_SIZE)
+        response = serial.read(4 + block_size)
         if response[:4] != expectedresponse:
             raise Exception("Error reading block %04x." % (block_addr))
 
@@ -160,8 +149,8 @@ def _nc630a_read_block(radio, block_addr, block_size):
 def _nc630a_write_block(radio, block_addr, block_size):
     serial = radio.pipe
 
-    cmd = struct.pack(">cHb", 'W', block_addr, BLOCK_SIZE)
-    data = radio.get_mmap()[block_addr:block_addr + 8]
+    cmd = struct.pack(">cHb", 'W', block_addr, block_size)
+    data = radio.get_mmap()[block_addr:block_addr + block_size]
 
     LOG.debug("Writing Data:")
     LOG.debug(util.hexprint(cmd + data))
@@ -187,18 +176,16 @@ def do_download(radio):
     status.cur = 0
     status.max = radio._memsize
 
-    for addr in range(0, radio._memsize, BLOCK_SIZE):
-        status.cur = addr + BLOCK_SIZE
+    for addr in range(0, radio._memsize, radio._block_size):
+        status.cur = addr + radio._block_size
         radio.status_fn(status)
 
-        block = _nc630a_read_block(radio, addr, BLOCK_SIZE)
+        block = _nc630a_read_block(radio, addr, radio._block_size)
         data += block
 
         LOG.debug("Address: %04x" % addr)
         LOG.debug(util.hexprint(block))
 
-    _nc630a_exit_programming_mode(radio)
-
     return memmap.MemoryMap(data)
 
 
@@ -212,25 +199,30 @@ def do_upload(radio):
     status.max = radio._memsize
 
     for start_addr, end_addr in radio._ranges:
-        for addr in range(start_addr, end_addr, BLOCK_SIZE):
-            status.cur = addr + BLOCK_SIZE
+        for addr in range(start_addr, end_addr, radio._block_size):
+            status.cur = addr + radio._block_size
             radio.status_fn(status)
-            _nc630a_write_block(radio, addr, BLOCK_SIZE)
+            _nc630a_write_block(radio, addr, radio._block_size)
 
-    _nc630a_exit_programming_mode(radio)
 
+class MT700Alias(chirp_common.Alias):
+    VENDOR = "Plant-Tours"
+    MODEL = "MT-700"
 
 @directory.register
 class NC630aRadio(chirp_common.CloneModeRadio):
     """KYD NC-630A"""
     VENDOR = "KYD"
     MODEL = "NC-630A"
+    ALIASES = [MT700Alias]
     BAUD_RATE = 9600
 
     _ranges = [
-               (0x0000, 0x0338),
+               (0x0000, 0x0330),
               ]
-    _memsize = 0x0338
+    _memsize = 0x03C8
+    _block_size = 0x08
+    _fileid = "P32073"
 
     def get_features(self):
         rf = chirp_common.RadioFeatures()
@@ -403,9 +395,11 @@ class NC630aRadio(chirp_common.CloneModeRadio):
         _skp = self._memobj.skipflags[bytepos]
 
         if mem.empty:
-            _mem.set_raw("\xFF" * (_mem.size() / 8))
+            _mem.set_raw("\xFF" * 16)
             return
 
+        _mem.set_raw("\x00" * 14 + "\xFF" * 2)
+
         _mem.rxfreq = mem.freq / 10
 
         if mem.duplex == "off":
@@ -504,3 +498,20 @@ class NC630aRadio(chirp_common.CloneModeRadio):
                 except Exception, e:
                     LOG.debug(element.get_name())
                     raise
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        match_size = match_model = False
+
+        # testing the file data size
+        if len(filedata) in [0x338, 0x3C8]:
+            match_size = True
+
+        # testing model fingerprint
+        if filedata[0x01B8:0x01BE] == cls._fileid:
+             match_model = True
+
+        if match_size and match_model:
+            return True
+        else:
+            return False
diff --git a/chirp/drivers/leixen.py b/chirp/drivers/leixen.py
index c484495..74225d4 100644
--- a/chirp/drivers/leixen.py
+++ b/chirp/drivers/leixen.py
@@ -129,9 +129,11 @@ struct channel {
   bbcd rx_freq[4];
   bbcd tx_freq[4];
   u8 rx_tone;
-  u8 rx_tmode;
+  u8 rx_tmode_extra:6,
+     rx_tmode:2;
   u8 tx_tone;
-  u8 tx_tmode;
+  u8 tx_tmode_extra:6,
+     tx_tmode:2;
   u8 unknown5;
   u8 pttidoff:1,
      dtmfoff:1,
@@ -154,12 +156,12 @@ struct name {
     u8 pad;
 };
 
-#seekto 0x0d00;
-struct channel default[3];
+#seekto 0x%(chanstart)x;
+struct channel default[%(defaults)i];
 struct channel memory[199];
 
-#seekto 0x19b0;
-struct name defaultname[3];
+#seekto 0x%(namestart)x;
+struct name defaultname[%(defaults)i];
 struct name name[199];
 """
 
@@ -330,6 +332,7 @@ def do_upload(radio):
     do_ident(radio)
 
     for start, end in _ranges:
+        LOG.debug('Uploading range 0x%04X - 0x%04X' % (start, end))
         for addr in range(start, end, 0x10):
             frame = make_frame("W", addr, radio._mmap[addr:addr + 0x10])
             send(radio, frame)
@@ -362,6 +365,7 @@ class LT898UV(chirp_common.Alias):
 
 @directory.register
 class LeixenVV898Radio(chirp_common.CloneModeRadio):
+
     """Leixen VV-898"""
     VENDOR = "Leixen"
     MODEL = "VV-898"
@@ -383,7 +387,10 @@ class LeixenVV898Radio(chirp_common.CloneModeRadio):
     ]
 
     _mem_formatter = {'unknownormode': 'unknown6:1',
-                      'modeorpower': 'mode:1, power:1'}
+                      'modeorpower': 'mode:1, power:1',
+                      'chanstart': 0x0D00,
+                      'namestart': 0x19B0,
+                      'defaults': 3}
     _power_levels = [chirp_common.PowerLevel("Low", watts=4),
                      chirp_common.PowerLevel("High", watts=10)]
 
@@ -437,8 +444,8 @@ class LeixenVV898Radio(chirp_common.CloneModeRadio):
             raise errors.RadioError("Failed to upload to radio: %s" % e)
 
     def get_raw_memory(self, number):
-        return repr(self._memobj.name[number - 1]) + \
-            repr(self._memobj.memory[number - 1])
+        name, mem = self._get_memobjs(number)
+        return repr(name) + repr(mem)
 
     def _get_tone(self, mem, _mem):
         rx_tone = tx_tone = None
@@ -468,9 +475,13 @@ class LeixenVV898Radio(chirp_common.CloneModeRadio):
             raw_tx += _mem.tx_freq[i].get_raw()
         return raw_tx == "\xFF\xFF\xFF\xFF"
 
-    def get_memory(self, number):
+    def _get_memobjs(self, number):
         _mem = self._memobj.memory[number - 1]
         _name = self._memobj.name[number - 1]
+        return _mem, _name
+
+    def get_memory(self, number):
+        _mem, _name = self._get_memobjs(number)
 
         mem = chirp_common.Memory()
         mem.number = number
@@ -572,8 +583,7 @@ class LeixenVV898Radio(chirp_common.CloneModeRadio):
             _mem.rx_tone = DTCS_CODES.index(rxtone) + 1
 
     def set_memory(self, mem):
-        _mem = self._memobj.memory[mem.number - 1]
-        _name = self._memobj.name[mem.number - 1]
+        _mem, _name = self._get_memobjs(mem.number)
 
         if mem.empty:
             _mem.set_raw("\xFF" * 16)
@@ -944,6 +954,7 @@ class LeixenVV898Radio(chirp_common.CloneModeRadio):
 
 @directory.register
 class JetstreamJT270MRadio(LeixenVV898Radio):
+
     """Jetstream JT270M"""
     VENDOR = "Jetstream"
     MODEL = "JT270M"
@@ -952,14 +963,60 @@ class JetstreamJT270MRadio(LeixenVV898Radio):
     _model_ident = 'LX-\x89\x85\x53'
 
 
+ at directory.register
+class JetstreamJT270MHRadio(LeixenVV898Radio):
+
+    """Jetstream JT270MH"""
+    VENDOR = "Jetstream"
+    MODEL = "JT270MH"
+
+    _file_ident = "Leixen"
+    _model_ident = 'LX-\x89\x85\x85'
+    _ranges = [(0x0C00, 0x2000)]
+    _mem_formatter = {'unknownormode': 'unknown6:1',
+                      'modeorpower': 'mode:1, power:1',
+                      'chanstart': 0x0C00,
+                      'namestart': 0x1930,
+                      'defaults': 6}
+
+    def get_features(self):
+        rf = super(JetstreamJT270MHRadio, self).get_features()
+        rf.has_sub_devices = self.VARIANT == ''
+        rf.memory_bounds = (1, 99)
+        return rf
+
+    def get_sub_devices(self):
+        return [JetstreamJT270MHRadioA(self._mmap),
+                JetstreamJT270MHRadioB(self._mmap)]
+
+    def _get_memobjs(self, number):
+        number = number * 2 - self._offset
+        _mem = self._memobj.memory[number]
+        _name = self._memobj.name[number]
+        return _mem, _name
+
+
+class JetstreamJT270MHRadioA(JetstreamJT270MHRadio):
+    VARIANT = 'A Band'
+    _offset = 1
+
+
+class JetstreamJT270MHRadioB(JetstreamJT270MHRadio):
+    VARIANT = 'B Band'
+    _offset = 2
+
+
 class VV898E(chirp_common.Alias):
-    '''Leixen has called this radio both 898E and S historically, ident is identical'''
+
+    '''Leixen has called this radio both 898E and S historically, ident is
+    identical'''
     VENDOR = "Leixen"
     MODEL = "VV-898E"
 
 
 @directory.register
 class LeixenVV898SRadio(LeixenVV898Radio):
+
     """Leixen VV-898S, also VV-898E which is identical"""
     VENDOR = "Leixen"
     MODEL = "VV-898S"
@@ -967,7 +1024,10 @@ class LeixenVV898SRadio(LeixenVV898Radio):
 
     _model_ident = 'LX-\x89\x85\x75'
     _mem_formatter = {'unknownormode': 'mode:1',
-                      'modeorpower': 'power:2'}
+                      'modeorpower': 'power:2',
+                      'chanstart': 0x0D00,
+                      'namestart': 0x19B0,
+                      'defaults': 3}
     _power_levels = [chirp_common.PowerLevel("Low", watts=5),
                      chirp_common.PowerLevel("Med", watts=10),
                      chirp_common.PowerLevel("High", watts=25)]
diff --git a/chirp/drivers/kyd.py b/chirp/drivers/retevis_rt22.py
similarity index 66%
copy from chirp/drivers/kyd.py
copy to chirp/drivers/retevis_rt22.py
index 87c46cc..a7098c9 100644
--- a/chirp/drivers/kyd.py
+++ b/chirp/drivers/retevis_rt22.py
@@ -1,5 +1,4 @@
-# Copyright 2014 Jim Unroe <rock.unroe at gmail.com>
-# Copyright 2014 Dan Smith <dsmith at danplanet.com>
+# Copyright 2016 Jim Unroe <rock.unroe at gmail.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -23,93 +22,106 @@ from chirp import chirp_common, directory, memmap
 from chirp import bitwise, errors, util
 from chirp.settings import RadioSetting, RadioSettingGroup, \
     RadioSettingValueInteger, RadioSettingValueList, \
-    RadioSettingValueBoolean, RadioSettings
+    RadioSettingValueBoolean, RadioSettings, \
+    RadioSettingValueString
 
 LOG = logging.getLogger(__name__)
 
 MEM_FORMAT = """
 #seekto 0x0010;
 struct {
-  lbcd rxfreq[4];
-  lbcd txfreq[4];
-  ul16 rx_tone;
-  ul16 tx_tone;
-  u8 unknown1:3,
-     bcl:2,       // Busy Lock
-     unknown2:3;
-  u8 unknown3:2,
-     highpower:1, // Power Level
-     wide:1,      // Bandwidth
-     unknown4:4;
+  lbcd rxfreq[4];                
+  lbcd txfreq[4];                
+  ul16 rx_tone;                  
+  ul16 tx_tone;                  
+  u8 unknown1;                   
+  u8 unknown3:2,                 
+     highpower:1, // Power Level 
+     wide:1,      // Bandwidth   
+     unknown4:4;                 
   u8 unknown5[2];
 } memory[16];
 
 #seekto 0x012F;
 struct {
   u8 voice;       // Voice Annunciation
-  u8 tot;         // Time-out Timer
-  u8 totalert;    // Time-out Timer Pre-alert
-  u8 unknown1[2];
+  u8 tot;         // Time-out Timer                   
+  u8 unknown1[3];
   u8 squelch;     // Squelch Level
-  u8 save;        // Battery Saver
-  u8 beep;        // Beep
-  u8 unknown2[3];
-  u8 vox;         // VOX Gain
-  u8 voxdelay;    // VOX Delay
+  u8 save;        // Battery Saver                    
+  u8 beep;        // Beep                             
+  u8 unknown2[2];
+  u8 vox;         // VOX
+  u8 voxgain;     // VOX Gain
+  u8 voxdelay;    // VOX Delay                        
+  u8 unknown3[2];
+  u8 pf2key;      // PF2 Key                          
 } settings;
 
 #seekto 0x017E;
 u8 skipflags[2];  // SCAN_ADD
+
+#seekto 0x0300;
+struct {
+  char line1[32];
+  char line2[32];
+} embedded_msg;
 """
 
 CMD_ACK = "\x06"
-BLOCK_SIZE = 0x08
 
-NC630A_POWER_LEVELS = [chirp_common.PowerLevel("Low",  watts=1.00),
-                       chirp_common.PowerLevel("High", watts=5.00)]
+RT22_POWER_LEVELS = [chirp_common.PowerLevel("Low",  watts=2.00),
+                     chirp_common.PowerLevel("High", watts=5.00)]
 
-NC630A_DTCS = sorted(chirp_common.DTCS_CODES + [645])
+RT22_DTCS = sorted(chirp_common.DTCS_CODES + [645])
 
-BCL_LIST = ["Off", "Carrier", "QT/DQT"]
+PF2KEY_LIST = ["Scan", "Local Alarm", "Remote Alarm"]
 TIMEOUTTIMER_LIST = [""] + ["%s seconds" % x for x in range(15, 615, 15)]
-TOTALERT_LIST = ["", "Off"] + ["%s seconds" % x for x in range(1, 11)]
 VOICE_LIST = ["Off", "Chinese", "English"]
 VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 17)]
-VOXDELAY_LIST = ["0.3", "0.5", "1.0", "1.5", "2.0", "3.0"]
+VOXDELAY_LIST = ["0.5", "1.0", "1.5", "2.0", "2.5", "3.0"]
 
 SETTING_LISTS = {
-    "bcl": BCL_LIST,
+    "pf2key": PF2KEY_LIST,
     "tot": TIMEOUTTIMER_LIST,
-    "totalert": TOTALERT_LIST,
     "voice": VOICE_LIST,
     "vox": VOX_LIST,
     "voxdelay": VOXDELAY_LIST,
     }
 
+VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
+    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
+
 
-def _nc630a_enter_programming_mode(radio):
+def _rt22_enter_programming_mode(radio):
     serial = radio.pipe
 
+    magic = "PROGRGS"
     try:
-        serial.write("\x02")
-        time.sleep(0.1)
-        serial.write("PROGRAM")
+        for j in range(0, len(magic)):
+            time.sleep(0.005)
+            serial.write(magic[j])
         ack = serial.read(1)
     except:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Error communicating with radio")
 
     if not ack:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("No response from radio")
     elif ack != CMD_ACK:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Radio refused to enter programming mode")
 
     try:
         serial.write("\x02")
         ident = serial.read(8)
     except:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Error communicating with radio")
 
     if not ident.startswith("P32073"):
+        _rt22_exit_programming_mode(radio)
         LOG.debug(util.hexprint(ident))
         raise errors.RadioError("Radio returned unknown identification string")
 
@@ -117,13 +129,26 @@ def _nc630a_enter_programming_mode(radio):
         serial.write(CMD_ACK)
         ack = serial.read(1)
     except:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Error communicating with radio")
 
     if ack != CMD_ACK:
+        _rt22_exit_programming_mode(radio)
+        raise errors.RadioError("Radio refused to enter programming mode")
+
+    try:
+        serial.write("\x07")
+        ack = serial.read(1)
+    except:
+        _rt22_exit_programming_mode(radio)
+        raise errors.RadioError("Error communicating with radio")
+
+    if ack != "\x4E":
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Radio refused to enter programming mode")
 
 
-def _nc630a_exit_programming_mode(radio):
+def _rt22_exit_programming_mode(radio):
     serial = radio.pipe
     try:
         serial.write("E")
@@ -131,17 +156,21 @@ def _nc630a_exit_programming_mode(radio):
         raise errors.RadioError("Radio refused to exit programming mode")
 
 
-def _nc630a_read_block(radio, block_addr, block_size):
+def _rt22_read_block(radio, block_addr, block_size):
     serial = radio.pipe
 
-    cmd = struct.pack(">cHb", 'R', block_addr, BLOCK_SIZE)
+    cmd = struct.pack(">cHb", 'R', block_addr, block_size)
     expectedresponse = "W" + cmd[1:]
     LOG.debug("Reading block %04x..." % (block_addr))
 
     try:
-        serial.write(cmd)
-        response = serial.read(4 + BLOCK_SIZE)
+        for j in range(0, len(cmd)):
+            time.sleep(0.005)
+            serial.write(cmd[j])
+
+        response = serial.read(4 + block_size)
         if response[:4] != expectedresponse:
+            _rt22_exit_programming_mode(radio)
             raise Exception("Error reading block %04x." % (block_addr))
 
         block_data = response[4:]
@@ -149,35 +178,43 @@ def _nc630a_read_block(radio, block_addr, block_size):
         serial.write(CMD_ACK)
         ack = serial.read(1)
     except:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Failed to read block at %04x" % block_addr)
 
     if ack != CMD_ACK:
+        _rt22_exit_programming_mode(radio)
         raise Exception("No ACK reading block %04x." % (block_addr))
 
     return block_data
 
 
-def _nc630a_write_block(radio, block_addr, block_size):
+def _rt22_write_block(radio, block_addr, block_size):
     serial = radio.pipe
 
-    cmd = struct.pack(">cHb", 'W', block_addr, BLOCK_SIZE)
-    data = radio.get_mmap()[block_addr:block_addr + 8]
+    cmd = struct.pack(">cHb", 'W', block_addr, block_size)
+    data = radio.get_mmap()[block_addr:block_addr + block_size]
 
     LOG.debug("Writing Data:")
     LOG.debug(util.hexprint(cmd + data))
 
     try:
-        serial.write(cmd + data)
+        for j in range(0, len(cmd)):
+            time.sleep(0.005)
+            serial.write(cmd[j])
+        for j in range(0, len(data)):
+            time.sleep(0.005)
+            serial.write(data[j])
         if serial.read(1) != CMD_ACK:
             raise Exception("No ACK")
     except:
+        _rt22_exit_programming_mode(radio)
         raise errors.RadioError("Failed to send block "
                                 "to radio at %04x" % block_addr)
 
 
 def do_download(radio):
     LOG.debug("download")
-    _nc630a_enter_programming_mode(radio)
+    _rt22_enter_programming_mode(radio)
 
     data = ""
 
@@ -187,17 +224,19 @@ def do_download(radio):
     status.cur = 0
     status.max = radio._memsize
 
-    for addr in range(0, radio._memsize, BLOCK_SIZE):
-        status.cur = addr + BLOCK_SIZE
+    for addr in range(0, radio._memsize, radio._block_size):
+        status.cur = addr + radio._block_size
         radio.status_fn(status)
 
-        block = _nc630a_read_block(radio, addr, BLOCK_SIZE)
+        block = _rt22_read_block(radio, addr, radio._block_size)
         data += block
 
         LOG.debug("Address: %04x" % addr)
         LOG.debug(util.hexprint(block))
 
-    _nc630a_exit_programming_mode(radio)
+    data += radio.MODEL.ljust(8)
+
+    _rt22_exit_programming_mode(radio)
 
     return memmap.MemoryMap(data)
 
@@ -206,31 +245,45 @@ def do_upload(radio):
     status = chirp_common.Status()
     status.msg = "Uploading to radio"
 
-    _nc630a_enter_programming_mode(radio)
+    _rt22_enter_programming_mode(radio)
 
     status.cur = 0
     status.max = radio._memsize
 
-    for start_addr, end_addr in radio._ranges:
-        for addr in range(start_addr, end_addr, BLOCK_SIZE):
-            status.cur = addr + BLOCK_SIZE
+    for start_addr, end_addr, block_size in radio._ranges:
+        for addr in range(start_addr, end_addr, block_size):
+            status.cur = addr + block_size
             radio.status_fn(status)
-            _nc630a_write_block(radio, addr, BLOCK_SIZE)
+            _rt22_write_block(radio, addr, block_size)
+
+    _rt22_exit_programming_mode(radio)
 
-    _nc630a_exit_programming_mode(radio)
+
+def model_match(cls, data):
+    """Match the opened/downloaded image to the correct version"""
+
+    if len(data) == 0x0408:
+        rid = data[0x0400:0x0408]
+        return rid.startswith(cls.MODEL)
+    else:
+        return False
 
 
 @directory.register
-class NC630aRadio(chirp_common.CloneModeRadio):
-    """KYD NC-630A"""
-    VENDOR = "KYD"
-    MODEL = "NC-630A"
+class RT22Radio(chirp_common.CloneModeRadio):
+    """Retevis RT22"""
+    VENDOR = "Retevis"
+    MODEL = "RT22"
     BAUD_RATE = 9600
 
     _ranges = [
-               (0x0000, 0x0338),
+               (0x0000, 0x0180, 0x10),
+               (0x01B8, 0x01F8, 0x10),
+               (0x01F8, 0x0200, 0x08),
+               (0x0200, 0x0340, 0x10),
               ]
-    _memsize = 0x0338
+    _memsize = 0x0400
+    _block_size = 0x40
 
     def get_features(self):
         rf = chirp_common.RadioFeatures()
@@ -246,7 +299,7 @@ class NC630aRadio(chirp_common.CloneModeRadio):
         rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
         rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
                                 "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
-        rf.valid_power_levels = NC630A_POWER_LEVELS
+        rf.valid_power_levels = RT22_POWER_LEVELS
         rf.valid_duplexes = ["", "-", "+", "split", "off"]
         rf.valid_modes = ["NFM", "FM"]  # 12.5 KHz, 25 kHz.
         rf.memory_bounds = (1, 16)
@@ -344,18 +397,11 @@ class NC630aRadio(chirp_common.CloneModeRadio):
 
         self._get_tone(_mem, mem)
 
-        mem.power = NC630A_POWER_LEVELS[_mem.highpower]
+        mem.power = RT22_POWER_LEVELS[_mem.highpower]
 
         mem.skip = "" if (_skp & bitpos) else "S"
         LOG.debug("mem.skip %s" % mem.skip)
 
-        mem.extra = RadioSettingGroup("Extra", "extra")
-
-        rs = RadioSetting("bcl", "Busy Channel Lockout",
-                          RadioSettingValueList(
-                              BCL_LIST, BCL_LIST[_mem.bcl]))
-        mem.extra.append(rs)
-
         return mem
 
     def _set_tone(self, mem, _mem):
@@ -424,7 +470,7 @@ class NC630aRadio(chirp_common.CloneModeRadio):
 
         self._set_tone(mem, _mem)
 
-        _mem.highpower = mem.power == NC630A_POWER_LEVELS[1]
+        _mem.highpower = mem.power == RT22_POWER_LEVELS[1]
 
         if mem.skip != "S":
             _skp |= bitpos
@@ -432,52 +478,72 @@ class NC630aRadio(chirp_common.CloneModeRadio):
             _skp &= ~bitpos
         LOG.debug("_skp %s" % _skp)
 
-        for setting in mem.extra:
-            setattr(_mem, setting.get_name(), setting.value)
-
     def get_settings(self):
         _settings = self._memobj.settings
+        _message = self._memobj.embedded_msg
         basic = RadioSettingGroup("basic", "Basic Settings")
         top = RadioSettings(basic)
 
+        rs = RadioSetting("squelch", "Squelch Level",
+                          RadioSettingValueInteger(0, 9, _settings.squelch))
+        basic.append(rs)
+
         rs = RadioSetting("tot", "Time-out timer",
                           RadioSettingValueList(
                               TIMEOUTTIMER_LIST,
                               TIMEOUTTIMER_LIST[_settings.tot]))
         basic.append(rs)
 
-        rs = RadioSetting("totalert", "TOT Pre-alert",
+        rs = RadioSetting("voice", "Voice Prompts",
                           RadioSettingValueList(
-                              TOTALERT_LIST,
-                              TOTALERT_LIST[_settings.totalert]))
+                              VOICE_LIST, VOICE_LIST[_settings.voice]))
         basic.append(rs)
 
-        rs = RadioSetting("vox", "VOX Gain",
+        rs = RadioSetting("pf2key", "PF2 Key",
                           RadioSettingValueList(
-                              VOX_LIST, VOX_LIST[_settings.vox]))
+                              PF2KEY_LIST, PF2KEY_LIST[_settings.pf2key]))
         basic.append(rs)
 
-        rs = RadioSetting("voice", "Voice Annumciation",
-                          RadioSettingValueList(
-                              VOICE_LIST, VOICE_LIST[_settings.voice]))
+        rs = RadioSetting("vox", "Vox",
+                          RadioSettingValueBoolean(_settings.vox))
         basic.append(rs)
 
-        rs = RadioSetting("squelch", "Squelch Level",
-                          RadioSettingValueInteger(0, 9, _settings.squelch))
+        rs = RadioSetting("voxgain", "VOX Level",
+                          RadioSettingValueList(
+                              VOX_LIST, VOX_LIST[_settings.voxgain]))
         basic.append(rs)
 
-        rs = RadioSetting("voxdelay", "VOX Delay",
+        rs = RadioSetting("voxdelay", "VOX Delay Time",
                           RadioSettingValueList(
                               VOXDELAY_LIST,
                               VOXDELAY_LIST[_settings.voxdelay]))
         basic.append(rs)
 
+        rs = RadioSetting("save", "Battery Save",
+                          RadioSettingValueBoolean(_settings.save))
+        basic.append(rs)
+
         rs = RadioSetting("beep", "Beep",
                           RadioSettingValueBoolean(_settings.beep))
         basic.append(rs)
 
-        rs = RadioSetting("save", "Battery Saver",
-                          RadioSettingValueBoolean(_settings.save))
+        def _filter(name):
+            filtered = ""
+            for char in str(name):
+                if char in VALID_CHARS:
+                    filtered += char
+                else:
+                    filtered += " "
+            return filtered
+
+        rs = RadioSetting("embedded_msg.line1", "Embedded Message 1",
+                          RadioSettingValueString(0, 32, _filter(
+                              _message.line1)))
+        basic.append(rs)
+
+        rs = RadioSetting("embedded_msg.line2", "Embedded Message 2",
+                          RadioSettingValueString(0, 32, _filter(
+                              _message.line2)))
         basic.append(rs)
 
         return top
@@ -504,3 +570,32 @@ class NC630aRadio(chirp_common.CloneModeRadio):
                 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) in [0x0408, ]:
+            match_size = True
+        
+        # testing the model fingerprint
+        match_model = model_match(cls, filedata)
+
+        if match_size and match_model:
+            return True
+        else:
+            return False
+
+ at directory.register
+class KDC1(RT22Radio):
+    """WLN KD-C1"""
+    VENDOR = "WLN"
+    MODEL = "KD-C1"
+
+ at directory.register
+class ZTX6(RT22Radio):
+    """Zastone ZT-X6"""
+    VENDOR = "Zastone"
+    MODEL = "ZT-X6"
diff --git a/chirp/drivers/thd72.py b/chirp/drivers/thd72.py
index c0a0cc7..2cf978f 100644
--- a/chirp/drivers/thd72.py
+++ b/chirp/drivers/thd72.py
@@ -189,6 +189,7 @@ EXCH_W = "W\x00\x00\x00\x00"
 
 @directory.register
 class THD72Radio(chirp_common.CloneModeRadio):
+
     BAUD_RATE = 9600
     VENDOR = "Kenwood"
     MODEL = "TH-D72 (clone mode)"
@@ -202,8 +203,9 @@ class THD72Radio(chirp_common.CloneModeRadio):
     _LCD_CONTRAST = ["Level %d" % x for x in range(1, 16)]
     _LAMP_CONTROL = ["Manual", "Auto"]
     _LAMP_TIMER = ["Seconds %d" % x for x in range(2, 11)]
-    _BATTERY_SAVER = ["OFF", "0.03 Seconds", "0.2 Seconds", "0.4 Seconds", "0.6 Seconds",
-                      "0.8 Seconds", "1 Seconds", "2 Seconds", "3 Seconds", "4 Seconds", "5 Seconds"]
+    _BATTERY_SAVER = ["OFF", "0.03 Seconds", "0.2 Seconds", "0.4 Seconds",
+                      "0.6 Seconds", "0.8 Seconds", "1 Seconds", "2 Seconds",
+                      "3 Seconds", "4 Seconds", "5 Seconds"]
     _APO = ["OFF", "15 Minutes", "30 Minutes", "60 Minutes"]
     _AUDIO_BALANCE = ["Center", "A +50%", "A +100%", "B +50%", "B +100%"]
     _KEY_BEEP = ["OFF", "Radio & GPS", "Radio Only", "GPS Only"]
diff --git a/chirp/drivers/uv5r.py b/chirp/drivers/uv5r.py
index e2ebc7f..8a2f9ee 100644
--- a/chirp/drivers/uv5r.py
+++ b/chirp/drivers/uv5r.py
@@ -289,8 +289,8 @@ BASETYPE_KT980HP = ["BFP3V3 B"]
 BASETYPE_F8HP = ["BFP3V3 F", "N5R-3", "N5R3", "F5R3", "BFT"]
 BASETYPE_UV82HP = ["N82-3", "N823"]
 BASETYPE_LIST = BASETYPE_UV5R + BASETYPE_F11 + BASETYPE_UV82 + \
-                BASETYPE_BJ55 + BASETYPE_UV6 + BASETYPE_KT980HP + \
-                BASETYPE_F8HP + BASETYPE_UV82HP
+    BASETYPE_BJ55 + BASETYPE_UV6 + BASETYPE_KT980HP + \
+    BASETYPE_F8HP + BASETYPE_UV82HP
 
 AB_LIST = ["A", "B"]
 ALMOD_LIST = ["Site", "Tone", "Code"]
@@ -607,6 +607,7 @@ UV5R_CHARSET = chirp_common.CHARSET_UPPER_NUMERIC + \
 
 class BaofengUV5R(chirp_common.CloneModeRadio,
                   chirp_common.ExperimentalRadio):
+
     """Baofeng UV-5R"""
     VENDOR = "Baofeng"
     MODEL = "UV-5R"
@@ -860,6 +861,20 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
             _nam.set_raw("\xff" * 16)
             return
 
+        was_empty = False
+        # same method as used in get_memory to find
+        # out whether a raw memory is empty
+        if _mem.get_raw()[0] == "\xff":
+            was_empty = True
+            LOG.debug("UV5R: this mem was empty")
+        else:
+            # memorize old extra-values before erasing the whole memory
+            # used to solve issue 4121
+            LOG.debug("mem was not empty, memorize extra-settings")
+            prev_bcl = _mem.bcl.get_value()
+            prev_scode = _mem.scode.get_value()
+            prev_pttid = _mem.pttid.get_value()
+
         _mem.set_raw("\x00" * 16)
 
         _mem.rxfreq = mem.freq / 10
@@ -929,6 +944,12 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
         else:
             _mem.lowpower = 0
 
+        if not was_empty:
+            # restoring old extra-settings (issue 4121
+            _mem.bcl.set_value(prev_bcl)
+            _mem.scode.set_value(prev_scode)
+            _mem.pttid.set_value(prev_pttid)
+
         for setting in mem.extra:
             setattr(_mem, setting.get_name(), setting.value)
 
@@ -1341,14 +1362,14 @@ class BaofengUV5R(chirp_common.CloneModeRadio,
                     value /= 10
 
             val1a = RadioSettingValueString(
-                        0, 10, convert_bytes_to_offset(_vfoa.offset))
+                0, 10, convert_bytes_to_offset(_vfoa.offset))
             rs = RadioSetting("vfoa.offset",
                               "VFO A Offset (0.00-69.95)", val1a)
             rs.set_apply_callback(apply_offset, _vfoa)
             workmode.append(rs)
 
             val1b = RadioSettingValueString(
-                        0, 10, convert_bytes_to_offset(_vfob.offset))
+                0, 10, convert_bytes_to_offset(_vfob.offset))
             rs = RadioSetting("vfob.offset",
                               "VFO B Offset (0.00-69.95)", val1b)
             rs.set_apply_callback(apply_offset, _vfob)
@@ -1644,6 +1665,7 @@ class BaofengUV82Radio(BaofengUV5R):
 
 @directory.register
 class BaofengUV6Radio(BaofengUV5R):
+
     """Baofeng UV-6/UV-7"""
     VENDOR = "Baofeng"
     MODEL = "UV-6"
diff --git a/chirp/drivers/uv5x3.py b/chirp/drivers/uv5x3.py
index 9246121..1ca42a7 100644
--- a/chirp/drivers/uv5x3.py
+++ b/chirp/drivers/uv5x3.py
@@ -71,6 +71,8 @@ LIST_TIMEOUT = ["%s sec" % x for x in range(15, 615, 15)]
 LIST_TXPOWER = ["High", "Low"]
 LIST_VOICE = ["Off", "English", "Chinese"]
 LIST_WORKMODE = ["Frequency", "Channel"]
+LIST_DTMF_SPECIAL_DIGITS = [ "*", "#", "A", "B", "C", "D"]
+LIST_DTMF_SPECIAL_VALUES = [ 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00]
 
 def model_match(cls, data):
     """Match the opened/downloaded image to the correct version"""
@@ -1119,6 +1121,33 @@ class UV5X3(baofeng_common.BaofengCommonHT):
         rs.set_apply_callback(apply_code, self._memobj.ani)
         dtmfd.append(rs)
 
+        def apply_dmtf_listvalue(setting, obj):
+            LOG.debug("Setting value: "+ str(setting.value) + " from list")
+            val = str(setting.value)
+            index = LIST_DTMF_SPECIAL_DIGITS.index(val)
+            val = LIST_DTMF_SPECIAL_VALUES[index]
+            obj.set_value(val)
+
+        if _mem.ani.groupcode in LIST_DTMF_SPECIAL_VALUES:
+            idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.ani.groupcode)
+        else:
+            idx = LIST_DTMF_SPECIAL_VALUES.index(0x0B)
+        rs = RadioSetting("ani.groupcode", "Group Code",
+                          RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+                                                LIST_DTMF_SPECIAL_DIGITS[idx]))
+        rs.set_apply_callback(apply_dmtf_listvalue, _mem.ani.groupcode)
+        dtmfd.append(rs)
+
+        if _mem.ani.spacecode in LIST_DTMF_SPECIAL_VALUES:
+            idx = LIST_DTMF_SPECIAL_VALUES.index(_mem.ani.spacecode)
+        else:
+            idx = LIST_DTMF_SPECIAL_VALUES.index(0x0C)
+        rs = RadioSetting("ani.spacecode", "Space Code",
+                          RadioSettingValueList(LIST_DTMF_SPECIAL_DIGITS,
+                                                LIST_DTMF_SPECIAL_DIGITS[idx]))
+        rs.set_apply_callback(apply_dmtf_listvalue, _mem.ani.spacecode)
+        dtmfd.append(rs)
+
         if _mem.ani.resettime > 0x9F:
             val = 0x4F
         else:
diff --git a/chirp/settings.py b/chirp/settings.py
index dd690ad..210e046 100644
--- a/chirp/settings.py
+++ b/chirp/settings.py
@@ -17,16 +17,19 @@ from chirp import chirp_common
 
 
 class InvalidValueError(Exception):
+
     """An invalid value was specified for a given setting"""
     pass
 
 
 class InternalError(Exception):
+
     """A driver provided an invalid settings object structure"""
     pass
 
 
 class RadioSettingValue:
+
     """Base class for a single radio setting"""
 
     def __init__(self):
@@ -69,6 +72,7 @@ class RadioSettingValue:
 
 
 class RadioSettingValueInteger(RadioSettingValue):
+
     """An integer setting"""
 
     def __init__(self, minval, maxval, current, step=1):
@@ -102,6 +106,7 @@ class RadioSettingValueInteger(RadioSettingValue):
 
 
 class RadioSettingValueFloat(RadioSettingValue):
+
     """A floating-point setting"""
 
     def __init__(self, minval, maxval, current, resolution=0.001, precision=4):
@@ -142,6 +147,7 @@ class RadioSettingValueFloat(RadioSettingValue):
 
 
 class RadioSettingValueBoolean(RadioSettingValue):
+
     """A boolean setting"""
 
     def __init__(self, current):
@@ -160,6 +166,7 @@ class RadioSettingValueBoolean(RadioSettingValue):
 
 
 class RadioSettingValueList(RadioSettingValue):
+
     """A list-of-strings setting"""
 
     def __init__(self, options, current):
@@ -181,6 +188,7 @@ class RadioSettingValueList(RadioSettingValue):
 
 
 class RadioSettingValueString(RadioSettingValue):
+
     """A string setting"""
 
     def __init__(self, minlength, maxlength, current,
@@ -213,6 +221,7 @@ class RadioSettingValueString(RadioSettingValue):
 
 
 class RadioSettingValueMap(RadioSettingValueList):
+
     """Map User Options to Radio Memory Values
 
     Provides User Option list for GUI, maintains state, verifies new values,
@@ -253,7 +262,8 @@ class RadioSettingValueMap(RadioSettingValueList):
                 "%s is not valid for this setting" % mem_val)
 
     def get_mem_val(self):
-        """Get the mem val corresponding to the currently selected user option"""
+        """Get the mem val corresponding to the currently selected user
+        option"""
         return self._mem_vals[self._options.index(self.get_value())]
 
     def __trunc__(self):
@@ -286,6 +296,7 @@ class RadioSettings(list):
 
 
 class RadioSettingGroup(object):
+
     """A group of settings"""
 
     def _validate(self, element):
@@ -331,6 +342,7 @@ class RadioSettingGroup(object):
 
     def __iter__(self):
         class RSGIterator:
+
             """Iterator for a RadioSettingGroup"""
 
             def __init__(self, rsg):
@@ -377,6 +389,7 @@ class RadioSettingGroup(object):
 
 
 class RadioSetting(RadioSettingGroup):
+
     """A single setting, which could be an array of items like a group"""
 
     def __init__(self, *args):
diff --git a/chirp/ui/mainapp.py b/chirp/ui/mainapp.py
index 1d0156c..23ac948 100644
--- a/chirp/ui/mainapp.py
+++ b/chirp/ui/mainapp.py
@@ -1376,7 +1376,8 @@ of file.
 
     def do_change_language(self):
         langs = ["Auto", "English", "Polish", "Italian", "Dutch", "German",
-                 "Hungarian", "Russian", "Portuguese (BR)", "French", "Spanish"]
+                 "Hungarian", "Russian", "Portuguese (BR)", "French",
+                 "Spanish"]
         d = inputdialog.ChoiceDialog(langs, parent=self,
                                      title="Choose Language")
         d.label.set_text(_("Choose a language or Auto to use the "
diff --git a/chirp/ui/settingsedit.py b/chirp/ui/settingsedit.py
index 4d50048..289c796 100644
--- a/chirp/ui/settingsedit.py
+++ b/chirp/ui/settingsedit.py
@@ -25,12 +25,14 @@ LOG = logging.getLogger(__name__)
 
 
 class RadioSettingProxy(settings.RadioSetting):
+
     def __init__(self, setting, editor):
         self._setting = setting
         self._editor = editor
 
 
 class SettingsEditor(common.Editor):
+
     def __init__(self, rthread):
         super(SettingsEditor, self).__init__(rthread)
 
@@ -45,12 +47,16 @@ class SettingsEditor(common.Editor):
         # The selection tree
         self._store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_INT)
         self._view = gtk.TreeView(self._store)
-        self._view.set_size_request(150, -1)
         self._view.get_selection().connect("changed", self._view_changed_cb)
         self._view.append_column(
             gtk.TreeViewColumn("", gtk.CellRendererText(), text=0))
         self._view.show()
-        paned.pack1(self._view)
+        scrolled_window = gtk.ScrolledWindow()
+        scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolled_window.add_with_viewport(self._view)
+        scrolled_window.set_size_request(200, -1)
+        scrolled_window.show()
+        paned.pack1(scrolled_window)
 
         # The settings notebook
         self._notebook = gtk.Notebook()

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