[chirp_devel] [PATCH] [KT7900D] Add Support for QYT KT7900D and KT8900D Mobile Radios

Jim Unroe
Sun Feb 19 16:16:39 PST 2017


# HG changeset patch
# User Jim Unroe <rock.unroe at gmail.com>
# Date 1487549731 18000
# Node ID 90b02a91d80c80d843f3c1de6ff7dc5470030f3b
# Parent  0611c6c740ab74c0d939634765a52ebc2b5a8497
[KT7900D] Add Support for QYT KT7900D and KT8900D Mobile Radios

This patch modifies the btech.py driver to prepare it to support a
new series of mobile radios that have an OLED color display.

Radio models initially supported:
QYT KT7900D (quad band)
QYT KT8900D (dual band)

New Model #4269

diff -r 0611c6c740ab -r 90b02a91d80c chirp/drivers/btech.py
--- a/chirp/drivers/btech.py	Thu Feb 16 18:28:49 2017 -0800
+++ b/chirp/drivers/btech.py	Sun Feb 19 19:15:31 2017 -0500
@@ -1,4 +1,4 @@
-# Copyright 2016:
+# Copyright 2016-2017:
 # * Pavel Milanes CO7WT, <pavelmc at gmail.com>
 # * Jim Unroe KC9HI, <rock.unroe at gmail.com>
 #
@@ -30,6 +30,2710 @@
     RadioSettingValueFloat, RadioSettings, InvalidValueError
 from textwrap import dedent
 
+# A note about the memmory in these radios
+#
+# The real memory of these radios extends to 0x4000
+# On read the factory software only uses up to 0x3200
+# On write it just uploads the contents up to 0x3100
+#
+# The mem beyond 0x3200 holds the ID data
+
+MEM_SIZE = 0x4000
+BLOCK_SIZE = 0x40
+TX_BLOCK_SIZE = 0x10
+ACK_CMD = "\x06"
+MODES = ["FM", "NFM"]
+SKIP_VALUES = ["S", ""]
+TONES = chirp_common.TONES
+DTCS = sorted(chirp_common.DTCS_CODES + [645])
+
+# lists related to "extra" settings
+PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
+PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
+OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
+SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
+
+# lists
+LIST_AB = ["A", "B"]
+LIST_ABCD = LIST_AB + ["C", "D"]
+LIST_ANIL = ["3", "4", "5"]
+LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
+LIST_COLOR4 = ["Off", "Blue", "Orange", "Purple"]
+LIST_COLOR8 = ["Black", "White", "Red", "Blue", "Green", "Yellow", "Indego",
+               "Purple", "Gray"]
+LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
+LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
+LIST_EMCTPX = ["Off"] + LIST_EMCTP
+LIST_LANGUA = ["English", "Chinese"]
+LIST_MDF = ["Frequency", "Channel", "Name"]
+LIST_OFF1TO9 = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
+LIST_OFF1TO10 = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
+LIST_OFF1TO50 = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
+LIST_PONMSG = ["Full", "Message", "Battery voltage"]
+LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
+LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
+LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
+LIST_SCMODE = ["Off", "PTT-SC", "MEM-SC", "PON-SC"]
+LIST_SHIFT = ["Off", "+", "-"]
+LIST_SKIPTX = ["Off", "Skip 1", "Skip 2"]
+STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0]
+LIST_STEP = [str(x) for x in STEPS]
+LIST_SYNC = ["Off", "AB", "CD", "AB+CD"]
+LIST_TMR = ["OFF", "M+A", "M+B", "M+C", "M+D", "M+A+B", "M+A+C", "M+A+D",
+            "M+B+C", "M+B+D", "M+C+D", "M+A+B+C", "M+A+B+D", "M+A+C+D",
+            "M+B+C+D", "A+B+C+D"]
+LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
+LIST_TXDISP = ["Power", "Mic Volume"]
+LIST_TXP = ["High", "Low"]
+LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
+LIST_VFOMR = ["Frequency", "Channel"]
+LIST_WIDE = ["Wide", "Narrow"]
+
+# lists related to DTMF, 2TONE and 5TONE settings
+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_5TONE_RESET_COLOR = ["%s ms" % x for x in range(100, 20100, 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.
+STIMEOUT = 0.7
+
+# this var controls the verbosity in the debug and by default it's low (False)
+# make it True and you will to get a very verbose debug.log
+debug = False
+
+# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
+VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
+    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
+
+
+##### ID strings #####################################################
+
+# BTECH UV2501 pre-production units
+UV2501pp_fp = "M2C294"
+# BTECH UV2501 pre-production units 2 + and 1st Gen radios
+UV2501pp2_fp = "M29204"
+# B-TECH UV-2501 second generation (2G) radios
+UV2501G2_fp = "BTG214"
+# B-TECH UV-2501 third generation (3G) radios
+UV2501G3_fp = "BTG324"
+
+# B-TECH UV-2501+220 pre-production units
+UV2501_220pp_fp = "M3C281"
+# extra block read for the 2501+220 pre-production units
+# the same for all of this radios so far
+UV2501_220pp_id = "      280528"
+# B-TECH UV-2501+220
+UV2501_220_fp = "M3G201"
+# new variant, let's call it Generation 2
+UV2501_220G2_fp = "BTG211"
+# B-TECH UV-2501+220 third generation (3G)
+UV2501_220G3_fp = "BTG311"
+
+# B-TECH UV-5001 pre-production units + 1st Gen radios
+UV5001pp_fp = "V19204"
+# B-TECH UV-5001 alpha units
+UV5001alpha_fp = "V28204"
+# B-TECH UV-5001 second generation (2G) radios
+UV5001G2_fp = "BTG214"
+# B-TECH UV-5001 second generation (2G2)
+UV5001G22_fp = "V2G204"
+# B-TECH UV-5001 third generation (3G)
+UV5001G3_fp = "BTG304"
+
+# special var to know when we found a BTECH Gen 3
+BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
+
+
+# WACCOM Mini-8900
+MINI8900_fp = "M28854"
+
+
+# QYT KT-UV980
+KTUV980_fp = "H28854"
+
+# QYT KT8900
+KT8900_fp = "M29154"
+# New generations KT8900
+KT8900_fp1 = "M2C234"
+KT8900_fp2 = "M2G1F4"
+KT8900_fp3 = "M2G2F4"
+KT8900_fp4 = "M2G304"
+KT8900_fp5 = "M2G314"
+# this radio has an extra ID
+KT8900_id = "      303688"
+
+# KT8900R
+KT8900R_fp = "M3G1F4"
+# Second Generation
+KT8900R_fp1 = "M3G214"
+# another model
+KT8900R_fp2 = "M3C234"
+# another model G4?
+KT8900R_fp3 = "M39164"
+# another model
+KT8900R_fp4 = "M3G314"
+# this radio has an extra ID
+KT8900R_id = "280528"
+
+# KT7900D (quad band)
+KT7900D_fp = "VC4004"
+
+# KT8900D (dual band)
+KT8900D_fp = "VC2002"
+
+# LUITON LT-588UV
+LT588UV_fp = "V2G1F4"
+# Added by rstrickoff gen 2 id
+LT588UV_fp1 = "V2G214"
+
+
+#### MAGICS
+# for the Waccom Mini-8900
+MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
+# for the B-TECH UV-2501+220 (including pre production ones)
+MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
+# for the QYT KT8900 & R
+MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
+MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
+# magic string for all other models
+MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
+# for the QYT KT7900D & KT8900D
+MSTRING_KT8900D = "\x55\x20\x16\x08\x01\xFF\xDC\x02"
+
+
+def _clean_buffer(radio):
+    """Cleaning the read serial buffer, hard timeout to survive an infinite
+    data stream"""
+
+    # touching the serial timeout to optimize the flushing
+    # restored at the end to the default value
+    radio.pipe.timeout = 0.1
+    dump = "1"
+    datacount = 0
+
+    try:
+        while len(dump) > 0:
+            dump = radio.pipe.read(100)
+            datacount += len(dump)
+            # hard limit to survive a infinite serial data stream
+            # 5 times bigger than a normal rx block (69 bytes)
+            if datacount > 345:
+                seriale = "Please check your serial port selection."
+                raise errors.RadioError(seriale)
+
+        # restore the default serial timeout
+        radio.pipe.timeout = STIMEOUT
+
+    except Exception:
+        raise errors.RadioError("Unknown error cleaning the serial buffer")
+
+
+def _rawrecv(radio, amount):
+    """Raw read from the radio device, less intensive way"""
+
+    data = ""
+
+    try:
+        data = radio.pipe.read(amount)
+
+        # DEBUG
+        if debug is True:
+            LOG.debug("<== (%d) bytes:\n\n%s" %
+                      (len(data), util.hexprint(data)))
+
+        # fail if no data is received
+        if len(data) == 0:
+            raise errors.RadioError("No data received from radio")
+
+        # notice on the logs if short
+        if len(data) < amount:
+            LOG.warn("Short reading %d bytes from the %d requested." %
+                     (len(data), amount))
+
+    except:
+        raise errors.RadioError("Error reading data from radio")
+
+    return data
+
+
+def _send(radio, data):
+    """Send data to the radio device"""
+
+    try:
+        for byte in data:
+            radio.pipe.write(byte)
+            # Some OS (mainly Linux ones) are too fast on the serial and
+            # get the MCU inside the radio stuck in the early stages, this
+            # hits some models more than others.
+            #
+            # To cope with that we introduce a delay on the writes.
+            # Many option have been tested (delaying only after error occures, 
+            # after short reads, only for linux, ...)
+            # Finally, a static delay was chosen as simplest of all solutions
+            # (Michael Wagner, OE4AMW)
+            # (for details, see issue 3993)
+            sleep(0.002)
+
+        # DEBUG
+        if debug is True:
+            LOG.debug("==> (%d) bytes:\n\n%s" %
+                      (len(data), util.hexprint(data)))
+    except:
+        raise errors.RadioError("Error sending data to radio")
+
+
+def _make_frame(cmd, addr, length, data=""):
+    """Pack the info in the headder format"""
+    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
+    # add the data if set
+    if len(data) != 0:
+        frame += data
+
+    return frame
+
+
+def _recv(radio, addr):
+    """Get data from the radio all at once to lower syscalls load"""
+
+    # Get the full 69 bytes at a time to reduce load
+    # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
+
+    # get the whole block
+    block = _rawrecv(radio, BLOCK_SIZE + 5)
+
+    # basic check
+    if len(block) < (BLOCK_SIZE + 5):
+        raise errors.RadioError("Short read of the block 0x%04x" % addr)
+
+    # checking for the ack
+    if block[0] != ACK_CMD:
+        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
+
+    # header validation
+    c, a, l = struct.unpack(">BHB", block[1:5])
+    if a != addr or l != BLOCK_SIZE or c != ord("X"):
+        LOG.debug("Invalid header for block 0x%04x" % addr)
+        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
+        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
+
+    # return the data
+    return block[5:]
+
+
+def _start_clone_mode(radio, status):
+    """Put the radio in clone mode and get the ident string, 3 tries"""
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    # prep the data to show in the UI
+    status.cur = 0
+    status.msg = "Identifying the radio..."
+    status.max = 3
+    radio.status_fn(status)
+
+    try:
+        for a in range(0, status.max):
+            # Update the UI
+            status.cur = a + 1
+            radio.status_fn(status)
+
+            # send the magic word
+            _send(radio, radio._magic)
+
+            # Now you get a x06 of ACK if all goes well
+            ack = radio.pipe.read(1)
+
+            if ack == "\x06":
+                # DEBUG
+                LOG.info("Magic ACK received")
+                status.cur = status.max
+                radio.status_fn(status)
+
+                return True
+
+        return False
+
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
+
+
+def _do_ident(radio, status, upload=False):
+    """Put the radio in PROGRAM mode & identify it"""
+    #  set the serial discipline
+    radio.pipe.baudrate = 9600
+    radio.pipe.parity = "N"
+
+    # open the radio into program mode
+    if _start_clone_mode(radio, status) is False:
+        msg = "Radio did not enter clone mode"
+        # warning about old versions of QYT KT8900
+        if radio.MODEL == "KT8900":
+            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
+            msg += " known variant of this radios that is a clone of it."
+        raise errors.RadioError(msg)
+
+    # Ok, get the ident string
+    ident = _rawrecv(radio, 49)
+
+    # basic check for the ident
+    if len(ident) != 49:
+        raise errors.RadioError("Radio send a short ident block.")
+
+    # check if ident is OK
+    itis = False
+    for fp in radio._fileid:
+        if fp in ident:
+            # got it!
+            itis = True
+            # checking if we are dealing with a Gen 3 BTECH
+            if radio.VENDOR == "BTECH" and fp in BTECH3:
+                radio.btech3 = True
+
+            break
+
+    if itis is False:
+        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
+        raise errors.RadioError("Radio identification failed.")
+
+    # some radios needs a extra read and check for a code on it, this ones
+    # has the check value in the _id2 var, others simply False
+    if radio._id2 is not False:
+        # lower the timeout here as this radios are reseting due to timeout
+        radio.pipe.timeout = 0.05
+
+        # query & receive the extra ID
+        _send(radio, _make_frame("S", 0x3DF0, 16))
+        id2 = _rawrecv(radio, 21)
+
+        # WARNING !!!!!!
+        # different radios send a response with a different amount of data
+        # it seems that it's padded with \xff, \x20 and some times with \x00
+        # we just care about the first 16, our magic string is in there
+        if len(id2) < 16:
+            raise errors.RadioError("The extra ID is short, aborting.")
+
+        # ok, the correct string must be in the received data
+        if radio._id2 not in id2:
+            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
+                      (radio.MODEL, util.hexprint(id2)))
+            raise errors.RadioError("The extra ID is wrong, aborting.")
+
+        # this radios need a extra request/answer here on the upload
+        # the amount of data received depends of the radio type
+        #
+        # also the first block of TX must no have the ACK at the beginning
+        # see _upload for this.
+        if upload is True:
+            # send an ACK
+            _send(radio, ACK_CMD)
+
+            # the amount of data depend on the radio, so far we have two radios
+            # reading two bytes with an ACK at the end and just ONE with just
+            # one byte (QYT KT8900)
+            # the JT-6188 appears a clone of the last, but reads TWO bytes.
+            #
+            # we will read two bytes with a custom timeout to not penalize the
+            # users for this.
+            #
+            # we just check for a response and last byte being a ACK, that is
+            # the common stone for all radios (3 so far)
+            ack = _rawrecv(radio, 2)
+
+            # checking
+            if len(ack) == 0 or ack[-1:] != ACK_CMD:
+                raise errors.RadioError("Radio didn't ACK the upload")
+
+            # restore the default serial timeout
+            radio.pipe.timeout = STIMEOUT
+
+    # DEBUG
+    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
+
+    return True
+
+
+def _download(radio):
+    """Get the memory map"""
+
+    # UI progress
+    status = chirp_common.Status()
+
+    # put radio in program mode and identify it
+    _do_ident(radio, status)
+
+    # the models that doesn't have the extra ID have to make a dummy read here
+    if radio._id2 is False:
+        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
+        discard = _rawrecv(radio, BLOCK_SIZE + 5)
+
+        if debug is True:
+            LOG.info("Dummy first block read done, got this:\n\n %s",
+                     util.hexprint(discard))
+
+    # reset the progress bar in the UI
+    status.max = MEM_SIZE / BLOCK_SIZE
+    status.msg = "Cloning from radio..."
+    status.cur = 0
+    radio.status_fn(status)
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    data = ""
+    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
+        # sending the read request
+        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
+
+        # read
+        d = _recv(radio, addr)
+
+        # aggregate the data
+        data += d
+
+        # UI Update
+        status.cur = addr / BLOCK_SIZE
+        status.msg = "Cloning from radio..."
+        radio.status_fn(status)
+
+    return data
+
+
+def _upload(radio):
+    """Upload procedure"""
+
+    # The UPLOAD mem is restricted to lower than 0x3100,
+    # so we will overide that here localy
+    MEM_SIZE = 0x3100
+
+    # UI progress
+    status = chirp_common.Status()
+
+    # put radio in program mode and identify it
+    _do_ident(radio, status, True)
+
+    # get the data to upload to radio
+    data = radio.get_mmap()
+
+    # Reset the UI progress
+    status.max = MEM_SIZE / TX_BLOCK_SIZE
+    status.cur = 0
+    status.msg = "Cloning to radio..."
+    radio.status_fn(status)
+
+    # the radios that doesn't have the extra ID 'may' do a dummy write, I found
+    # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
+    # dummy write is accepted (it actually writes to the mem!) by the radio.
+
+    # cleaning the serial buffer
+    _clean_buffer(radio)
+
+    # the fun start here
+    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
+        # getting the block of data to send
+        d = data[addr:addr + TX_BLOCK_SIZE]
+
+        # build the frame to send
+        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
+
+        # first block must not send the ACK at the beginning for the
+        # ones that has the extra id, since this have to do a extra step
+        if addr == 0 and radio._id2 is not False:
+            frame = frame[1:]
+
+        # send the frame
+        _send(radio, frame)
+
+        # receiving the response
+        ack = _rawrecv(radio, 1)
+
+        # basic check
+        if len(ack) != 1:
+            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
+
+        if not ack in "\x06\x05":
+            raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
+
+         # UI Update
+        status.cur = addr / TX_BLOCK_SIZE
+        status.msg = "Cloning to radio..."
+        radio.status_fn(status)
+
+
+def model_match(cls, data):
+    """Match the opened/downloaded image to the correct version"""
+    rid = data[0x3f70:0x3f76]
+
+    if rid in cls._fileid:
+        return True
+
+    return False
+
+
+def _decode_ranges(low, high):
+    """Unpack the data in the ranges zones in the memmap and return
+    a tuple with the integer corresponding to the Mhz it means"""
+    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
+    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
+    ilow *= 1000000
+    ihigh *= 1000000
+
+    return (ilow, ihigh)
+
+
+def _split(rf, f1, f2):
+    """Returns False if the two freqs are in the same band (no split)
+    or True otherwise"""
+
+    # determine if the two freqs are in the same band
+    for low, high in rf.valid_bands:
+        if f1 >= low and f1 <= high and \
+                f2 >= low and f2 <= high:
+            # if the two freqs are on the same Band this is not a split
+            return False
+
+    # if you get here is because the freq pairs are split
+    return False
+
+
+class BTechMobileCommon(chirp_common.CloneModeRadio,
+                        chirp_common.ExperimentalRadio):
+    """BTECH's UV-5001 and alike radios"""
+    VENDOR = "BTECH"
+    MODEL = ""
+    IDENT = ""
+    BANDS = 2
+    COLOR_LCD = False
+    NAME_LENGTH = 6
+    _power_levels = [chirp_common.PowerLevel("High", watts=25),
+                     chirp_common.PowerLevel("Low", watts=10)]
+    _vhf_range = (130000000, 180000000)
+    _220_range = (200000000, 271000000)
+    _uhf_range = (400000000, 521000000)
+    _350_range = (350000000, 391000000)
+    _upper = 199
+    _magic = MSTRING
+    _fileid = None
+    _id2 = False
+    btech3 = False
+
+    @classmethod
+    def get_prompts(cls):
+        rp = chirp_common.RadioPrompts()
+        rp.experimental = \
+            ('This driver is experimental.\n'
+             '\n'
+             'Please keep a copy of your memories with the original software '
+             'if you treasure them, this driver is new and may contain'
+             ' bugs.\n'
+             '\n'
+             )
+        rp.pre_download = _(dedent("""\
+            Follow these instructions to download your info:
+
+            1 - Turn off your radio
+            2 - Connect your interface cable
+            3 - Turn on your radio
+            4 - Do the download of your radio data
+
+            """))
+        rp.pre_upload = _(dedent("""\
+            Follow these instructions to upload your info:
+
+            1 - Turn off your radio
+            2 - Connect your interface cable
+            3 - Turn on your radio
+            4 - Do the upload of your radio data
+
+            """))
+        return rp
+
+    def get_features(self):
+        """Get the radio's features"""
+
+        # we will use the following var as global
+        global POWER_LEVELS
+
+        rf = chirp_common.RadioFeatures()
+        rf.has_settings = True
+        rf.has_bank = False
+        rf.has_tuning_step = False
+        rf.can_odd_split = True
+        rf.has_name = True
+        rf.has_offset = True
+        rf.has_mode = True
+        rf.has_dtcs = True
+        rf.has_rx_dtcs = True
+        rf.has_dtcs_polarity = True
+        rf.has_ctone = True
+        rf.has_cross = True
+        rf.valid_modes = MODES
+        rf.valid_characters = VALID_CHARS
+        rf.valid_name_length = self.NAME_LENGTH
+        rf.valid_duplexes = ["", "-", "+", "split", "off"]
+        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
+        rf.valid_cross_modes = [
+            "Tone->Tone",
+            "DTCS->",
+            "->DTCS",
+            "Tone->DTCS",
+            "DTCS->Tone",
+            "->Tone",
+            "DTCS->DTCS"]
+        rf.valid_skips = SKIP_VALUES
+        rf.valid_dtcs_codes = DTCS
+        rf.memory_bounds = (0, self._upper)
+
+        # power levels
+        POWER_LEVELS = self._power_levels
+        rf.valid_power_levels = POWER_LEVELS
+
+        # normal dual bands
+        rf.valid_bands = [self._vhf_range, self._uhf_range]
+
+        # 220 band
+        if self.BANDS == 3 or self.BANDS == 4:
+            rf.valid_bands.append(self._220_range)
+
+        # 350 band
+        if self.BANDS == 4:
+            rf.valid_bands.append(self._350_range)
+
+        return rf
+
+    def sync_in(self):
+        """Download from radio"""
+        data = _download(self)
+        self._mmap = memmap.MemoryMap(data)
+        self.process_mmap()
+
+    def sync_out(self):
+        """Upload to radio"""
+        try:
+            _upload(self)
+        except errors.RadioError:
+            raise
+        except Exception, e:
+            raise errors.RadioError("Error: %s" % e)
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number])
+
+    def _decode_tone(self, val):
+        """Parse the tone data to decode from mem, it returns:
+        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
+        pol = None
+
+        if val in [0, 65535]:
+            return '', None, None
+        elif val > 0x0258:
+            a = val / 10.0
+            return 'Tone', a, pol
+        else:
+            if val > 0x69:
+                index = val - 0x6A
+                pol = "R"
+            else:
+                index = val - 1
+                pol = "N"
+
+            tone = DTCS[index]
+            return 'DTCS', tone, pol
+
+    def _encode_tone(self, memval, mode, val, pol):
+        """Parse the tone data to encode from UI to mem"""
+        if mode == '' or mode is None:
+            memval.set_raw("\x00\x00")
+        elif mode == 'Tone':
+            memval.set_value(val * 10)
+        elif mode == 'DTCS':
+            # detect the index in the DTCS list
+            try:
+                index = DTCS.index(val)
+                if pol == "N":
+                    index += 1
+                else:
+                    index += 0x6A
+                memval.set_value(index)
+            except:
+                msg = "Digital Tone '%d' is not supported" % value
+                LOG.error(msg)
+                raise errors.RadioError(msg)
+        else:
+            msg = "Internal error: invalid mode '%s'" % mode
+            LOG.error(msg)
+            raise errors.InvalidDataError(msg)
+
+    def get_memory(self, number):
+        """Get the mem representation from the radio image"""
+        _mem = self._memobj.memory[number]
+        _names = self._memobj.names[number]
+
+        # Create a high-level memory object to return to the UI
+        mem = chirp_common.Memory()
+
+        # Memory number
+        mem.number = number
+
+        if _mem.get_raw()[0] == "\xFF":
+            mem.empty = True
+            return mem
+
+        # Freq and offset
+        mem.freq = int(_mem.rxfreq) * 10
+        # tx freq can be blank
+        if _mem.get_raw()[4] == "\xFF":
+            # TX freq not set
+            mem.offset = 0
+            mem.duplex = "off"
+        else:
+            # TX freq set
+            offset = (int(_mem.txfreq) * 10) - mem.freq
+            if offset != 0:
+                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
+                    mem.duplex = "split"
+                    mem.offset = int(_mem.txfreq) * 10
+                elif offset < 0:
+                    mem.offset = abs(offset)
+                    mem.duplex = "-"
+                elif offset > 0:
+                    mem.offset = offset
+                    mem.duplex = "+"
+            else:
+                mem.offset = 0
+
+        # name TAG of the channel
+        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
+
+        # power
+        mem.power = POWER_LEVELS[int(_mem.power)]
+
+        # wide/narrow
+        mem.mode = MODES[int(_mem.wide)]
+
+        # skip
+        mem.skip = SKIP_VALUES[_mem.add]
+
+        # tone data
+        rxtone = txtone = None
+        txtone = self._decode_tone(_mem.txtone)
+        rxtone = self._decode_tone(_mem.rxtone)
+        chirp_common.split_tone_decode(mem, txtone, rxtone)
+
+        # Extra
+        mem.extra = RadioSettingGroup("extra", "Extra")
+
+        scramble = RadioSetting("scramble", "Scramble",
+                                RadioSettingValueBoolean(bool(
+                                    _mem.scramble)))
+        mem.extra.append(scramble)
+
+        bcl = RadioSetting("bcl", "Busy channel lockout",
+                           RadioSettingValueBoolean(bool(_mem.bcl)))
+        mem.extra.append(bcl)
+
+        pttid = RadioSetting("pttid", "PTT ID",
+                             RadioSettingValueList(PTTID_LIST,
+                                                   PTTID_LIST[_mem.pttid]))
+        mem.extra.append(pttid)
+
+        # validating scode
+        scode = _mem.scode if _mem.scode != 15 else 0
+        pttidcode = RadioSetting("scode", "PTT ID signal code",
+                                 RadioSettingValueList(
+                                     PTTIDCODE_LIST,
+                                     PTTIDCODE_LIST[scode]))
+        mem.extra.append(pttidcode)
+
+        optsig = RadioSetting("optsig", "Optional signaling",
+                              RadioSettingValueList(
+                                  OPTSIG_LIST,
+                                  OPTSIG_LIST[_mem.optsig]))
+        mem.extra.append(optsig)
+
+        spmute = RadioSetting("spmute", "Speaker mute",
+                              RadioSettingValueList(
+                                  SPMUTE_LIST,
+                                  SPMUTE_LIST[_mem.spmute]))
+        mem.extra.append(spmute)
+
+        return mem
+
+    def set_memory(self, mem):
+        """Set the memory data in the eeprom img from the UI"""
+        # get the eprom representation of this channel
+        _mem = self._memobj.memory[mem.number]
+        _names = self._memobj.names[mem.number]
+
+        mem_was_empty = False
+        # same method as used in get_memory for determining if mem is empty
+        # doing this BEFORE overwriting it with new values ...
+        if _mem.get_raw()[0] == "\xFF":
+            LOG.debug("This mem was empty before")
+            mem_was_empty = True
+        
+        # if empty memmory
+        if mem.empty:
+            # the channel itself
+            _mem.set_raw("\xFF" * 16)
+            # the name tag
+            _names.set_raw("\xFF" * 16)
+            return
+
+        # frequency
+        _mem.rxfreq = mem.freq / 10
+
+        # duplex
+        if mem.duplex == "+":
+            _mem.txfreq = (mem.freq + mem.offset) / 10
+        elif mem.duplex == "-":
+            _mem.txfreq = (mem.freq - mem.offset) / 10
+        elif mem.duplex == "off":
+            for i in _mem.txfreq:
+                i.set_raw("\xFF")
+        elif mem.duplex == "split":
+            _mem.txfreq = mem.offset / 10
+        else:
+            _mem.txfreq = mem.freq / 10
+
+        # tone data
+        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
+            chirp_common.split_tone_encode(mem)
+        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
+        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
+
+        # name TAG of the channel
+        if len(mem.name) < self.NAME_LENGTH:
+            # we must pad to self.NAME_LENGTH chars, " " = "\xFF"
+            mem.name = str(mem.name).ljust(self.NAME_LENGTH, " ")
+        _names.name = str(mem.name).replace(" ", "\xFF")
+
+        # power, # default power level is high
+        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
+
+        # wide/narrow
+        _mem.wide = MODES.index(mem.mode)
+
+        # scan add property
+        _mem.add = SKIP_VALUES.index(mem.skip)
+
+        # reseting unknowns, this have to be set by hand
+        _mem.unknown0 = 0
+        _mem.unknown1 = 0
+        _mem.unknown2 = 0
+        _mem.unknown3 = 0
+        _mem.unknown4 = 0
+        _mem.unknown5 = 0
+        _mem.unknown6 = 0
+
+        # extra settings
+        if len(mem.extra) > 0:
+            # there are setting, parse
+            LOG.debug("Extra-Setting supplied. Setting them.")
+            for setting in mem.extra:
+                setattr(_mem, setting.get_name(), setting.value)
+        else:
+            if mem.empty:
+                LOG.debug("New mem is empty.")
+            else:
+                LOG.debug("New mem is NOT empty")
+                # set extra-settings to default ONLY when apreviously empty or
+                # deleted memory was edited to prevent errors such as #4121
+                if mem_was_empty :
+                    LOG.debug("old mem was empty. Setting default for extras.")
+                    _mem.spmute = 0
+                    _mem.optsig = 0
+                    _mem.scramble = 0
+                    _mem.bcl = 0
+                    _mem.pttid = 0
+                    _mem.scode = 0
+
+        return mem
+
+    def get_settings(self):
+        """Translate the bit in the mem_struct into settings in the UI"""
+        _mem = self._memobj
+        basic = RadioSettingGroup("basic", "Basic Settings")
+        advanced = RadioSettingGroup("advanced", "Advanced Settings")
+        other = RadioSettingGroup("other", "Other Settings")
+        work = RadioSettingGroup("work", "Work Mode Settings")
+        top = RadioSettings(basic, advanced, other, work)
+
+        # Basic
+        if self.COLOR_LCD:
+            tmr = RadioSetting("settings.tmr", "Transceiver multi-receive",
+                               RadioSettingValueList(
+                                   LIST_TMR,
+                                   LIST_TMR[_mem.settings.tmr]))
+            basic.append(tmr)
+        else:
+            tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
+                               RadioSettingValueBoolean(_mem.settings.tdr))
+            basic.append(tdr)
+
+        sql = RadioSetting("settings.sql", "Squelch level",
+                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
+        basic.append(sql)
+
+        tot = RadioSetting("settings.tot", "Time out timer",
+                           RadioSettingValueList(
+                               LIST_TOT,
+                               LIST_TOT[_mem.settings.tot]))
+        basic.append(tot)
+
+        if self.VENDOR == "BTECH" or self.COLOR_LCD:
+            apo = RadioSetting("settings.apo", "Auto power off timer",
+                               RadioSettingValueList(
+                                   LIST_APO,
+                                   LIST_APO[_mem.settings.apo]))
+            basic.append(apo)
+        else:
+            toa = RadioSetting("settings.apo", "Time out alert timer",
+                               RadioSettingValueList(
+                                   LIST_OFF1TO10, 
+                                   LIST_OFF1TO10[_mem.settings.apo]))
+            basic.append(toa)
+
+        abr = RadioSetting("settings.abr", "Backlight timer",
+                           RadioSettingValueList(
+                               LIST_OFF1TO50,
+                               LIST_OFF1TO50[_mem.settings.abr]))
+        basic.append(abr)
+
+        beep = RadioSetting("settings.beep", "Key beep",
+                            RadioSettingValueBoolean(_mem.settings.beep))
+        basic.append(beep)
+
+        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
+                              RadioSettingValueList(
+                                  LIST_DTMFST,
+                                  LIST_DTMFST[_mem.settings.dtmfst]))
+        basic.append(dtmfst)
+
+        if not self.COLOR_LCD:
+            prisc = RadioSetting("settings.prisc", "Priority scan",
+                                 RadioSettingValueBoolean(
+                                     _mem.settings.prisc))
+            basic.append(prisc)
+
+            prich = RadioSetting("settings.prich", "Priority channel",
+                                 RadioSettingValueInteger(0, 199,
+                                     _mem.settings.prich))
+            basic.append(prich)
+
+        screv = RadioSetting("settings.screv", "Scan resume method",
+                             RadioSettingValueList(
+                                 LIST_SCREV,
+                                 LIST_SCREV[_mem.settings.screv]))
+        basic.append(screv)
+
+        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
+                             RadioSettingValueInteger(0, 30,
+                                 _mem.settings.pttlt))
+        basic.append(pttlt)
+
+        emctp = RadioSetting("settings.emctp", "Alarm mode",
+                             RadioSettingValueList(
+                                 LIST_EMCTP,
+                                 LIST_EMCTP[_mem.settings.emctp]))
+        basic.append(emctp)
+
+        emcch = RadioSetting("settings.emcch", "Alarm channel",
+                             RadioSettingValueInteger(0, 199,
+                                 _mem.settings.emcch))
+        basic.append(emcch)
+
+        if self.COLOR_LCD:
+            if _mem.settings.sigbp > 0x01:
+                val = 0x00
+            else:
+                val = _mem.settings.sigbp
+            sigbp = RadioSetting("settings.sigbp", "Roger beep",
+                                 RadioSettingValueBoolean(val))
+            basic.append(sigbp)
+        else:
+            ringt = RadioSetting("settings.ringt", "Ring time",
+                                 RadioSettingValueList(
+                                     LIST_OFF1TO9,
+                                     LIST_OFF1TO9[_mem.settings.ringt]))
+            basic.append(ringt)
+
+        camdf = RadioSetting("settings.camdf", "Display mode A",
+                             RadioSettingValueList(
+                                 LIST_MDF,
+                                 LIST_MDF[_mem.settings.camdf]))
+        basic.append(camdf)
+
+        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
+                             RadioSettingValueList(
+                                 LIST_MDF,
+                                 LIST_MDF[_mem.settings.cbmdf]))
+        basic.append(cbmdf)
+
+        if self.COLOR_LCD:
+            ccmdf = RadioSetting("settings.ccmdf", "Display mode C",
+                                 RadioSettingValueList(
+                                     LIST_MDF,
+                                     LIST_MDF[_mem.settings.ccmdf]))
+            basic.append(ccmdf)
+
+            cdmdf = RadioSetting("settings.cdmdf", "Display mode D",
+                                 RadioSettingValueList(
+                                     LIST_MDF,
+                                     LIST_MDF[_mem.settings.cdmdf]))
+            basic.append(cdmdf)
+
+            langua = RadioSetting("settings.langua", "Language",
+                                  RadioSettingValueList(
+                                      LIST_LANGUA,
+                                      LIST_LANGUA[_mem.settings.langua]))
+            basic.append(langua)
+
+        if self.VENDOR == "BTECH":
+            sync = RadioSetting("settings.sync", "A/B channel sync",
+                                RadioSettingValueBoolean(
+                                    _mem.settings.sync))
+            basic.append(sync)
+        else:
+            autolk = RadioSetting("settings.sync", "Auto keylock",
+                                  RadioSettingValueBoolean(
+                                      _mem.settings.sync))
+            basic.append(autolk)
+
+        if not self.COLOR_LCD:
+            ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
+                                  RadioSettingValueList(
+                                      LIST_PONMSG,
+                                      LIST_PONMSG[_mem.settings.ponmsg]))
+            basic.append(ponmsg)
+
+        if self.COLOR_LCD:
+            mainfc = RadioSetting("settings.mainfc", 
+                                  "Main LCD foreground color",
+                                      RadioSettingValueList(
+                                          LIST_COLOR8,
+                                          LIST_COLOR8[_mem.settings.mainfc]))
+            basic.append(mainfc)
+
+            mainbc = RadioSetting("settings.mainbc",
+                                  "Main LCD background color",
+                                      RadioSettingValueList(
+                                          LIST_COLOR8,
+                                          LIST_COLOR8[_mem.settings.mainbc]))
+            basic.append(mainbc)
+
+            menufc = RadioSetting("settings.menufc", "Menu foreground color",
+                                  RadioSettingValueList(
+                                      LIST_COLOR8,
+                                      LIST_COLOR8[_mem.settings.menufc]))
+            basic.append(menufc)
+
+            menubc = RadioSetting("settings.menubc", "Menu background color",
+                                  RadioSettingValueList(
+                                      LIST_COLOR8,
+                                      LIST_COLOR8[_mem.settings.menubc]))
+            basic.append(menubc)
+
+            stafc = RadioSetting("settings.stafc",
+                                 "Top status foreground color",
+                                     RadioSettingValueList(
+                                         LIST_COLOR8,
+                                         LIST_COLOR8[_mem.settings.stafc]))
+            basic.append(stafc)
+
+            stabc = RadioSetting("settings.stabc",
+                                 "Top status background color",
+                                     RadioSettingValueList(
+                                         LIST_COLOR8,
+                                         LIST_COLOR8[_mem.settings.stabc]))
+            basic.append(stabc)
+
+            sigfc = RadioSetting("settings.sigfc",
+                                 "Bottom status foreground color",
+                                     RadioSettingValueList(
+                                         LIST_COLOR8,
+                                         LIST_COLOR8[_mem.settings.sigfc]))
+            basic.append(sigfc)
+
+            sigbc = RadioSetting("settings.sigbc",
+                                 "Bottom status background color",
+                                     RadioSettingValueList(
+                                         LIST_COLOR8,
+                                         LIST_COLOR8[_mem.settings.sigbc]))
+            basic.append(sigbc)
+
+            rxfc = RadioSetting("settings.rxfc", "Receiving character color",
+                                RadioSettingValueList(
+                                    LIST_COLOR8,
+                                    LIST_COLOR8[_mem.settings.rxfc]))
+            basic.append(rxfc)
+
+            txfc = RadioSetting("settings.txfc",
+                                "Transmitting character color",
+                                    RadioSettingValueList(
+                                        LIST_COLOR8,
+                                        LIST_COLOR8[_mem.settings.txfc]))
+            basic.append(txfc)
+
+            txdisp = RadioSetting("settings.txdisp",
+                                  "Transmitting status display",
+                                      RadioSettingValueList(
+                                          LIST_TXDISP,
+                                          LIST_TXDISP[_mem.settings.txdisp]))
+            basic.append(txdisp)
+        else:
+            wtled = RadioSetting("settings.wtled", "Standby backlight Color",
+                                 RadioSettingValueList(
+                                     LIST_COLOR4,
+                                     LIST_COLOR4[_mem.settings.wtled]))
+            basic.append(wtled)
+
+            rxled = RadioSetting("settings.rxled", "RX backlight Color",
+                                 RadioSettingValueList(
+                                     LIST_COLOR4,
+                                     LIST_COLOR4[_mem.settings.rxled]))
+            basic.append(rxled)
+
+            txled = RadioSetting("settings.txled", "TX backlight Color",
+                                 RadioSettingValueList(
+                                     LIST_COLOR4,
+                                     LIST_COLOR4[_mem.settings.txled]))
+            basic.append(txled)
+
+        anil = RadioSetting("settings.anil", "ANI length",
+                            RadioSettingValueList(
+                                LIST_ANIL,
+                                LIST_ANIL[_mem.settings.anil]))
+        basic.append(anil)
+
+        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
+                            RadioSettingValueList(
+                                LIST_REPS,
+                                LIST_REPS[_mem.settings.reps]))
+        basic.append(reps)
+
+        repm = RadioSetting("settings.repm", "Relay condition",
+                            RadioSettingValueList(
+                                LIST_REPM,
+                                LIST_REPM[_mem.settings.repm]))
+        basic.append(repm)
+
+        if self.VENDOR == "BTECH" or self.COLOR_LCD:
+            if self.COLOR_LCD:
+                tmrmr = RadioSetting("settings.tmrmr", "TMR return time",
+                                     RadioSettingValueList(
+                                         LIST_OFF1TO50,
+                                         LIST_OFF1TO50[_mem.settings.tmrmr]))
+                basic.append(tmrmr)
+            else:
+                tdrab = RadioSetting("settings.tdrab", "TDR return time",
+                                     RadioSettingValueList(
+                                         LIST_OFF1TO50, 
+                                         LIST_OFF1TO50[_mem.settings.tdrab]))
+                basic.append(tdrab)
+
+            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
+                               RadioSettingValueBoolean(_mem.settings.ste))
+            basic.append(ste)
+
+            rpste = RadioSetting("settings.rpste", "Repeater STE",
+                                 RadioSettingValueList(
+                                     LIST_OFF1TO9,
+                                     LIST_OFF1TO9[_mem.settings.rpste]))
+            basic.append(rpste)
+
+            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
+                                 RadioSettingValueList(
+                                     LIST_RPTDL,
+                                     LIST_RPTDL[_mem.settings.rptdl]))
+            basic.append(rptdl)
+
+        if str(_mem.fingerprint.fp) in BTECH3:
+            mgain = RadioSetting("settings.mgain", "Mic gain",
+                                 RadioSettingValueInteger(0, 120,
+                                     _mem.settings.mgain))
+            basic.append(mgain)
+
+        if str(_mem.fingerprint.fp) in BTECH3 or self.COLOR_LCD:
+            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
+                                 RadioSettingValueInteger(0, 60,
+                                     _mem.settings.dtmfg))
+            basic.append(dtmfg)
+
+        # Advanced
+        def _filter(name):
+            filtered = ""
+            for char in str(name):
+                if char in VALID_CHARS:
+                    filtered += char
+                else:
+                    filtered += " "
+            return filtered
+
+        _msg = self._memobj.poweron_msg
+        if self.COLOR_LCD:
+            line1 = RadioSetting("poweron_msg.line1",
+                                 "Power-on message line 1",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line1)))
+            advanced.append(line1)
+            line2 = RadioSetting("poweron_msg.line2",
+                                 "Power-on message line 2",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line2)))
+            advanced.append(line2)
+            line3 = RadioSetting("poweron_msg.line3",
+                                 "Power-on message line 3",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line3)))
+            advanced.append(line3)
+            line4 = RadioSetting("poweron_msg.line4",
+                                 "Power-on message line 4",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line4)))
+            advanced.append(line4)
+            line5 = RadioSetting("poweron_msg.line5",
+                                 "Power-on message line 5",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line5)))
+            advanced.append(line5)
+            line6 = RadioSetting("poweron_msg.line6",
+                                 "Power-on message line 6",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line6)))
+            advanced.append(line6)
+            line7 = RadioSetting("poweron_msg.line7",
+                                 "Power-on message line 7",
+                                     RadioSettingValueString(0, 8, _filter(
+                                         _msg.line7)))
+            advanced.append(line7)
+            line8 = RadioSetting("poweron_msg.line8", "Static message",
+                                 RadioSettingValueString(0, 8, _filter(
+                                     _msg.line8)))
+            advanced.append(line8)
+        else:
+            line1 = RadioSetting("poweron_msg.line1",
+                                 "Power-on message line 1",
+                                     RadioSettingValueString(0, 6, _filter(
+                                         _msg.line1)))
+            advanced.append(line1)
+            line2 = RadioSetting("poweron_msg.line2",
+                                 "Power-on message line 2",
+                                     RadioSettingValueString(0, 6, _filter(
+                                         _msg.line2)))
+            advanced.append(line2)
+
+        if self.MODEL in ("UV-2501", "UV-5001"):
+            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
+                                   RadioSettingValueBoolean(
+                                       _mem.settings2.vfomren))
+            advanced.append(vfomren)
+
+            reseten = RadioSetting("settings2.reseten", "RESET",
+                                   RadioSettingValueBoolean(
+                                       _mem.settings2.reseten))
+            advanced.append(reseten)
+
+            menuen = RadioSetting("settings2.menuen", "Menu",
+                                  RadioSettingValueBoolean(
+                                      _mem.settings2.menuen))
+            advanced.append(menuen)
+
+        # Other
+        def convert_bytes_to_limit(bytes):
+            limit = ""
+            for byte in bytes:
+                if byte < 10:
+                    limit += chr(byte + 0x30)
+                else:
+                    break
+            return limit
+
+        if self.MODEL in ["UV-2501+220", "KT8900R"]:
+            _ranges = self._memobj.ranges220
+            ranges = "ranges220"
+        else:
+            _ranges = self._memobj.ranges
+            ranges = "ranges"
+
+        _limit = convert_bytes_to_limit(_ranges.vhf_low)
+        val = RadioSettingValueString(0, 3, _limit)
+        val.set_mutable(False)
+        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
+        other.append(vhf_low)
+
+        _limit = convert_bytes_to_limit(_ranges.vhf_high)
+        val = RadioSettingValueString(0, 3, _limit)
+        val.set_mutable(False)
+        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
+        other.append(vhf_high)
+
+        if self.BANDS == 3 or self.BANDS == 4:
+            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
+            val = RadioSettingValueString(0, 3, _limit)
+            val.set_mutable(False)
+            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
+            other.append(vhf2_low)
+
+            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
+            val = RadioSettingValueString(0, 3, _limit)
+            val.set_mutable(False)
+            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
+            other.append(vhf2_high)
+
+        _limit = convert_bytes_to_limit(_ranges.uhf_low)
+        val = RadioSettingValueString(0, 3, _limit)
+        val.set_mutable(False)
+        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
+        other.append(uhf_low)
+
+        _limit = convert_bytes_to_limit(_ranges.uhf_high)
+        val = RadioSettingValueString(0, 3, _limit)
+        val.set_mutable(False)
+        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
+        other.append(uhf_high)
+
+        if self.BANDS == 4:
+            _limit = convert_bytes_to_limit(_ranges.uhf2_low)
+            val = RadioSettingValueString(0, 3, _limit)
+            val.set_mutable(False)
+            uhf2_low = RadioSetting("%s.uhf2_low" % ranges, "UHF2 low", val)
+            other.append(uhf2_low)
+
+            _limit = convert_bytes_to_limit(_ranges.uhf2_high)
+            val = RadioSettingValueString(0, 3, _limit)
+            val.set_mutable(False)
+            uhf2_high = RadioSetting("%s.uhf2_high" % ranges, "UHF2 high", val)
+            other.append(uhf2_high)
+
+        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
+        val.set_mutable(False)
+        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
+        other.append(fp)
+
+
+        # Work
+        if self.COLOR_LCD:
+            dispab = RadioSetting("settings2.dispab", "Display",
+                                  RadioSettingValueList(
+                                      LIST_ABCD,
+                                      LIST_ABCD[_mem.settings2.dispab]))
+            work.append(dispab)
+        else:
+            dispab = RadioSetting("settings2.dispab", "Display",
+                                  RadioSettingValueList(
+                                      LIST_AB,
+                                      LIST_AB[_mem.settings2.dispab]))
+            work.append(dispab)
+
+        if self.COLOR_LCD:
+            vfomra = RadioSetting("settings2.vfomra", "VFO/MR A mode",
+                                  RadioSettingValueList(
+                                      LIST_VFOMR,
+                                      LIST_VFOMR[_mem.settings2.vfomra]))
+            work.append(vfomra)
+
+            vfomrb = RadioSetting("settings2.vfomrb", "VFO/MR B mode",
+                                  RadioSettingValueList(
+                                      LIST_VFOMR,
+                                      LIST_VFOMR[_mem.settings2.vfomrb]))
+            work.append(vfomrb)
+
+            vfomrc = RadioSetting("settings2.vfomrc", "VFO/MR C mode",
+                                  RadioSettingValueList(
+                                      LIST_VFOMR,
+                                      LIST_VFOMR[_mem.settings2.vfomrc]))
+            work.append(vfomrc)
+
+            vfomrd = RadioSetting("settings2.vfomrd", "VFO/MR D mode",
+                                  RadioSettingValueList(
+                                      LIST_VFOMR,
+                                      LIST_VFOMR[_mem.settings2.vfomrd]))
+            work.append(vfomrd)
+        else:
+            vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
+                                 RadioSettingValueList(
+                                     LIST_VFOMR,
+                                     LIST_VFOMR[_mem.settings2.vfomr]))
+            work.append(vfomr)
+
+
+        keylock = RadioSetting("settings2.keylock", "Keypad lock",
+                           RadioSettingValueBoolean(_mem.settings2.keylock))
+        work.append(keylock)
+
+        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
+                             RadioSettingValueInteger(0, 199,
+                                 _mem.settings2.mrcha))
+        work.append(mrcha)
+
+        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
+                             RadioSettingValueInteger(0, 199,
+                                 _mem.settings2.mrchb))
+        work.append(mrchb)
+
+        if self.COLOR_LCD:
+            mrchc = RadioSetting("settings2.mrchc", "MR C channel",
+                                 RadioSettingValueInteger(0, 199,
+                                     _mem.settings2.mrchc))
+            work.append(mrchc)
+
+            mrchd = RadioSetting("settings2.mrchd", "MR D channel",
+                                 RadioSettingValueInteger(0, 199,
+                                     _mem.settings2.mrchd))
+            work.append(mrchd)
+
+        def convert_bytes_to_freq(bytes):
+            real_freq = 0
+            for byte in bytes:
+                real_freq = (real_freq * 10) + byte
+            return chirp_common.format_freq(real_freq * 10)
+
+        def my_validate(value):
+            _vhf_lower = int(convert_bytes_to_limit(_ranges.vhf_low))
+            _vhf_upper = int(convert_bytes_to_limit(_ranges.vhf_high))
+            _uhf_lower = int(convert_bytes_to_limit(_ranges.uhf_low))
+            _uhf_upper = int(convert_bytes_to_limit(_ranges.uhf_high))
+            if self.BANDS == 3 or self.BANDS == 4:
+                _vhf2_lower = int(convert_bytes_to_limit(_ranges.vhf2_low))
+                _vhf2_upper = int(convert_bytes_to_limit(_ranges.vhf2_high))
+            if self.BANDS == 4:
+                _uhf2_lower = int(convert_bytes_to_limit(_ranges.uhf2_low))
+                _uhf2_upper = int(convert_bytes_to_limit(_ranges.uhf2_high))
+
+            value = chirp_common.parse_freq(value)
+            msg = ("Can't be less then %i.0000")
+            if value > 99000000 and value < _vhf_lower * 1000000:
+                raise InvalidValueError(msg % (_vhf_lower))
+            msg = ("Can't be betweeb %i.9975-%i.0000")
+            if self.BANDS == 2:
+                if (_vhf_upper + 1) * 1000000 <= value and \
+                    value < _uhf_lower * 1000000:
+                    raise InvalidValueError(msg % (_vhf_upper, _uhf_lower))
+            if self.BANDS == 3:
+                if (_vhf_upper + 1) * 1000000 <= value and \
+                    value < _vhf2_lower * 1000000:
+                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
+                if (_vhf2_upper + 1) * 1000000 <= value and \
+                    value < _uhf_lower * 1000000:
+                    raise InvalidValueError(msg % (_vhf2_upper, _uhf_lower))
+            if self.BANDS == 4:
+                if (_vhf_upper + 1) * 1000000 <= value and \
+                    value < _vhf2_lower * 1000000:
+                    raise InvalidValueError(msg % (_vhf_upper, _vhf2_lower))
+                if (_vhf2_upper + 1) * 1000000 <= value and \
+                    value < _uhf2_lower * 1000000:
+                    raise InvalidValueError(msg % (_vhf2_upper, _uhf2_lower))
+                if (_uhf2_upper + 1) * 1000000 <= value and \
+                    value < _uhf_lower * 1000000:
+                    raise InvalidValueError(msg % (_uhf2_upper, _uhf_lower))
+            msg = ("Can't be greater then %i.9975")
+            if value > 99000000 and value >= _uhf_upper * 1000000:
+                raise InvalidValueError(msg % (_uhf_upper))
+            return chirp_common.format_freq(value)
+
+        def apply_freq(setting, obj):
+            value = chirp_common.parse_freq(str(setting.value)) / 10
+            for i in range(7, -1, -1):
+                obj.freq[i] = value % 10
+                value /= 10
+
+        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+                                        _mem.vfo.a.freq))
+        val1a.set_validate_callback(my_validate)
+        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
+        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
+        work.append(vfoafreq)
+
+        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+                                        _mem.vfo.b.freq))
+        val1b.set_validate_callback(my_validate)
+        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
+        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
+        work.append(vfobfreq)
+
+        if self.COLOR_LCD:
+            val1c = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+                                            _mem.vfo.c.freq))
+            val1c.set_validate_callback(my_validate)
+            vfocfreq = RadioSetting("vfo.c.freq", "VFO C frequency", val1c)
+            vfocfreq.set_apply_callback(apply_freq, _mem.vfo.c)
+            work.append(vfocfreq)
+
+            val1d = RadioSettingValueString(0, 10, convert_bytes_to_freq(
+                                            _mem.vfo.d.freq))
+            val1d.set_validate_callback(my_validate)
+            vfodfreq = RadioSetting("vfo.d.freq", "VFO D frequency", val1d)
+            vfodfreq.set_apply_callback(apply_freq, _mem.vfo.d)
+            work.append(vfodfreq)
+
+        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
+                                  RadioSettingValueList(
+                                      LIST_SHIFT,
+                                      LIST_SHIFT[_mem.vfo.a.shiftd]))
+        work.append(vfoashiftd)
+
+        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
+                                  RadioSettingValueList(
+                                      LIST_SHIFT,
+                                      LIST_SHIFT[_mem.vfo.b.shiftd]))
+        work.append(vfobshiftd)
+
+        if self.COLOR_LCD:
+            vfocshiftd = RadioSetting("vfo.c.shiftd", "VFO C shift",
+                                      RadioSettingValueList(
+                                          LIST_SHIFT,
+                                          LIST_SHIFT[_mem.vfo.c.shiftd]))
+            work.append(vfocshiftd)
+
+            vfodshiftd = RadioSetting("vfo.d.shiftd", "VFO D shift",
+                                      RadioSettingValueList(
+                                          LIST_SHIFT,
+                                          LIST_SHIFT[_mem.vfo.d.shiftd]))
+            work.append(vfodshiftd)
+
+        def convert_bytes_to_offset(bytes):
+            real_offset = 0
+            for byte in bytes:
+                real_offset = (real_offset * 10) + byte
+            return chirp_common.format_freq(real_offset * 1000)
+
+        def apply_offset(setting, obj):
+            value = chirp_common.parse_freq(str(setting.value)) / 1000
+            for i in range(5, -1, -1):
+                obj.offset[i] = value % 10
+                value /= 10
+
+        if self.COLOR_LCD:
+            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.a.offset))
+            vfoaoffset = RadioSetting("vfo.a.offset",
+                                      "VFO A offset (0.000-999.999)", val1a)
+            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
+            work.append(vfoaoffset)
+
+            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.b.offset))
+            vfoboffset = RadioSetting("vfo.b.offset",
+                                      "VFO B offset (0.000-999.999)", val1b)
+            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
+            work.append(vfoboffset)
+
+            val1c = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.c.offset))
+            vfocoffset = RadioSetting("vfo.c.offset",
+                                      "VFO C offset (0.000-999.999)", val1c)
+            vfocoffset.set_apply_callback(apply_offset, _mem.vfo.c)
+            work.append(vfocoffset)
+
+            val1d = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.d.offset))
+            vfodoffset = RadioSetting("vfo.d.offset",
+                                      "VFO D offset (0.000-999.999)", val1d)
+            vfodoffset.set_apply_callback(apply_offset, _mem.vfo.d)
+            work.append(vfodoffset)
+        else:
+            val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.a.offset))
+            vfoaoffset = RadioSetting("vfo.a.offset",
+                                      "VFO A offset (0.000-99.999)", val1a)
+            vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
+            work.append(vfoaoffset)
+
+            val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
+                                            _mem.vfo.b.offset))
+            vfoboffset = RadioSetting("vfo.b.offset",
+                                      "VFO B offset (0.000-99.999)", val1b)
+            vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
+            work.append(vfoboffset)
+
+
+        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
+                               RadioSettingValueList(
+                                   LIST_TXP,
+                                   LIST_TXP[_mem.vfo.a.power]))
+        work.append(vfoatxp)
+
+        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
+                               RadioSettingValueList(
+                                   LIST_TXP,
+                                   LIST_TXP[_mem.vfo.b.power]))
+        work.append(vfobtxp)
+
+        if self.COLOR_LCD:
+            vfoctxp = RadioSetting("vfo.c.power", "VFO C power",
+                                   RadioSettingValueList(
+                                       LIST_TXP,
+                                       LIST_TXP[_mem.vfo.c.power]))
+            work.append(vfoctxp)
+
+            vfodtxp = RadioSetting("vfo.d.power", "VFO D power",
+                                   RadioSettingValueList(
+                                       LIST_TXP,
+                                       LIST_TXP[_mem.vfo.d.power]))
+            work.append(vfodtxp)
+
+        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
+                                RadioSettingValueList(
+                                    LIST_WIDE,
+                                    LIST_WIDE[_mem.vfo.a.wide]))
+        work.append(vfoawide)
+
+        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
+                                RadioSettingValueList(
+                                    LIST_WIDE,
+                                    LIST_WIDE[_mem.vfo.b.wide]))
+        work.append(vfobwide)
+
+        if self.COLOR_LCD:
+            vfocwide = RadioSetting("vfo.c.wide", "VFO C bandwidth",
+                                    RadioSettingValueList(
+                                        LIST_WIDE,
+                                        LIST_WIDE[_mem.vfo.c.wide]))
+            work.append(vfocwide)
+
+            vfodwide = RadioSetting("vfo.d.wide", "VFO D bandwidth",
+                                    RadioSettingValueList(
+                                        LIST_WIDE,
+                                        LIST_WIDE[_mem.vfo.d.wide]))
+            work.append(vfodwide)
+
+        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
+                                RadioSettingValueList(
+                                    LIST_STEP,
+                                    LIST_STEP[_mem.vfo.a.step]))
+        work.append(vfoastep)
+
+        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
+                                RadioSettingValueList(
+                                    LIST_STEP,
+                                    LIST_STEP[_mem.vfo.b.step]))
+        work.append(vfobstep)
+
+        if self.COLOR_LCD:
+            vfocstep = RadioSetting("vfo.c.step", "VFO C step",
+                                    RadioSettingValueList(
+                                        LIST_STEP,
+                                        LIST_STEP[_mem.vfo.c.step]))
+            work.append(vfocstep)
+
+            vfodstep = RadioSetting("vfo.d.step", "VFO D step",
+                                    RadioSettingValueList(
+                                        LIST_STEP,
+                                        LIST_STEP[_mem.vfo.d.step]))
+            work.append(vfodstep)
+
+        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
+                                  RadioSettingValueList(
+                                      OPTSIG_LIST,
+                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
+        work.append(vfoaoptsig)
+
+        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
+                                  RadioSettingValueList(
+                                      OPTSIG_LIST,
+                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
+        work.append(vfoboptsig)
+
+        if self.COLOR_LCD:
+            vfocoptsig = RadioSetting("vfo.c.optsig", "VFO C optional signal",
+                                      RadioSettingValueList(
+                                          OPTSIG_LIST,
+                                          OPTSIG_LIST[_mem.vfo.c.optsig]))
+            work.append(vfocoptsig)
+
+            vfodoptsig = RadioSetting("vfo.d.optsig", "VFO D optional signal",
+                                      RadioSettingValueList(
+                                          OPTSIG_LIST,
+                                          OPTSIG_LIST[_mem.vfo.d.optsig]))
+            work.append(vfodoptsig)
+
+        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
+                                  RadioSettingValueList(
+                                      SPMUTE_LIST,
+                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
+        work.append(vfoaspmute)
+
+        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
+                                  RadioSettingValueList(
+                                      SPMUTE_LIST,
+                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
+        work.append(vfobspmute)
+
+        if self.COLOR_LCD:
+            vfocspmute = RadioSetting("vfo.c.spmute", "VFO C speaker mute",
+                                      RadioSettingValueList(
+                                          SPMUTE_LIST,
+                                          SPMUTE_LIST[_mem.vfo.c.spmute]))
+            work.append(vfocspmute)
+
+            vfodspmute = RadioSetting("vfo.d.spmute", "VFO D speaker mute",
+                                      RadioSettingValueList(
+                                          SPMUTE_LIST,
+                                          SPMUTE_LIST[_mem.vfo.d.spmute]))
+            work.append(vfodspmute)
+
+        vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
+                               RadioSettingValueBoolean(
+                                   _mem.vfo.a.scramble))
+        work.append(vfoascr)
+
+        vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
+                               RadioSettingValueBoolean(
+                                   _mem.vfo.b.scramble))
+        work.append(vfobscr)
+
+        if self.COLOR_LCD:
+            vfocscr = RadioSetting("vfo.c.scramble", "VFO C scramble",
+                                   RadioSettingValueBoolean(
+                                       _mem.vfo.c.scramble))
+            work.append(vfocscr)
+
+            vfodscr = RadioSetting("vfo.d.scramble", "VFO D scramble",
+                                   RadioSettingValueBoolean(
+                                       _mem.vfo.d.scramble))
+            work.append(vfodscr)
+
+        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
+                                 RadioSettingValueList(
+                                     PTTIDCODE_LIST,
+                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
+        work.append(vfoascode)
+
+        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
+                                 RadioSettingValueList(
+                                     PTTIDCODE_LIST,
+                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
+        work.append(vfobscode)
+
+        if self.COLOR_LCD:
+            vfocscode = RadioSetting("vfo.c.scode", "VFO C PTT-ID",
+                                     RadioSettingValueList(
+                                         PTTIDCODE_LIST,
+                                         PTTIDCODE_LIST[_mem.vfo.c.scode]))
+            work.append(vfocscode)
+
+            vfodscode = RadioSetting("vfo.d.scode", "VFO D PTT-ID",
+                                     RadioSettingValueList(
+                                         PTTIDCODE_LIST,
+                                         PTTIDCODE_LIST[_mem.vfo.d.scode]))
+            work.append(vfodscode)
+
+        pttid = RadioSetting("settings.pttid", "PTT ID",
+                             RadioSettingValueList(
+                                 PTTID_LIST,
+                                 PTTID_LIST[_mem.settings.pttid]))
+        work.append(pttid)
+
+        if not self.COLOR_LCD:
+            #FM presets
+            fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
+            top.append(fm_presets)
+
+            def fm_validate(value):
+                if value == 0:
+                    return chirp_common.format_freq(value)
+                if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
+                    msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
+                    raise InvalidValueError(msg)
+                return value
+
+            def apply_fm_preset_name(setting, obj):
+                valstring = str (setting.value)
+                for i in range(0,6):
+                    if valstring[i] in VALID_CHARS:
+                        obj[i] = valstring[i]
+                    else:
+                        obj[i] = '0xff'
+
+            def apply_fm_freq(setting, obj):
+                value = chirp_common.parse_freq(str(setting.value)) / 10
+                for i in range(7, -1, -1):
+                    obj.freq[i] = value % 10
+                    value /= 10
+        
+            _presets = self._memobj.fm_radio_preset
+            i = 1
+            for preset in _presets:
+                line = RadioSetting("fm_presets_"+ str(i), 
+                                    "Station name " + str(i),
+                                        RadioSettingValueString(0, 6, _filter(
+                                            preset.broadcast_station_name)))
+                line.set_apply_callback(apply_fm_preset_name, 
+                                        preset.broadcast_station_name)
+            
+                val = RadioSettingValueFloat(0, 108,
+                                             convert_bytes_to_freq(
+                                                 preset.freq))
+                fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq",
+                                      "Frequency "+ str(i), val)
+                val.set_validate_callback(fm_validate)
+                fmfreq.set_apply_callback(apply_fm_freq, preset)
+                fm_presets.append(line)
+                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)
+
+        if self.COLOR_LCD:
+            line = RadioSetting(
+                "dtmf_settings.resettime",
+                "Reset time",
+                RadioSettingValueList(LIST_5TONE_RESET_COLOR,
+                                      LIST_5TONE_RESET_COLOR[
+                                          _mem.dtmf_settings.resettime]))
+            dtmf_dec_settings.append(line)
+        else:
+            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):
+        _settings = self._memobj.settings
+        for element in settings:
+            if not isinstance(element, RadioSetting):
+                if element.get_name() == "fm_preset":
+                    self._set_fm_preset(element)
+                else:
+                    self.set_settings(element)
+                    continue
+            else:
+                try:
+                    name = element.get_name()
+                    if "." in name:
+                        bits = name.split(".")
+                        obj = self._memobj
+                        for bit in bits[:-1]:
+                            if "/" in bit:
+                                bit, index = bit.split("/", 1)
+                                index = int(index)
+                                obj = getattr(obj, bit)[index]
+                            else:
+                                obj = getattr(obj, bit)
+                        setting = bits[-1]
+                    else:
+                        obj = _settings
+                        setting = element.get_name()
+
+                    if element.has_apply_callback():
+                        LOG.debug("Using apply callback")
+                        element.run_apply_callback()
+                    elif element.value.get_mutable():
+                        LOG.debug("Setting %s = %s" % (setting, element.value))
+                        setattr(obj, setting, element.value)
+                except Exception, e:
+                    LOG.debug(element.get_name())
+                    raise
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        match_size = False
+        match_model = False
+
+        # testing the file data size
+        if len(filedata) == MEM_SIZE:
+            match_size = True
+
+        # testing the firmware model fingerprint
+        match_model = model_match(cls, filedata)
+
+        if match_size and match_model:
+            return True
+        else:
+            return False
+
+
 MEM_FORMAT = """
 #seekto 0x0000;
 struct {
@@ -127,9 +2831,8 @@
 
 struct settings_vfo {
   u8 freq[8];
-  u8 unknown1;
-  u8 offset[4];
-  u8 unknown2[3];
+  u8 offset[6];
+  u8 unknown2[2];
   ul16 rxtone;
   ul16 txtone;
   u8 scode;
@@ -316,696 +3019,12 @@
 
 """
 
-# A note about the memmory in these radios
-#
-# The real memory of these radios extends to 0x4000
-# On read the factory software only uses up to 0x3200
-# On write it just uploads the contents up to 0x3100
-#
-# The mem beyond 0x3200 holds the ID data
-
-MEM_SIZE = 0x4000
-BLOCK_SIZE = 0x40
-TX_BLOCK_SIZE = 0x10
-ACK_CMD = "\x06"
-MODES = ["FM", "NFM"]
-SKIP_VALUES = ["S", ""]
-TONES = chirp_common.TONES
-DTCS = sorted(chirp_common.DTCS_CODES + [645])
-NAME_LENGTH = 6
-PTTID_LIST = ["OFF", "BOT", "EOT", "BOTH"]
-PTTIDCODE_LIST = ["%s" % x for x in range(1, 16)]
-OPTSIG_LIST = ["OFF", "DTMF", "2TONE", "5TONE"]
-SPMUTE_LIST = ["Tone/DTCS", "Tone/DTCS and Optsig", "Tone/DTCS or Optsig"]
-
-LIST_TOT = ["%s sec" % x for x in range(15, 615, 15)]
-LIST_TOA = ["Off"] + ["%s seconds" % x for x in range(1, 11)]
-LIST_APO = ["Off"] + ["%s minutes" % x for x in range(30, 330, 30)]
-LIST_ABR = ["Off"] + ["%s seconds" % x for x in range(1, 51)]
-LIST_DTMFST = ["OFF", "Keyboard", "ANI", "Keyboad + ANI"]
-LIST_SCREV = ["TO (timeout)", "CO (carrier operated)", "SE (search)"]
-LIST_EMCTP = ["TX alarm sound", "TX ANI", "Both"]
-LIST_RINGT = ["Off"] + ["%s seconds" % x for x in range(1, 10)]
-LIST_MDF = ["Frequency", "Channel", "Name"]
-LIST_PONMSG = ["Full", "Message", "Battery voltage"]
-LIST_COLOR = ["Off", "Blue", "Orange", "Purple"]
-LIST_REPS = ["1000 Hz", "1450 Hz", "1750 Hz", "2100Hz"]
-LIST_REPM = ["Off", "Carrier", "CTCSS or DCS", "Tone", "DTMF"]
-LIST_RPTDL = ["Off"] + ["%s ms" % x for x in range(1, 10)]
-LIST_ANIL = ["3", "4", "5"]
-LIST_AB = ["A", "B"]
-LIST_VFOMR = ["Frequency", "Channel"]
-LIST_SHIFT = ["Off", "+", "-"]
-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.
-STIMEOUT = 0.7
-
-# this var controls the verbosity in the debug and by default it's low (False)
-# make it True and you will to get a very verbose debug.log
-debug = False
-
-# Power Levels
-NORMAL_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=25),
-                       chirp_common.PowerLevel("Low", watts=10)]
-UV5001_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=50),
-                       chirp_common.PowerLevel("Low", watts=10)]
-
-# this must be defined globaly
-POWER_LEVELS = None
-
-# valid chars on the LCD, Note that " " (space) is stored as "\xFF"
-VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
-    "`{|}!\"#$%&'()*+,-./:;<=>?@[]^_"
-
-
-##### ID strings #####################################################
-
-# BTECH UV2501 pre-production units
-UV2501pp_fp = "M2C294"
-# BTECH UV2501 pre-production units 2 + and 1st Gen radios
-UV2501pp2_fp = "M29204"
-# B-TECH UV-2501 second generation (2G) radios
-UV2501G2_fp = "BTG214"
-# B-TECH UV-2501 third generation (3G) radios
-UV2501G3_fp = "BTG324"
-
-# B-TECH UV-2501+220 pre-production units
-UV2501_220pp_fp = "M3C281"
-# extra block read for the 2501+220 pre-production units
-# the same for all of this radios so far
-UV2501_220pp_id = "      280528"
-# B-TECH UV-2501+220
-UV2501_220_fp = "M3G201"
-# new variant, let's call it Generation 2
-UV2501_220G2_fp = "BTG211"
-# B-TECH UV-2501+220 third generation (3G)
-UV2501_220G3_fp = "BTG311"
-
-# B-TECH UV-5001 pre-production units + 1st Gen radios
-UV5001pp_fp = "V19204"
-# B-TECH UV-5001 alpha units
-UV5001alpha_fp = "V28204"
-# B-TECH UV-5001 second generation (2G) radios
-UV5001G2_fp = "BTG214"
-# B-TECH UV-5001 second generation (2G2)
-UV5001G22_fp = "V2G204"
-# B-TECH UV-5001 third generation (3G)
-UV5001G3_fp = "BTG304"
-
-# special var to know when we found a BTECH Gen 3
-BTECH3 = [UV2501G3_fp, UV2501_220G3_fp, UV5001G3_fp]
-
-
-# WACCOM Mini-8900
-MINI8900_fp = "M28854"
-
-
-# QYT KT-UV980
-KTUV980_fp = "H28854"
-
-# QYT KT8900
-KT8900_fp = "M29154"
-# New generations KT8900
-KT8900_fp1 = "M2C234"
-KT8900_fp2 = "M2G1F4"
-KT8900_fp3 = "M2G2F4"
-KT8900_fp4 = "M2G304"
-KT8900_fp5 = "M2G314"
-# this radio has an extra ID
-KT8900_id = "      303688"
-
-# KT8900R
-KT8900R_fp = "M3G1F4"
-# Second Generation
-KT8900R_fp1 = "M3G214"
-# another model
-KT8900R_fp2 = "M3C234"
-# another model G4?
-KT8900R_fp3 = "M39164"
-# another model
-KT8900R_fp4 = "M3G314"
-# this radio has an extra ID
-KT8900R_id = "280528"
-
-
-# LUITON LT-588UV
-LT588UV_fp = "V2G1F4"
-# Added by rstrickoff gen 2 id
-LT588UV_fp1 = "V2G214"
-
-
-#### MAGICS
-# for the Waccom Mini-8900
-MSTRING_MINI8900 = "\x55\xA5\xB5\x45\x55\x45\x4d\x02"
-# for the B-TECH UV-2501+220 (including pre production ones)
-MSTRING_220 = "\x55\x20\x15\x12\x12\x01\x4d\x02"
-# for the QYT KT8900 & R
-MSTRING_KT8900 = "\x55\x20\x15\x09\x16\x45\x4D\x02"
-MSTRING_KT8900R = "\x55\x20\x15\x09\x25\x01\x4D\x02"
-# magic string for all other models
-MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
-
-
-def _clean_buffer(radio):
-    """Cleaning the read serial buffer, hard timeout to survive an infinite
-    data stream"""
-
-    # touching the serial timeout to optimize the flushing
-    # restored at the end to the default value
-    radio.pipe.timeout = 0.1
-    dump = "1"
-    datacount = 0
-
-    try:
-        while len(dump) > 0:
-            dump = radio.pipe.read(100)
-            datacount += len(dump)
-            # hard limit to survive a infinite serial data stream
-            # 5 times bigger than a normal rx block (69 bytes)
-            if datacount > 345:
-                seriale = "Please check your serial port selection."
-                raise errors.RadioError(seriale)
-
-        # restore the default serial timeout
-        radio.pipe.timeout = STIMEOUT
-
-    except Exception:
-        raise errors.RadioError("Unknown error cleaning the serial buffer")
-
-
-def _rawrecv(radio, amount):
-    """Raw read from the radio device, less intensive way"""
-
-    data = ""
-
-    try:
-        data = radio.pipe.read(amount)
-
-        # DEBUG
-        if debug is True:
-            LOG.debug("<== (%d) bytes:\n\n%s" %
-                      (len(data), util.hexprint(data)))
-
-        # fail if no data is received
-        if len(data) == 0:
-            raise errors.RadioError("No data received from radio")
-
-        # notice on the logs if short
-        if len(data) < amount:
-            LOG.warn("Short reading %d bytes from the %d requested." %
-                     (len(data), amount))
-
-    except:
-        raise errors.RadioError("Error reading data from radio")
-
-    return data
-
-
-def _send(radio, data):
-    """Send data to the radio device"""
-
-    try:
-        for byte in data:
-            radio.pipe.write(byte)
-            # Some OS (mainly Linux ones) are too fast on the serial and
-            # get the MCU inside the radio stuck in the early stages, this
-            # hits some models more than others.
-            #
-            # To cope with that we introduce a delay on the writes.
-            # Many option have been tested (delaying only after error occures, after short reads, only for linux, ...)
-            # Finally, a static delay was chosen as simplest of all solutions (Michael Wagner, OE4AMW)
-            # (for details, see issue 3993)
-            sleep(0.002)
-
-        # DEBUG
-        if debug is True:
-            LOG.debug("==> (%d) bytes:\n\n%s" %
-                      (len(data), util.hexprint(data)))
-    except:
-        raise errors.RadioError("Error sending data to radio")
-
-
-def _make_frame(cmd, addr, length, data=""):
-    """Pack the info in the headder format"""
-    frame = "\x06" + struct.pack(">BHB", ord(cmd), addr, length)
-    # add the data if set
-    if len(data) != 0:
-        frame += data
-
-    return frame
-
-
-def _recv(radio, addr):
-    """Get data from the radio all at once to lower syscalls load"""
-
-    # Get the full 69 bytes at a time to reduce load
-    # 1 byte ACK + 4 bytes header + 64 bytes of data (BLOCK_SIZE)
-
-    # get the whole block
-    block = _rawrecv(radio, BLOCK_SIZE + 5)
-
-    # basic check
-    if len(block) < (BLOCK_SIZE + 5):
-        raise errors.RadioError("Short read of the block 0x%04x" % addr)
-
-    # checking for the ack
-    if block[0] != ACK_CMD:
-        raise errors.RadioError("Bad ack from radio in block 0x%04x" % addr)
-
-    # header validation
-    c, a, l = struct.unpack(">BHB", block[1:5])
-    if a != addr or l != BLOCK_SIZE or c != ord("X"):
-        LOG.debug("Invalid header for block 0x%04x" % addr)
-        LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
-        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
-
-    # return the data
-    return block[5:]
-
-
-def _start_clone_mode(radio, status):
-    """Put the radio in clone mode and get the ident string, 3 tries"""
-
-    # cleaning the serial buffer
-    _clean_buffer(radio)
-
-    # prep the data to show in the UI
-    status.cur = 0
-    status.msg = "Identifying the radio..."
-    status.max = 3
-    radio.status_fn(status)
-
-    try:
-        for a in range(0, status.max):
-            # Update the UI
-            status.cur = a + 1
-            radio.status_fn(status)
-
-            # send the magic word
-            _send(radio, radio._magic)
-
-            # Now you get a x06 of ACK if all goes well
-            ack = radio.pipe.read(1)
-
-            if ack == "\x06":
-                # DEBUG
-                LOG.info("Magic ACK received")
-                status.cur = status.max
-                radio.status_fn(status)
-
-                return True
-
-        return False
-
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Error sending Magic to radio:\n%s" % e)
-
-
-def _do_ident(radio, status, upload=False):
-    """Put the radio in PROGRAM mode & identify it"""
-    #  set the serial discipline
-    radio.pipe.baudrate = 9600
-    radio.pipe.parity = "N"
-
-    # open the radio into program mode
-    if _start_clone_mode(radio, status) is False:
-        msg = "Radio did not enter clone mode"
-        # warning about old versions of QYT KT8900
-        if radio.MODEL == "KT8900":
-            msg += ". You may want to try it as a WACCOM MINI-8900, there is a"
-            msg += " known variant of this radios that is a clone of it."
-        raise errors.RadioError(msg)
-
-    # Ok, get the ident string
-    ident = _rawrecv(radio, 49)
-
-    # basic check for the ident
-    if len(ident) != 49:
-        raise errors.RadioError("Radio send a short ident block.")
-
-    # check if ident is OK
-    itis = False
-    for fp in radio._fileid:
-        if fp in ident:
-            # got it!
-            itis = True
-            # checking if we are dealing with a Gen 3 BTECH
-            if radio.VENDOR == "BTECH" and fp in BTECH3:
-                radio.btech3 = True
-
-            break
-
-    if itis is False:
-        LOG.debug("Incorrect model ID, got this:\n\n" + util.hexprint(ident))
-        raise errors.RadioError("Radio identification failed.")
-
-    # some radios needs a extra read and check for a code on it, this ones
-    # has the check value in the _id2 var, others simply False
-    if radio._id2 is not False:
-        # lower the timeout here as this radios are reseting due to timeout
-        radio.pipe.timeout = 0.05
-
-        # query & receive the extra ID
-        _send(radio, _make_frame("S", 0x3DF0, 16))
-        id2 = _rawrecv(radio, 21)
-
-        # WARNING !!!!!!
-        # different radios send a response with a different amount of data
-        # it seems that it's padded with \xff, \x20 and some times with \x00
-        # we just care about the first 16, our magic string is in there
-        if len(id2) < 16:
-            raise errors.RadioError("The extra ID is short, aborting.")
-
-        # ok, the correct string must be in the received data
-        if radio._id2 not in id2:
-            LOG.debug("Full *BAD* extra ID on the %s is: \n%s" %
-                      (radio.MODEL, util.hexprint(id2)))
-            raise errors.RadioError("The extra ID is wrong, aborting.")
-
-        # this radios need a extra request/answer here on the upload
-        # the amount of data received depends of the radio type
-        #
-        # also the first block of TX must no have the ACK at the beginning
-        # see _upload for this.
-        if upload is True:
-            # send an ACK
-            _send(radio, ACK_CMD)
-
-            # the amount of data depend on the radio, so far we have two radios
-            # reading two bytes with an ACK at the end and just ONE with just
-            # one byte (QYT KT8900)
-            # the JT-6188 appears a clone of the last, but reads TWO bytes.
-            #
-            # we will read two bytes with a custom timeout to not penalize the
-            # users for this.
-            #
-            # we just check for a response and last byte being a ACK, that is
-            # the common stone for all radios (3 so far)
-            ack = _rawrecv(radio, 2)
-
-            # checking
-            if len(ack) == 0 or ack[-1:] != ACK_CMD:
-                raise errors.RadioError("Radio didn't ACK the upload")
-
-            # restore the default serial timeout
-            radio.pipe.timeout = STIMEOUT
-
-    # DEBUG
-    LOG.info("Positive ident, this is a %s %s" % (radio.VENDOR, radio.MODEL))
-
-    return True
-
-
-def _download(radio):
-    """Get the memory map"""
-
-    # UI progress
-    status = chirp_common.Status()
-
-    # put radio in program mode and identify it
-    _do_ident(radio, status)
-
-    # the models that doesn't have the extra ID have to make a dummy read here
-    if radio._id2 is False:
-        _send(radio, _make_frame("S", 0, BLOCK_SIZE))
-        discard = _rawrecv(radio, BLOCK_SIZE + 5)
-
-        if debug is True:
-            LOG.info("Dummy first block read done, got this:\n\n %s",
-                     util.hexprint(discard))
-
-    # reset the progress bar in the UI
-    status.max = MEM_SIZE / BLOCK_SIZE
-    status.msg = "Cloning from radio..."
-    status.cur = 0
-    radio.status_fn(status)
-
-    # cleaning the serial buffer
-    _clean_buffer(radio)
-
-    data = ""
-    for addr in range(0, MEM_SIZE, BLOCK_SIZE):
-        # sending the read request
-        _send(radio, _make_frame("S", addr, BLOCK_SIZE))
-
-        # read
-        d = _recv(radio, addr)
-
-        # aggregate the data
-        data += d
-
-        # UI Update
-        status.cur = addr / BLOCK_SIZE
-        status.msg = "Cloning from radio..."
-        radio.status_fn(status)
-
-    return data
-
-
-def _upload(radio):
-    """Upload procedure"""
-
-    # The UPLOAD mem is restricted to lower than 0x3100,
-    # so we will overide that here localy
-    MEM_SIZE = 0x3100
-
-    # UI progress
-    status = chirp_common.Status()
-
-    # put radio in program mode and identify it
-    _do_ident(radio, status, True)
-
-    # get the data to upload to radio
-    data = radio.get_mmap()
-
-    # Reset the UI progress
-    status.max = MEM_SIZE / TX_BLOCK_SIZE
-    status.cur = 0
-    status.msg = "Cloning to radio..."
-    radio.status_fn(status)
-
-    # the radios that doesn't have the extra ID 'may' do a dummy write, I found
-    # that leveraging the bad ACK and NOT doing the dummy write is ok, as the
-    # dummy write is accepted (it actually writes to the mem!) by the radio.
-
-    # cleaning the serial buffer
-    _clean_buffer(radio)
-
-    # the fun start here
-    for addr in range(0, MEM_SIZE, TX_BLOCK_SIZE):
-        # getting the block of data to send
-        d = data[addr:addr + TX_BLOCK_SIZE]
-
-        # build the frame to send
-        frame = _make_frame("X", addr, TX_BLOCK_SIZE, d)
-
-        # first block must not send the ACK at the beginning for the
-        # ones that has the extra id, since this have to do a extra step
-        if addr == 0 and radio._id2 is not False:
-            frame = frame[1:]
-
-        # send the frame
-        _send(radio, frame)
-
-        # receiving the response
-        ack = _rawrecv(radio, 1)
-
-        # basic check
-        if len(ack) != 1:
-            raise errors.RadioError("No ACK when writing block 0x%04x" % addr)
-
-        if not ack in "\x06\x05":
-            raise errors.RadioError("Bad ACK writing block 0x%04x:" % addr)
-
-         # UI Update
-        status.cur = addr / TX_BLOCK_SIZE
-        status.msg = "Cloning to radio..."
-        radio.status_fn(status)
-
-
-def model_match(cls, data):
-    """Match the opened/downloaded image to the correct version"""
-    rid = data[0x3f70:0x3f76]
-
-    if rid in cls._fileid:
-        return True
-
-    return False
-
-
-def _decode_ranges(low, high):
-    """Unpack the data in the ranges zones in the memmap and return
-    a tuple with the integer corresponding to the Mhz it means"""
-    ilow = int(low[0]) * 100 + int(low[1]) * 10 + int(low[2])
-    ihigh = int(high[0]) * 100 + int(high[1]) * 10 + int(high[2])
-    ilow *= 1000000
-    ihigh *= 1000000
-
-    return (ilow, ihigh)
-
-
-def _split(rf, f1, f2):
-    """Returns False if the two freqs are in the same band (no split)
-    or True otherwise"""
-
-    # determine if the two freqs are in the same band
-    for low, high in rf.valid_bands:
-        if f1 >= low and f1 <= high and \
-                f2 >= low and f2 <= high:
-            # if the two freqs are on the same Band this is not a split
-            return False
-
-    # if you get here is because the freq pairs are split
-    return False
-
-
-class BTech(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
+
+class BTech(BTechMobileCommon):
     """BTECH's UV-5001 and alike radios"""
-    VENDOR = "BTECH"
-    MODEL = ""
-    IDENT = ""
-    _vhf_range = (130000000, 180000000)
-    _220_range = (210000000, 231000000)
-    _uhf_range = (400000000, 521000000)
-    _upper = 199
-    _magic = MSTRING
-    _fileid = None
-    _id2 = False
-    btech3 = False
-
-    @classmethod
-    def get_prompts(cls):
-        rp = chirp_common.RadioPrompts()
-        rp.experimental = \
-            ('This driver is experimental.\n'
-             '\n'
-             'Please keep a copy of your memories with the original software '
-             'if you treasure them, this driver is new and may contain'
-             ' bugs.\n'
-             '\n'
-             )
-        rp.pre_download = _(dedent("""\
-            Follow these instructions to download your info:
-
-            1 - Turn off your radio
-            2 - Connect your interface cable
-            3 - Turn on your radio
-            4 - Do the download of your radio data
-
-            """))
-        rp.pre_upload = _(dedent("""\
-            Follow these instructions to upload your info:
-
-            1 - Turn off your radio
-            2 - Connect your interface cable
-            3 - Turn on your radio
-            4 - Do the upload of your radio data
-
-            """))
-        return rp
-
-    def get_features(self):
-        """Get the radio's features"""
-
-        # we will use the following var as global
-        global POWER_LEVELS
-
-        rf = chirp_common.RadioFeatures()
-        rf.has_settings = True
-        rf.has_bank = False
-        rf.has_tuning_step = False
-        rf.can_odd_split = True
-        rf.has_name = True
-        rf.has_offset = True
-        rf.has_mode = True
-        rf.has_dtcs = True
-        rf.has_rx_dtcs = True
-        rf.has_dtcs_polarity = True
-        rf.has_ctone = True
-        rf.has_cross = True
-        rf.valid_modes = MODES
-        rf.valid_characters = VALID_CHARS
-        rf.valid_name_length = NAME_LENGTH
-        rf.valid_duplexes = ["", "-", "+", "split", "off"]
-        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
-        rf.valid_cross_modes = [
-            "Tone->Tone",
-            "DTCS->",
-            "->DTCS",
-            "Tone->DTCS",
-            "DTCS->Tone",
-            "->Tone",
-            "DTCS->DTCS"]
-        rf.valid_skips = SKIP_VALUES
-        rf.valid_dtcs_codes = DTCS
-        rf.memory_bounds = (0, self._upper)
-
-        # power levels
-        if self.MODEL == "UV-5001":
-            POWER_LEVELS = UV5001_POWER_LEVELS  # Higher power (50W)
-        else:
-            POWER_LEVELS = NORMAL_POWER_LEVELS  # Lower power (25W)
-
-        rf.valid_power_levels = POWER_LEVELS
-
-        # bands
-        rf.valid_bands = [self._vhf_range, self._uhf_range]
-
-        # 2501+220 & KT8900R
-        if self.MODEL in ["UV-2501+220", "KT8900R"]:
-            rf.valid_bands.append(self._220_range)
-
-        return rf
-
-    def sync_in(self):
-        """Download from radio"""
-        data = _download(self)
-        self._mmap = memmap.MemoryMap(data)
-        self.process_mmap()
-
-    def sync_out(self):
-        """Upload to radio"""
-        try:
-            _upload(self)
-        except errors.RadioError:
-            raise
-        except Exception, e:
-            raise errors.RadioError("Error: %s" % e)
+    BANDS = 2
+    COLOR_LCD = False
+    NAME_LENGTH = 6
 
     def set_options(self):
         """This is to read the options from the image and set it in the
@@ -1048,1546 +3067,6 @@
         # load specific parameters from the radio image
         self.set_options()
 
-    def get_raw_memory(self, number):
-        return repr(self._memobj.memory[number])
-
-    def _decode_tone(self, val):
-        """Parse the tone data to decode from mem, it returns:
-        Mode (''|DTCS|Tone), Value (None|###), Polarity (None,N,R)"""
-        pol = None
-
-        if val in [0, 65535]:
-            return '', None, None
-        elif val > 0x0258:
-            a = val / 10.0
-            return 'Tone', a, pol
-        else:
-            if val > 0x69:
-                index = val - 0x6A
-                pol = "R"
-            else:
-                index = val - 1
-                pol = "N"
-
-            tone = DTCS[index]
-            return 'DTCS', tone, pol
-
-    def _encode_tone(self, memval, mode, val, pol):
-        """Parse the tone data to encode from UI to mem"""
-        if mode == '' or mode is None:
-            memval.set_raw("\x00\x00")
-        elif mode == 'Tone':
-            memval.set_value(val * 10)
-        elif mode == 'DTCS':
-            # detect the index in the DTCS list
-            try:
-                index = DTCS.index(val)
-                if pol == "N":
-                    index += 1
-                else:
-                    index += 0x6A
-                memval.set_value(index)
-            except:
-                msg = "Digital Tone '%d' is not supported" % value
-                LOG.error(msg)
-                raise errors.RadioError(msg)
-        else:
-            msg = "Internal error: invalid mode '%s'" % mode
-            LOG.error(msg)
-            raise errors.InvalidDataError(msg)
-
-    def get_memory(self, number):
-        """Get the mem representation from the radio image"""
-        _mem = self._memobj.memory[number]
-        _names = self._memobj.names[number]
-
-        # Create a high-level memory object to return to the UI
-        mem = chirp_common.Memory()
-
-        # Memory number
-        mem.number = number
-
-        if _mem.get_raw()[0] == "\xFF":
-            mem.empty = True
-            return mem
-
-        # Freq and offset
-        mem.freq = int(_mem.rxfreq) * 10
-        # tx freq can be blank
-        if _mem.get_raw()[4] == "\xFF":
-            # TX freq not set
-            mem.offset = 0
-            mem.duplex = "off"
-        else:
-            # TX freq set
-            offset = (int(_mem.txfreq) * 10) - mem.freq
-            if offset != 0:
-                if _split(self.get_features(), mem.freq, int(_mem.txfreq) * 10):
-                    mem.duplex = "split"
-                    mem.offset = int(_mem.txfreq) * 10
-                elif offset < 0:
-                    mem.offset = abs(offset)
-                    mem.duplex = "-"
-                elif offset > 0:
-                    mem.offset = offset
-                    mem.duplex = "+"
-            else:
-                mem.offset = 0
-
-        # name TAG of the channel
-        mem.name = str(_names.name).rstrip("\xFF").replace("\xFF", " ")
-
-        # power
-        mem.power = POWER_LEVELS[int(_mem.power)]
-
-        # wide/narrow
-        mem.mode = MODES[int(_mem.wide)]
-
-        # skip
-        mem.skip = SKIP_VALUES[_mem.add]
-
-        # tone data
-        rxtone = txtone = None
-        txtone = self._decode_tone(_mem.txtone)
-        rxtone = self._decode_tone(_mem.rxtone)
-        chirp_common.split_tone_decode(mem, txtone, rxtone)
-
-        # Extra
-        mem.extra = RadioSettingGroup("extra", "Extra")
-
-        scramble = RadioSetting("scramble", "Scramble",
-                                RadioSettingValueBoolean(bool(_mem.scramble)))
-        mem.extra.append(scramble)
-
-        bcl = RadioSetting("bcl", "Busy channel lockout",
-                           RadioSettingValueBoolean(bool(_mem.bcl)))
-        mem.extra.append(bcl)
-
-        pttid = RadioSetting("pttid", "PTT ID",
-                             RadioSettingValueList(PTTID_LIST,
-                                                   PTTID_LIST[_mem.pttid]))
-        mem.extra.append(pttid)
-
-        # validating scode
-        scode = _mem.scode if _mem.scode != 15 else 0
-        pttidcode = RadioSetting("scode", "PTT ID signal code",
-                                 RadioSettingValueList(
-                                     PTTIDCODE_LIST,
-                                     PTTIDCODE_LIST[scode]))
-        mem.extra.append(pttidcode)
-
-        optsig = RadioSetting("optsig", "Optional signaling",
-                              RadioSettingValueList(
-                                  OPTSIG_LIST,
-                                  OPTSIG_LIST[_mem.optsig]))
-        mem.extra.append(optsig)
-
-        spmute = RadioSetting("spmute", "Speaker mute",
-                              RadioSettingValueList(
-                                  SPMUTE_LIST,
-                                  SPMUTE_LIST[_mem.spmute]))
-        mem.extra.append(spmute)
-
-        return mem
-
-    def set_memory(self, mem):
-        """Set the memory data in the eeprom img from the UI"""
-        # get the eprom representation of this channel
-        _mem = self._memobj.memory[mem.number]
-        _names = self._memobj.names[mem.number]
-
-        mem_was_empty = False
-        # same method as used in get_memory for determining if mem is empty
-        # doing this BEFORE overwriting it with new values ...
-        if _mem.get_raw()[0] == "\xFF":
-            LOG.debug("This mem was empty before")
-            mem_was_empty = True
-        
-        # if empty memmory
-        if mem.empty:
-            # the channel itself
-            _mem.set_raw("\xFF" * 16)
-            # the name tag
-            _names.set_raw("\xFF" * 16)
-            return
-
-        # frequency
-        _mem.rxfreq = mem.freq / 10
-
-        # duplex
-        if mem.duplex == "+":
-            _mem.txfreq = (mem.freq + mem.offset) / 10
-        elif mem.duplex == "-":
-            _mem.txfreq = (mem.freq - mem.offset) / 10
-        elif mem.duplex == "off":
-            for i in _mem.txfreq:
-                i.set_raw("\xFF")
-        elif mem.duplex == "split":
-            _mem.txfreq = mem.offset / 10
-        else:
-            _mem.txfreq = mem.freq / 10
-
-        # tone data
-        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
-            chirp_common.split_tone_encode(mem)
-        self._encode_tone(_mem.txtone, txmode, txtone, txpol)
-        self._encode_tone(_mem.rxtone, rxmode, rxtone, rxpol)
-
-        # name TAG of the channel
-        if len(mem.name) < NAME_LENGTH:
-            # we must pad to NAME_LENGTH chars, " " = "\xFF"
-            mem.name = str(mem.name).ljust(NAME_LENGTH, " ")
-        _names.name = str(mem.name).replace(" ", "\xFF")
-
-        # power, # default power level is high
-        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
-
-        # wide/narrow
-        _mem.wide = MODES.index(mem.mode)
-
-        # scan add property
-        _mem.add = SKIP_VALUES.index(mem.skip)
-
-        # reseting unknowns, this have to be set by hand
-        _mem.unknown0 = 0
-        _mem.unknown1 = 0
-        _mem.unknown2 = 0
-        _mem.unknown3 = 0
-        _mem.unknown4 = 0
-        _mem.unknown5 = 0
-        _mem.unknown6 = 0
-
-        # extra settings
-        if len(mem.extra) > 0:
-            # there are setting, parse
-            LOG.debug("Extra-Setting supplied. Setting them.")
-            for setting in mem.extra:
-                setattr(_mem, setting.get_name(), setting.value)
-        else:
-            if mem.empty:
-                LOG.debug("New mem is empty.")
-            else:
-                LOG.debug("New mem is NOT empty")
-                # set extra-settings to default ONLY when apreviously empty or
-                # deleted memory was edited to prevent errors such as #4121
-                if mem_was_empty :
-                    LOG.debug("old mem was empty. Setting default for extras.")
-                    _mem.spmute = 0
-                    _mem.optsig = 0
-                    _mem.scramble = 0
-                    _mem.bcl = 0
-                    _mem.pttid = 0
-                    _mem.scode = 0
-
-        return mem
-
-    def get_settings(self):
-        """Translate the bit in the mem_struct into settings in the UI"""
-        _mem = self._memobj
-        basic = RadioSettingGroup("basic", "Basic Settings")
-        advanced = RadioSettingGroup("advanced", "Advanced Settings")
-        other = RadioSettingGroup("other", "Other Settings")
-        work = RadioSettingGroup("work", "Work Mode Settings")
-        fm_presets = RadioSettingGroup("fm_presets", "FM Presets")
-        top = RadioSettings(basic, advanced, other, work, fm_presets)
-
-        # Basic
-        tdr = RadioSetting("settings.tdr", "Transceiver dual receive",
-                           RadioSettingValueBoolean(_mem.settings.tdr))
-        basic.append(tdr)
-
-        sql = RadioSetting("settings.sql", "Squelch level",
-                           RadioSettingValueInteger(0, 9, _mem.settings.sql))
-        basic.append(sql)
-
-        tot = RadioSetting("settings.tot", "Time out timer",
-                           RadioSettingValueList(LIST_TOT, LIST_TOT[
-                               _mem.settings.tot]))
-        basic.append(tot)
-
-        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
-            apo = RadioSetting("settings.apo", "Auto power off timer",
-                               RadioSettingValueList(LIST_APO, LIST_APO[
-                                   _mem.settings.apo]))
-            basic.append(apo)
-        else:
-            toa = RadioSetting("settings.apo", "Time out alert timer",
-                               RadioSettingValueList(LIST_TOA, LIST_TOA[
-                                   _mem.settings.apo]))
-            basic.append(toa)
-
-        abr = RadioSetting("settings.abr", "Backlight timer",
-                           RadioSettingValueList(LIST_ABR, LIST_ABR[
-                               _mem.settings.abr]))
-        basic.append(abr)
-
-        beep = RadioSetting("settings.beep", "Key beep",
-                            RadioSettingValueBoolean(_mem.settings.beep))
-        basic.append(beep)
-
-        dtmfst = RadioSetting("settings.dtmfst", "DTMF side tone",
-                              RadioSettingValueList(LIST_DTMFST, LIST_DTMFST[
-                                  _mem.settings.dtmfst]))
-        basic.append(dtmfst)
-
-        prisc = RadioSetting("settings.prisc", "Priority scan",
-                             RadioSettingValueBoolean(_mem.settings.prisc))
-        basic.append(prisc)
-
-        prich = RadioSetting("settings.prich", "Priority channel",
-                             RadioSettingValueInteger(0, 199,
-                                 _mem.settings.prich))
-        basic.append(prich)
-
-        screv = RadioSetting("settings.screv", "Scan resume method",
-                             RadioSettingValueList(LIST_SCREV, LIST_SCREV[
-                                 _mem.settings.screv]))
-        basic.append(screv)
-
-        pttlt = RadioSetting("settings.pttlt", "PTT transmit delay",
-                             RadioSettingValueInteger(0, 30,
-                                 _mem.settings.pttlt))
-        basic.append(pttlt)
-
-        emctp = RadioSetting("settings.emctp", "Alarm mode",
-                             RadioSettingValueList(LIST_EMCTP, LIST_EMCTP[
-                                 _mem.settings.emctp]))
-        basic.append(emctp)
-
-        emcch = RadioSetting("settings.emcch", "Alarm channel",
-                             RadioSettingValueInteger(0, 199,
-                                 _mem.settings.emcch))
-        basic.append(emcch)
-
-        ringt = RadioSetting("settings.ringt", "Ring time",
-                             RadioSettingValueList(LIST_RINGT, LIST_RINGT[
-                                 _mem.settings.ringt]))
-        basic.append(ringt)
-
-        camdf = RadioSetting("settings.camdf", "Display mode A",
-                             RadioSettingValueList(LIST_MDF, LIST_MDF[
-                                 _mem.settings.camdf]))
-        basic.append(camdf)
-
-        cbmdf = RadioSetting("settings.cbmdf", "Display mode B",
-                             RadioSettingValueList(LIST_MDF, LIST_MDF[
-                                 _mem.settings.cbmdf]))
-        basic.append(cbmdf)
-
-        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
-           sync = RadioSetting("settings.sync", "A/B channel sync",
-                               RadioSettingValueBoolean(_mem.settings.sync))
-           basic.append(sync)
-        else:
-           autolk = RadioSetting("settings.sync", "Auto keylock",
-                                 RadioSettingValueBoolean(_mem.settings.sync))
-           basic.append(autolk)
-
-        ponmsg = RadioSetting("settings.ponmsg", "Power-on message",
-                              RadioSettingValueList(LIST_PONMSG, LIST_PONMSG[
-                                  _mem.settings.ponmsg]))
-        basic.append(ponmsg)
-
-        wtled = RadioSetting("settings.wtled", "Standby backlight Color",
-                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
-                                 _mem.settings.wtled]))
-        basic.append(wtled)
-
-        rxled = RadioSetting("settings.rxled", "RX backlight Color",
-                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
-                                 _mem.settings.rxled]))
-        basic.append(rxled)
-
-        txled = RadioSetting("settings.txled", "TX backlight Color",
-                             RadioSettingValueList(LIST_COLOR, LIST_COLOR[
-                                 _mem.settings.txled]))
-        basic.append(txled)
-
-        anil = RadioSetting("settings.anil", "ANI length",
-                            RadioSettingValueList(LIST_ANIL, LIST_ANIL[
-                                _mem.settings.anil]))
-        basic.append(anil)
-
-        reps = RadioSetting("settings.reps", "Relay signal (tone burst)",
-                            RadioSettingValueList(LIST_REPS, LIST_REPS[
-                                _mem.settings.reps]))
-        basic.append(reps)
-
-        repm = RadioSetting("settings.repm", "Relay condition",
-                            RadioSettingValueList(LIST_REPM, LIST_REPM[
-                                _mem.settings.repm]))
-        basic.append(repm)
-
-        if self.MODEL in ("UV-2501", "UV-2501+220", "UV-5001"):
-            tdrab = RadioSetting("settings.tdrab", "TDR return time",
-                                 RadioSettingValueList(LIST_ABR, LIST_ABR[
-                                     _mem.settings.tdrab]))
-            basic.append(tdrab)
-
-            ste = RadioSetting("settings.ste", "Squelch tail eliminate",
-                               RadioSettingValueBoolean(_mem.settings.ste))
-            basic.append(ste)
-
-            rpste = RadioSetting("settings.rpste", "Repeater STE",
-                                 RadioSettingValueList(LIST_RINGT, LIST_RINGT[
-                                     _mem.settings.rpste]))
-            basic.append(rpste)
-
-            rptdl = RadioSetting("settings.rptdl", "Repeater STE delay",
-                                 RadioSettingValueList(LIST_RPTDL, LIST_RPTDL[
-                                     _mem.settings.rptdl]))
-            basic.append(rptdl)
-
-        if str(_mem.fingerprint.fp) in BTECH3:
-
-            mgain = RadioSetting("settings.mgain", "Mic gain",
-                                 RadioSettingValueInteger(0, 120,
-                                     _mem.settings.mgain))
-            basic.append(mgain)
-
-            dtmfg = RadioSetting("settings.dtmfg", "DTMF gain",
-                                 RadioSettingValueInteger(0, 60,
-                                     _mem.settings.dtmfg))
-            basic.append(dtmfg)
-
-        # Advanced
-        def _filter(name):
-            filtered = ""
-            for char in str(name):
-                if char in VALID_CHARS:
-                    filtered += char
-                else:
-                    filtered += " "
-            return filtered
-
-        _msg = self._memobj.poweron_msg
-        line1 = RadioSetting("poweron_msg.line1", "Power-on message line 1",
-                             RadioSettingValueString(0, 6, _filter(
-                                 _msg.line1)))
-        advanced.append(line1)
-        line2 = RadioSetting("poweron_msg.line2", "Power-on message line 2",
-                             RadioSettingValueString(0, 6, _filter(
-                                 _msg.line2)))
-        advanced.append(line2)
-
-        if self.MODEL in ("UV-2501", "UV-5001"):
-            vfomren = RadioSetting("settings2.vfomren", "VFO/MR switching",
-                                   RadioSettingValueBoolean(
-                                       _mem.settings2.vfomren))
-            advanced.append(vfomren)
-
-            reseten = RadioSetting("settings2.reseten", "RESET",
-                                   RadioSettingValueBoolean(
-                                       _mem.settings2.reseten))
-            advanced.append(reseten)
-
-            menuen = RadioSetting("settings2.menuen", "Menu",
-                                  RadioSettingValueBoolean(
-                                      _mem.settings2.menuen))
-            advanced.append(menuen)
-
-        # Other
-        def convert_bytes_to_limit(bytes):
-            limit = ""
-            for byte in bytes:
-                if byte < 10:
-                    limit += chr(byte + 0x30)
-                else:
-                    break
-            return limit
-
-        if self.MODEL in ["UV-2501+220", "KT8900R"]:
-            _ranges = self._memobj.ranges220
-            ranges = "ranges220"
-        else:
-            _ranges = self._memobj.ranges
-            ranges = "ranges"
-
-        _limit = convert_bytes_to_limit(_ranges.vhf_low)
-        val = RadioSettingValueString(0, 3, _limit)
-        val.set_mutable(False)
-        vhf_low = RadioSetting("%s.vhf_low" % ranges, "VHF low", val)
-        other.append(vhf_low)
-
-        _limit = convert_bytes_to_limit(_ranges.vhf_high)
-        val = RadioSettingValueString(0, 3, _limit)
-        val.set_mutable(False)
-        vhf_high = RadioSetting("%s.vhf_high" % ranges, "VHF high", val)
-        other.append(vhf_high)
-
-        if self.MODEL in ["UV-2501+220", "KT8900R"]:
-            _limit = convert_bytes_to_limit(_ranges.vhf2_low)
-            val = RadioSettingValueString(0, 3, _limit)
-            val.set_mutable(False)
-            vhf2_low = RadioSetting("%s.vhf2_low" % ranges, "VHF2 low", val)
-            other.append(vhf2_low)
-
-            _limit = convert_bytes_to_limit(_ranges.vhf2_high)
-            val = RadioSettingValueString(0, 3, _limit)
-            val.set_mutable(False)
-            vhf2_high = RadioSetting("%s.vhf2_high" % ranges, "VHF2 high", val)
-            other.append(vhf2_high)
-
-        _limit = convert_bytes_to_limit(_ranges.uhf_low)
-        val = RadioSettingValueString(0, 3, _limit)
-        val.set_mutable(False)
-        uhf_low = RadioSetting("%s.uhf_low" % ranges, "UHF low", val)
-        other.append(uhf_low)
-
-        _limit = convert_bytes_to_limit(_ranges.uhf_high)
-        val = RadioSettingValueString(0, 3, _limit)
-        val.set_mutable(False)
-        uhf_high = RadioSetting("%s.uhf_high" % ranges, "UHF high", val)
-        other.append(uhf_high)
-
-        val = RadioSettingValueString(0, 6, _filter(_mem.fingerprint.fp))
-        val.set_mutable(False)
-        fp = RadioSetting("fingerprint.fp", "Fingerprint", val)
-        other.append(fp)
-
-        # Work
-        dispab = RadioSetting("settings2.dispab", "Display",
-                              RadioSettingValueList(LIST_AB,LIST_AB[
-                                  _mem.settings2.dispab]))
-        work.append(dispab)
-
-        vfomr = RadioSetting("settings2.vfomr", "VFO/MR mode",
-                             RadioSettingValueList(LIST_VFOMR,LIST_VFOMR[
-                                 _mem.settings2.vfomr]))
-        work.append(vfomr)
-
-        keylock = RadioSetting("settings2.keylock", "Keypad lock",
-                           RadioSettingValueBoolean(_mem.settings2.keylock))
-        work.append(keylock)
-
-        mrcha = RadioSetting("settings2.mrcha", "MR A channel",
-                             RadioSettingValueInteger(0, 199,
-                                 _mem.settings2.mrcha))
-        work.append(mrcha)
-
-        mrchb = RadioSetting("settings2.mrchb", "MR B channel",
-                             RadioSettingValueInteger(0, 199,
-                                 _mem.settings2.mrchb))
-        work.append(mrchb)
-
-        def convert_bytes_to_freq(bytes):
-            real_freq = 0
-            for byte in bytes:
-                real_freq = (real_freq * 10) + byte
-            return chirp_common.format_freq(real_freq * 10)
-
-        def my_validate(value):
-            value = chirp_common.parse_freq(value)
-            if "+220" in self.MODEL:
-                if 180000000 <= value and value < 210000000:
-                    msg = ("Can't be between 180.00000-210.00000")
-                    raise InvalidValueError(msg)
-                elif 231000000 <= value and value < 400000000:
-                    msg = ("Can't be between 231.00000-400.00000")
-                    raise InvalidValueError(msg)
-            elif "8900R" in self.MODEL:
-                if 180000000 <= value and value < 240000000:
-                    msg = ("Can't be between 180.00000-240.00000")
-                    raise InvalidValueError(msg)
-                elif 271000000 <= value and value < 400000000:
-                    msg = ("Can't be between 271.00000-400.00000")
-                    raise InvalidValueError(msg)
-            elif 180000000 <= value and value < 400000000:
-                msg = ("Can't be between 180.00000-400.00000")
-                raise InvalidValueError(msg)
-            return chirp_common.format_freq(value)
-
-        def apply_freq(setting, obj):
-            value = chirp_common.parse_freq(str(setting.value)) / 10
-            for i in range(7, -1, -1):
-                obj.freq[i] = value % 10
-                value /= 10
-
-        val1a = RadioSettingValueString(0, 10, convert_bytes_to_freq(
-                                        _mem.vfo.a.freq))
-        val1a.set_validate_callback(my_validate)
-        vfoafreq = RadioSetting("vfo.a.freq", "VFO A frequency", val1a)
-        vfoafreq.set_apply_callback(apply_freq, _mem.vfo.a)
-        work.append(vfoafreq)
-
-        val1b = RadioSettingValueString(0, 10, convert_bytes_to_freq(
-                                        _mem.vfo.b.freq))
-        val1b.set_validate_callback(my_validate)
-        vfobfreq = RadioSetting("vfo.b.freq", "VFO B frequency", val1b)
-        vfobfreq.set_apply_callback(apply_freq, _mem.vfo.b)
-        work.append(vfobfreq)
-
-        vfoashiftd = RadioSetting("vfo.a.shiftd", "VFO A shift",
-                                  RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
-                                      _mem.vfo.a.shiftd]))
-        work.append(vfoashiftd)
-
-        vfobshiftd = RadioSetting("vfo.b.shiftd", "VFO B shift",
-                                  RadioSettingValueList(LIST_SHIFT, LIST_SHIFT[
-                                      _mem.vfo.b.shiftd]))
-        work.append(vfobshiftd)
-
-        def convert_bytes_to_offset(bytes):
-            real_offset = 0
-            for byte in bytes:
-                real_offset = (real_offset * 10) + byte
-            return chirp_common.format_freq(real_offset * 10000)
-
-        def apply_offset(setting, obj):
-            value = chirp_common.parse_freq(str(setting.value)) / 10000
-            for i in range(3, -1, -1):
-                obj.offset[i] = value % 10
-                value /= 10
-
-        val1a = RadioSettingValueString(0, 10, convert_bytes_to_offset(
-                                        _mem.vfo.a.offset))
-        vfoaoffset = RadioSetting("vfo.a.offset",
-                                  "VFO A offset (0.00-99.95)", val1a)
-        vfoaoffset.set_apply_callback(apply_offset, _mem.vfo.a)
-        work.append(vfoaoffset)
-
-        val1b = RadioSettingValueString(0, 10, convert_bytes_to_offset(
-                                        _mem.vfo.b.offset))
-        vfoboffset = RadioSetting("vfo.b.offset",
-                                  "VFO B offset (0.00-99.95)", val1b)
-        vfoboffset.set_apply_callback(apply_offset, _mem.vfo.b)
-        work.append(vfoboffset)
-
-        vfoatxp = RadioSetting("vfo.a.power", "VFO A power",
-                                RadioSettingValueList(LIST_TXP,LIST_TXP[
-                                    _mem.vfo.a.power]))
-        work.append(vfoatxp)
-
-        vfobtxp = RadioSetting("vfo.b.power", "VFO B power",
-                                RadioSettingValueList(LIST_TXP,LIST_TXP[
-                                    _mem.vfo.b.power]))
-        work.append(vfobtxp)
-
-        vfoawide = RadioSetting("vfo.a.wide", "VFO A bandwidth",
-                                RadioSettingValueList(LIST_WIDE,LIST_WIDE[
-                                    _mem.vfo.a.wide]))
-        work.append(vfoawide)
-
-        vfobwide = RadioSetting("vfo.b.wide", "VFO B bandwidth",
-                                RadioSettingValueList(LIST_WIDE,LIST_WIDE[
-                                    _mem.vfo.b.wide]))
-        work.append(vfobwide)
-
-        vfoastep = RadioSetting("vfo.a.step", "VFO A step",
-                                RadioSettingValueList(LIST_STEP,LIST_STEP[
-                                    _mem.vfo.a.step]))
-        work.append(vfoastep)
-
-        vfobstep = RadioSetting("vfo.b.step", "VFO B step",
-                                RadioSettingValueList(LIST_STEP,LIST_STEP[
-                                    _mem.vfo.b.step]))
-        work.append(vfobstep)
-
-        vfoaoptsig = RadioSetting("vfo.a.optsig", "VFO A optional signal",
-                                  RadioSettingValueList(OPTSIG_LIST,
-                                      OPTSIG_LIST[_mem.vfo.a.optsig]))
-        work.append(vfoaoptsig)
-
-        vfoboptsig = RadioSetting("vfo.b.optsig", "VFO B optional signal",
-                                  RadioSettingValueList(OPTSIG_LIST,
-                                      OPTSIG_LIST[_mem.vfo.b.optsig]))
-        work.append(vfoboptsig)
-
-        vfoaspmute = RadioSetting("vfo.a.spmute", "VFO A speaker mute",
-                                  RadioSettingValueList(SPMUTE_LIST,
-                                      SPMUTE_LIST[_mem.vfo.a.spmute]))
-        work.append(vfoaspmute)
-
-        vfobspmute = RadioSetting("vfo.b.spmute", "VFO B speaker mute",
-                                  RadioSettingValueList(SPMUTE_LIST,
-                                      SPMUTE_LIST[_mem.vfo.b.spmute]))
-        work.append(vfobspmute)
-
-        vfoascr = RadioSetting("vfo.a.scramble", "VFO A scramble",
-                               RadioSettingValueBoolean(_mem.vfo.a.scramble))
-        work.append(vfoascr)
-
-        vfobscr = RadioSetting("vfo.b.scramble", "VFO B scramble",
-                               RadioSettingValueBoolean(_mem.vfo.b.scramble))
-        work.append(vfobscr)
-
-        vfoascode = RadioSetting("vfo.a.scode", "VFO A PTT-ID",
-                                 RadioSettingValueList(PTTIDCODE_LIST,
-                                     PTTIDCODE_LIST[_mem.vfo.a.scode]))
-        work.append(vfoascode)
-
-        vfobscode = RadioSetting("vfo.b.scode", "VFO B PTT-ID",
-                                 RadioSettingValueList(PTTIDCODE_LIST,
-                                     PTTIDCODE_LIST[_mem.vfo.b.scode]))
-        work.append(vfobscode)
-
-        pttid = RadioSetting("settings.pttid", "PTT ID",
-                             RadioSettingValueList(PTTID_LIST,
-                                 PTTID_LIST[_mem.settings.pttid]))
-        work.append(pttid)
-
-        #FM presets
-        def fm_validate(value):
-            if value == 0:
-                return chirp_common.format_freq(value)
-            if not (87.5 <= value and value <= 108.0):  # 87.5-108MHz
-                msg = ("FM-Preset-Frequency: Must be between 87.5 and 108 MHz")
-                raise InvalidValueError(msg)
-            return value
-
-        def apply_fm_preset_name(setting, obj):
-            valstring = str (setting.value)
-            for i in range(0,6):
-                if valstring[i] in VALID_CHARS:
-                    obj[i] = valstring[i]
-                else:
-                    obj[i] = '0xff'
-
-        def apply_fm_freq(setting, obj):
-            value = chirp_common.parse_freq(str(setting.value)) / 10
-            for i in range(7, -1, -1):
-                obj.freq[i] = value % 10
-                value /= 10
-        
-        _presets = self._memobj.fm_radio_preset
-        i = 1
-        for preset in _presets:
-            line = RadioSetting("fm_presets_"+ str(i), "Station name " + str(i),
-                                RadioSettingValueString(0, 6, _filter(
-                                    preset.broadcast_station_name)))
-            line.set_apply_callback(apply_fm_preset_name, 
-                                    preset.broadcast_station_name)
-            
-            val = RadioSettingValueFloat(0, 108, convert_bytes_to_freq(preset.freq))
-            fmfreq = RadioSetting("fm_presets_"+ str(i) + "_freq", "Frequency "+ str(i), val)
-            val.set_validate_callback(fm_validate)
-            fmfreq.set_apply_callback(apply_fm_freq, preset)
-            fm_presets.append(line)
-            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):
-        _settings = self._memobj.settings
-        for element in settings:
-            if not isinstance(element, RadioSetting):
-                if element.get_name() == "fm_preset":
-                    self._set_fm_preset(element)
-                else:
-                    self.set_settings(element)
-                    continue
-            else:
-                try:
-                    name = element.get_name()
-                    if "." in name:
-                        bits = name.split(".")
-                        obj = self._memobj
-                        for bit in bits[:-1]:
-                            if "/" in bit:
-                                bit, index = bit.split("/", 1)
-                                index = int(index)
-                                obj = getattr(obj, bit)[index]
-                            else:
-                                obj = getattr(obj, bit)
-                        setting = bits[-1]
-                    else:
-                        obj = _settings
-                        setting = element.get_name()
-
-                    if element.has_apply_callback():
-                        LOG.debug("Using apply callback")
-                        element.run_apply_callback()
-                    elif element.value.get_mutable():
-                        LOG.debug("Setting %s = %s" % (setting, element.value))
-                        setattr(obj, setting, element.value)
-                except Exception, e:
-                    LOG.debug(element.get_name())
-                    raise
-
-    @classmethod
-    def match_model(cls, filedata, filename):
-        match_size = False
-        match_model = False
-
-        # testing the file data size
-        if len(filedata) == MEM_SIZE:
-            match_size = True
-
-        # testing the firmware model fingerprint
-        match_model = model_match(cls, filedata)
-
-        if match_size and match_model:
-            return True
-        else:
-            return False
-
 
 # Declaring Aliases (Clones of the real radios)
 class JT2705M(chirp_common.Alias):
@@ -2630,6 +3109,7 @@
 class UV2501_220(BTech):
     """Baofeng Tech UV2501+220"""
     MODEL = "UV-2501+220"
+    BANDS = 3
     _magic = MSTRING_220
     _id2 = UV2501_220pp_id
     _fileid = [UV2501_220G3_fp,
@@ -2647,6 +3127,8 @@
                UV5001G2_fp,
                UV5001alpha_fp,
                UV5001pp_fp]
+    _power_levels = [chirp_common.PowerLevel("High", watts=50),
+                     chirp_common.PowerLevel("Low", watts=10)]
 
 
 @directory.register
@@ -2698,6 +3180,7 @@
     """QYT KT8900R"""
     VENDOR = "QYT"
     MODEL = "KT8900R"
+    BANDS = 3
     _vhf_range = (136000000, 175000000)
     _220_range = (240000000, 271000000)
     _uhf_range = (400000000, 481000000)
@@ -2720,3 +3203,377 @@
     _magic = MSTRING_KT8900
     _fileid = [LT588UV_fp,
                LT588UV_fp1]
+    _power_levels = [chirp_common.PowerLevel("High", watts=60),
+                     chirp_common.PowerLevel("Low", watts=10)]
+
+
+COLOR_MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+  lbcd rxfreq[4];
+  lbcd txfreq[4];
+  ul16 rxtone;
+  ul16 txtone;
+  u8 unknown0:4,
+     scode:4;
+  u8 unknown1:2,
+     spmute:1,
+     unknown2:3,
+     optsig:2;
+  u8 unknown3:3,
+     scramble:1,
+     unknown4:3,
+     power:1;
+  u8 unknown5:1,
+     wide:1,
+     unknown6:2,
+     bcl:1,
+     add:1,
+     pttid:2;
+} memory[200];
+
+#seekto 0x0E00;
+struct {
+  u8 tmr;
+  u8 unknown1;      
+  u8 sql;           
+  u8 unknown2[2];   
+  u8 tot;           
+  u8 apo;           
+  u8 unknown3;      
+  u8 abr;           
+  u8 beep;          
+  u8 unknown4[4];   
+  u8 dtmfst;        
+  u8 unknown5[2];   
+  u8 screv;         
+  u8 unknown6[2];
+  u8 pttid;
+  u8 pttlt;
+  u8 unknown7;
+  u8 emctp;         
+  u8 emcch;         
+  u8 sigbp;         
+  u8 unknown8;
+  u8 camdf;         
+  u8 cbmdf;         
+  u8 ccmdf;         
+  u8 cdmdf;         
+  u8 langua;
+  u8 sync;          // BTech radios use this as the display sync
+                    // setting, other radios use this as the auto
+                    // keypad lock setting
+  u8 mainfc;
+  u8 mainbc;        
+  u8 menufc;        
+  u8 menubc;        
+  u8 stafc;         
+  u8 stabc;         
+  u8 sigfc;         
+  u8 sigbc;         
+  u8 rxfc;          
+  u8 txfc;          
+  u8 txdisp;
+  u8 unknown9[5];
+  u8 anil;
+  u8 reps;
+  u8 repm;
+  u8 tmrmr;
+  u8 ste;
+  u8 rpste;
+  u8 rptdl;
+  u8 dtmfg;
+  u8 mgain;
+  u8 skiptx;
+  u8 scmode;
+} settings;
+
+#seekto 0x0E80;
+struct {
+  u8 unknown1;
+  u8 vfomr;
+  u8 keylock;
+  u8 unknown2;
+  u8 unknown3:4,
+     vfomren:1,
+     unknown4:1,
+     reseten:1,
+     menuen:1;
+  u8 unknown5[11];
+  u8 dispab;        
+  u8 unknown6[2];   
+  u8 menu;          
+  u8 unknown7[7];   
+  u8 vfomra;        
+  u8 vfomrb;        
+  u8 vfomrc;        
+  u8 vfomrd;        
+  u8 mrcha;         
+  u8 mrchb;         
+  u8 mrchc;         
+  u8 mrchd;
+} settings2;
+
+struct settings_vfo {
+  u8 freq[8];
+  u8 offset[6];
+  u8 unknown2[2];
+  ul16 rxtone;
+  ul16 txtone;
+  u8 scode;
+  u8 spmute;
+  u8 optsig;
+  u8 scramble;
+  u8 wide;
+  u8 power;
+  u8 shiftd;
+  u8 step;
+  u8 unknown3[4];
+};
+
+#seekto 0x0F00;
+struct {
+  struct settings_vfo a;
+  struct settings_vfo b;
+  struct settings_vfo c;
+  struct settings_vfo d;
+} vfo;
+
+#seekto 0x0F80;
+struct {
+  char line1[8];
+  char line2[8];
+  char line3[8];
+  char line4[8];
+  char line5[8];
+  char line6[8];
+  char line7[8];
+  char line8[8];
+} poweron_msg;
+
+#seekto 0x1000;
+struct {
+  char name[8];
+  u8 unknown1[8];
+} 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 0x3D80;
+struct {
+  u8 vhf_low[3];
+  u8 vhf_high[3];
+  u8 unknown1[4];
+  u8 unknown2[6];
+  u8 vhf2_low[3];
+  u8 vhf2_high[3];
+  u8 unknown3[4];
+  u8 unknown4[6];
+  u8 uhf_low[3];
+  u8 uhf_high[3];
+  u8 unknown5[4];
+  u8 unknown6[6];
+  u8 uhf2_low[3];
+  u8 uhf2_high[3];
+} ranges;
+
+#seekto 0x3F70;
+struct {
+  char fp[6];
+} fingerprint;
+
+"""
+
+
+class BTechColor(BTechMobileCommon):
+    """BTECH's Color LCD Mobile and alike radios"""
+    COLOR_LCD = True
+    NAME_LENGTH = 8
+
+    def process_mmap(self):
+        """Process the mem map into the mem object"""
+
+        # Get it
+        self._memobj = bitwise.parse(COLOR_MEM_FORMAT, self._mmap)
+
+        # load specific parameters from the radio image
+        self.set_options()
+
+    def set_options(self):
+        """This is to read the options from the image and set it in the
+        environment, for now just the limits of the freqs in the VHF/UHF
+        ranges"""
+
+        # setting the correct ranges for each radio type
+        ranges = self._memobj.ranges
+
+        # the normal dual bands
+        vhf = _decode_ranges(ranges.vhf_low, ranges.vhf_high)
+        uhf = _decode_ranges(ranges.uhf_low, ranges.uhf_high)
+
+        # DEBUG
+        LOG.info("Radio ranges: VHF %d to %d" % vhf)
+        LOG.info("Radio ranges: UHF %d to %d" % uhf)
+
+        # the additional bands
+        if self.MODEL in ["KT8900D", ]:
+            # 200Mhz band
+            vhf2 = _decode_ranges(ranges.vhf2_low, ranges.vhf2_high)
+            LOG.info("Radio ranges: VHF(220) %d to %d" % vhf2)
+            self._220_range = vhf2
+
+            # 350Mhz band
+            uhf2 = _decode_ranges(ranges.uhf2_low, ranges.uhf2_high)
+            LOG.info("Radio ranges: UHF(350) %d to %d" % uhf2)
+            self._350_range = uhf2
+
+        # set the class with the real data
+        self._vhf_range = vhf
+        self._uhf_range = uhf
+    
+
+ at directory.register
+class KT7900D(BTechColor):
+    """QYT KT7900D"""
+    VENDOR = "QYT"
+    MODEL = "KT7900D"
+    BANDS = 4
+    _vhf_range = (136000000, 175000000)
+    _220_range = (200000000, 271000000)
+    _uhf_range = (400000000, 481000000)
+    _350_range = (350000000, 371000000)
+    _magic = MSTRING_KT8900D
+    _fileid = [KT7900D_fp, ]
+
+
+ at directory.register
+class KT8900D(BTechColor):
+    """QYT KT8900D"""
+    VENDOR = "QYT"
+    MODEL = "KT8900D"
+    BANDS = 2
+    _vhf_range = (136000000, 175000000)
+    _uhf_range = (400000000, 481000000)
+    _magic = MSTRING_KT8900D
+    _fileid = [KT8900D_fp, ]



More information about the chirp_devel mailing list