[chirp_devel] [PATCH 1 of 2] Divide different unrelated radio in different sources

Marco Filippi IZ3GME
Thu Oct 11 15:35:33 PDT 2012


# HG changeset patch
# User Marco Filippi <iz3gme.marco at gmail.com>
# Date 1349969176 -7200
# Node ID baf288011d09cf65f8e5afb694c47884af64c54b
# Parent  c03ed75f179a183ce8f1ae537bcc690ca4de3c71
Divide different unrelated radio in different sources
no functional changes
in preparation for Feature #285

diff -r c03ed75f179a -r baf288011d09 chirp/baofeng_uv3r.py
--- /dev/null	gio gen 01 00:00:00 1970 +0000
+++ b/chirp/baofeng_uv3r.py	gio ott 11 17:26:16 2012 +0200
@@ -0,0 +1,292 @@
+# Copyright 2011 Dan Smith <dsmith at danplanet.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import struct
+import time
+import os
+from chirp import util, chirp_common, bitwise, memmap, errors, directory
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+                RadioSettingValueBoolean, RadioSettingValueList
+
+if os.getenv("CHIRP_DEBUG"):
+    DEBUG = True
+else:
+    DEBUG = False
+
+def _uv3r_prep(radio):
+    radio.pipe.write("\x05PROGRAM")
+    ack = radio.pipe.read(1)
+    if ack != "\x06":
+        raise errors.RadioError("Radio did not ACK first command")
+
+    radio.pipe.write("\x02")
+    ident = radio.pipe.read(8)
+    if len(ident) != 8:
+        print util.hexprint(ident)
+        raise errors.RadioError("Radio did not send identification")
+
+    radio.pipe.write("\x06")
+    if radio.pipe.read(1) != "\x06":
+        raise errors.RadioError("Radio did not ACK ident")
+
+def uv3r_prep(radio):
+    """Do the UV3R identification dance"""
+    for _i in range(0, 10):
+        try:
+            return _uv3r_prep(radio)
+        except errors.RadioError, e:
+            time.sleep(1)
+
+    raise e
+
+def uv3r_download(radio):
+    """Talk to a UV3R and do a download"""
+    try:
+        uv3r_prep(radio)
+        return do_download(radio, 0x0000, 0x0E40, 0x0010)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+def uv3r_upload(radio):
+    """Talk to a UV3R and do an upload"""
+    try:
+        uv3r_prep(radio)
+        return do_upload(radio, 0x0000, 0x0E40, 0x0010)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+UV3R_MEM_FORMAT = """
+#seekto 0x0010;
+struct {
+  lbcd rx_freq[4];
+  u8 rxtone;
+  lbcd offset[4];
+  u8 txtone;
+  u8 ishighpower:1,
+     iswide:1,
+     dtcsinvt:1,
+     unknown1:1,
+     dtcsinvr:1,
+     unknown2:1,
+     duplex:2;
+  u8 unknown;
+  lbcd tx_freq[4];
+} tx_memory[99];
+#seekto 0x0810;
+struct {
+  lbcd rx_freq[4];
+  u8 rxtone;
+  lbcd offset[4];
+  u8 txtone;
+  u8 ishighpower:1,
+     iswide:1,
+     dtcsinvt:1,
+     unknown1:1,
+     dtcsinvr:1,
+     unknown2:1,
+     duplex:2;
+  u8 unknown;
+  lbcd tx_freq[4];
+} rx_memory[99];
+
+#seekto 0x1008;
+struct {
+  u8 unknown[8];
+  u8 name[6];
+  u8 pad[2];
+} names[128];
+"""
+
+UV3R_DUPLEX = ["", "-", "+", ""]
+UV3R_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
+                     chirp_common.PowerLevel("Low", watts=0.50)]
+UV3R_DTCS_POL = ["NN", "NR", "RN", "RR"]
+
+ at directory.register
+class UV3RRadio(chirp_common.CloneModeRadio):
+    """Baofeng UV-3R"""
+    VENDOR = "Baofeng"
+    MODEL = "UV-3R"
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
+        rf.valid_modes = ["FM", "NFM"]
+        rf.valid_power_levels = UV3R_POWER_LEVELS
+        rf.valid_bands = [(136000000, 174000000), (400000000, 470000000)]
+        rf.valid_skips = []
+        rf.valid_duplexes = ["", "-", "+", "split"]
+        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
+                                "->Tone", "->DTCS"]
+        rf.has_ctone = True
+        rf.has_cross = True
+        rf.has_tuning_step = False
+        rf.has_bank = False
+        rf.has_name = False
+        rf.can_odd_split = True
+        rf.memory_bounds = (1, 99)
+        return rf
+
+    def sync_in(self):
+        self._mmap = uv3r_download(self)
+        self.process_mmap()
+
+    def sync_out(self):
+        uv3r_upload(self)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(UV3R_MEM_FORMAT, self._mmap)
+
+    def get_memory(self, number):
+        _mem = self._memobj.rx_memory[number - 1]
+        mem = chirp_common.Memory()
+        mem.number = number
+
+        if _mem.get_raw()[0] == "\xff":
+            mem.empty = True
+            return mem
+
+        mem.freq = int(_mem.rx_freq) * 10
+        mem.offset = int(_mem.offset) * 10
+        mem.duplex = UV3R_DUPLEX[_mem.duplex]
+        if mem.offset > 60000000:
+            if mem.duplex == "+":
+                mem.offset = mem.freq + mem.offset
+            elif mem.duplex == "-":
+                mem.offset = mem.freq - mem.offset
+            mem.duplex = "split"
+        mem.power = UV3R_POWER_LEVELS[1 - _mem.ishighpower]
+        if not _mem.iswide:
+            mem.mode = "NFM"
+
+        dtcspol = (int(_mem.dtcsinvt) << 1) + _mem.dtcsinvr
+        mem.dtcs_polarity = UV3R_DTCS_POL[dtcspol]
+
+        if _mem.txtone in [0, 0xFF]:
+            txmode = ""
+        elif _mem.txtone < 0x33:
+            mem.rtone = chirp_common.TONES[_mem.txtone - 1]
+            txmode = "Tone"
+        elif _mem.txtone >= 0x33:
+            tcode = chirp_common.DTCS_CODES[_mem.txtone - 0x33]
+            mem.dtcs = tcode
+            txmode = "DTCS"
+        else:
+            print "Bug: tx_mode is %02x" % _mem.txtone
+
+        if _mem.rxtone in [0, 0xFF]:
+            rxmode = ""
+        elif _mem.rxtone < 0x33:
+            mem.ctone = chirp_common.TONES[_mem.rxtone - 1]
+            rxmode = "Tone"
+        elif _mem.rxtone >= 0x33:
+            rcode = chirp_common.DTCS_CODES[_mem.rxtone - 0x33]
+            mem.dtcs = rcode
+            rxmode = "DTCS"
+        else:
+            print "Bug: rx_mode is %02x" % _mem.rxtone
+
+        if txmode == "Tone" and not rxmode:
+            mem.tmode = "Tone"
+        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
+            mem.tmode = "TSQL"
+        elif txmode == rxmode and txmode == "DTCS":
+            mem.tmode = "DTCS"
+        elif rxmode or txmode:
+            mem.tmode = "Cross"
+            mem.cross_mode = "%s->%s" % (txmode, rxmode)
+
+        return mem
+
+    def _set_tone(self, _mem, which, value, mode):
+        if mode == "Tone":
+            val = chirp_common.TONES.index(value) + 1
+        elif mode == "DTCS":
+            val = chirp_common.DTCS_CODES.index(value) + 0x33
+        elif mode == "":
+            val = 0
+        else:
+            raise errors.RadioError("Internal error: tmode %s" % mode)
+
+        setattr(_mem, which, val)
+
+    def _set_memory(self, mem, _mem):
+        if mem.empty:
+            _mem.set_raw("\xff" * 16)
+            return
+
+        _mem.rx_freq = mem.freq / 10
+        if mem.duplex == "split":
+            diff = mem.freq - mem.offset
+            _mem.offset = abs(diff) / 10
+            _mem.duplex = UV3R_DUPLEX.index(diff < 0 and "+" or "-")
+            for i in range(0, 4):
+                _mem.tx_freq[i].set_raw("\xFF")
+        else:
+            _mem.offset = mem.offset / 10
+            _mem.duplex = UV3R_DUPLEX.index(mem.duplex)
+            _mem.tx_freq = (mem.freq + mem.offset) / 10
+
+        _mem.ishighpower = mem.power == UV3R_POWER_LEVELS[0]
+        _mem.iswide = mem.mode == "FM"
+
+        _mem.dtcsinvt = mem.dtcs_polarity[0] == "R"
+        _mem.dtcsinvr = mem.dtcs_polarity[1] == "R"
+
+        rxtone = txtone = 0
+        rxmode = txmode = ""
+
+        if mem.tmode == "DTCS":
+            rxmode = txmode = "DTCS"
+            rxtone = txtone = mem.dtcs
+        elif mem.tmode and mem.tmode != "Cross":
+            rxtone = txtone = mem.tmode == "Tone" and mem.rtone or mem.ctone
+            txmode = "Tone"
+            rxmode = mem.tmode == "TSQL" and "Tone" or ""
+        elif mem.tmode == "Cross":
+            txmode, rxmode = mem.cross_mode.split("->", 1)
+
+            if txmode == "DTCS":
+                txtone = mem.dtcs
+            elif txmode == "Tone":
+                txtone = mem.rtone
+
+            if rxmode == "DTCS":
+                rxtone = mem.dtcs
+            elif rxmode == "Tone":
+                rxtone = mem.ctone
+
+        self._set_tone(_mem, "txtone", txtone, txmode)
+        self._set_tone(_mem, "rxtone", rxtone, rxmode)
+
+    def set_memory(self, mem):
+        _tmem = self._memobj.tx_memory[mem.number - 1]
+        _rmem = self._memobj.rx_memory[mem.number - 1]
+
+        self._set_memory(mem, _tmem)
+        self._set_memory(mem, _rmem)
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == 3648
+
+    def get_raw_memory(self, number):
+        _rmem = self._memobj.tx_memory[number - 1]
+        _tmem = self._memobj.rx_memory[number - 1]
+        return repr(_rmem) + repr(_tmem)
diff -r c03ed75f179a -r baf288011d09 chirp/puxing.py
--- /dev/null	gio gen 01 00:00:00 1970 +0000
+++ b/chirp/puxing.py	gio ott 11 17:26:16 2012 +0200
@@ -0,0 +1,506 @@
+# Copyright 2011 Dan Smith <dsmith at danplanet.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import struct
+import time
+import os
+from chirp import util, chirp_common, bitwise, memmap, errors, directory
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+                RadioSettingValueBoolean, RadioSettingValueList
+
+if os.getenv("CHIRP_DEBUG"):
+    DEBUG = True
+else:
+    DEBUG = False
+
+def _puxing_prep(radio):
+    radio.pipe.write("\x02PROGRA")
+    ack = radio.pipe.read(1)
+    if ack != "\x06":
+        raise Exception("Radio did not ACK first command")
+
+    radio.pipe.write("M\x02")
+    ident = radio.pipe.read(8)
+    if len(ident) != 8:
+        print util.hexprint(ident)
+        raise Exception("Radio did not send identification")
+
+    radio.pipe.write("\x06")
+    if radio.pipe.read(1) != "\x06":
+        raise Exception("Radio did not ACK ident")
+
+def puxing_prep(radio):
+    """Do the Puxing PX-777 identification dance"""
+    for _i in range(0, 10):
+        try:
+            return _puxing_prep(radio)
+        except Exception, e:
+            time.sleep(1)
+
+    raise e
+
+def puxing_download(radio):
+    """Talk to a Puxing PX-777 and do a download"""
+    try:
+        puxing_prep(radio)
+        return do_download(radio, 0x0000, 0x0C60, 0x0008)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+def puxing_upload(radio):
+    """Talk to a Puxing PX-777 and do an upload"""
+    try:
+        puxing_prep(radio)
+        return do_upload(radio, 0x0000, 0x0C40, 0x0008)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.00),
+                chirp_common.PowerLevel("Low", watts=1.00)]
+                
+PUXING_CHARSET = list("0123456789") + \
+    [chr(x + ord("A")) for x in range(0, 26)] + \
+    list("-                       ")
+
+PUXING_MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+  lbcd rx_freq[4];
+  lbcd tx_freq[4];
+  lbcd rx_tone[2];
+  lbcd tx_tone[2];
+  u8 _3_unknown_1;
+  u8 _2_unknown_1:2,
+     power_high:1,
+     iswide:1,
+     skip:1,
+     bclo:2,
+     _2_unknown_2:1;
+  u8 _4_unknown1:7,
+     pttid:1;
+  u8 unknown;
+} memory[128];
+
+#seekto 0x080A;
+struct {
+  u8 limits;
+  u8 model;
+} model[1];
+
+#seekto 0x0850;
+struct {
+  u8 name[6];
+  u8 pad[2];
+} names[128];
+"""
+
+# Limits
+#   67- 72: 0xEE
+#  136-174: 0xEF
+#  240-260: 0xF0
+#  350-390: 0xF1
+#  400-430: 0xF2
+#  430-450: 0xF3
+#  450-470: 0xF4
+#  470-490: 0xF5
+#  400-470: 0xF6
+#  460-520: 0xF7
+
+PUXING_MODELS = {
+    328 : 0x38,
+    338 : 0x39,
+    777 : 0x3A,
+}
+
+PUXING_777_BANDS = [
+    ( 67000000,  72000000),
+    (136000000, 174000000),
+    (240000000, 260000000),
+    (350000000, 390000000),
+    (400000000, 430000000),
+    (430000000, 450000000),
+    (450000000, 470000000),
+    (470000000, 490000000),
+    (400000000, 470000000),
+    (460000000, 520000000),
+]
+
+ at directory.register
+class Puxing777Radio(chirp_common.CloneModeRadio):
+    """Puxing PX-777"""
+    VENDOR = "Puxing"
+    MODEL = "PX-777"
+
+    def sync_in(self):
+        self._mmap = puxing_download(self)
+        self.process_mmap()
+
+    def sync_out(self):
+        puxing_upload(self)
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"]
+        rf.valid_modes = ["FM", "NFM"]
+        rf.valid_power_levels = POWER_LEVELS
+        rf.valid_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        rf.valid_name_length = 6
+        rf.has_ctone = False
+        rf.has_tuning_step = False
+        rf.has_bank = False
+        rf.memory_bounds = (1, 128)
+
+        if not hasattr(self, "_memobj") or self._memobj is None:
+            limit_idx = 1
+        else:
+            limit_idx = self._memobj.model.limits - 0xEE
+        try:
+            rf.valid_bands = [PUXING_777_BANDS[limit_idx]]
+        except IndexError:
+            print "Invalid band index %i (0x%02x)" % \
+                (limit_idx, self._memobj.model.limits)
+            rf.valid_bands = [PUXING_777_BANDS[1]]
+
+        return rf
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(PUXING_MEM_FORMAT, self._mmap)
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number - 1]) + "\r\n" + \
+            repr(self._memobj.names[number - 1])
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        if len(filedata) > 0x080B and \
+                ord(filedata[0x080B]) != PUXING_MODELS[777]:
+            return False
+        return len(filedata) == 3168
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number - 1]
+        _nam = self._memobj.names[number - 1]
+
+        def _is_empty():
+            for i in range(0, 4):
+                if _mem.rx_freq[i].get_raw() != "\xFF":
+                    return False
+            return True
+
+        def _is_no_tone(field):
+            return field[0].get_raw() == "\xFF"
+
+        def _get_dtcs(value):
+            # Upper nibble 0x80 -> DCS, 0xC0 -> Inv. DCS
+            if value > 12000:
+                return "R", value - 12000
+            elif value > 8000:
+                return "N", value - 8000
+            else:
+                raise Exception("Unable to convert DCS value")
+
+        def _do_dtcs(mem, txfield, rxfield):
+            if int(txfield) < 8000 or int(rxfield) < 8000:
+                raise Exception("Split tone not supported")
+
+            if txfield[0].get_raw() == "\xFF":
+                tp, tx = "N", None
+            else:
+                tp, tx = _get_dtcs(int(txfield))
+            
+            if rxfield[0].get_raw() == "\xFF":
+                rp, rx = "N", None
+            else:
+                rp, rx = _get_dtcs(int(rxfield))
+
+            if not rx:
+                rx = tx
+            if not tx:
+                tx = rx
+
+            if tx != rx:
+                raise Exception("Different RX and TX DCS codes not supported")
+
+            mem.dtcs = tx
+            mem.dtcs_polarity = "%s%s" % (tp, rp)
+
+        mem = chirp_common.Memory()
+        mem.number = number
+
+        if _is_empty():
+            mem.empty = True
+            return mem
+
+        mem.freq = int(_mem.rx_freq) * 10
+        mem.offset = (int(_mem.tx_freq) * 10) - mem.freq
+        if mem.offset < 0:
+            mem.duplex = "-"
+        elif mem.offset:
+            mem.duplex = "+"
+        mem.offset = abs(mem.offset)
+        if not _mem.skip:
+            mem.skip = "S"
+        if not _mem.iswide:
+            mem.mode = "NFM"
+
+        if _is_no_tone(_mem.tx_tone):
+            pass # No tone
+        elif int(_mem.tx_tone) > 8000 or \
+                (not _is_no_tone(_mem.rx_tone) and int(_mem.rx_tone) > 8000):
+            mem.tmode = "DTCS"
+            _do_dtcs(mem, _mem.tx_tone, _mem.rx_tone)
+        else:
+            mem.rtone = int(_mem.tx_tone) / 10.0
+            mem.tmode = _is_no_tone(_mem.rx_tone) and "Tone" or "TSQL"
+
+        mem.power = POWER_LEVELS[not _mem.power_high]
+
+        for i in _nam.name:
+            if i == 0xFF:
+                break
+            mem.name += PUXING_CHARSET[i]
+
+        return mem
+
+    def set_memory(self, mem):
+        _mem = self._memobj.memory[mem.number - 1]
+        _nam = self._memobj.names[mem.number - 1]
+
+        if mem.empty:
+            wipe_memory(_mem, "\xFF")
+            return
+
+        _mem.rx_freq = mem.freq / 10
+        if mem.duplex == "+":
+            _mem.tx_freq = (mem.freq / 10) + (mem.offset / 10)
+        elif mem.duplex == "-":
+            _mem.tx_freq = (mem.freq / 10) - (mem.offset / 10)
+        else:
+            _mem.tx_freq = (mem.freq / 10)
+        _mem.skip = mem.skip != "S"
+        _mem.iswide = mem.mode != "NFM"
+
+
+        _mem.rx_tone[0].set_raw("\xFF")
+        _mem.rx_tone[1].set_raw("\xFF")
+        _mem.tx_tone[0].set_raw("\xFF")
+        _mem.tx_tone[1].set_raw("\xFF")
+
+
+        if mem.tmode == "DTCS":
+            _mem.tx_tone = int("%x" % int("%i" % (mem.dtcs), 16))
+            _mem.rx_tone = int("%x" % int("%i" % (mem.dtcs), 16))
+
+            # Argh.  Set the high order two bits to signal DCS or Inv. DCS
+            txm = mem.dtcs_polarity[0] == "N" and 0x80 or 0xC0
+            rxm = mem.dtcs_polarity[1] == "N" and 0x80 or 0xC0
+            _mem.tx_tone[1].set_raw(chr(ord(_mem.tx_tone[1].get_raw()) | txm))
+            _mem.rx_tone[1].set_raw(chr(ord(_mem.rx_tone[1].get_raw()) | rxm))
+
+        elif mem.tmode:
+            _mem.tx_tone = int(mem.rtone * 10)
+            if mem.tmode == "TSQL":
+                _mem.rx_tone = int(_mem.tx_tone)
+
+        if mem.power:
+            _mem.power_high = not POWER_LEVELS.index(mem.power)
+        else:
+            _mem.power_high = True
+
+        # Default to disabling the busy channel lockout
+        # 00 == Close
+        # 01 == Carrier
+        # 10 == QT/DQT
+        _mem.bclo = 0
+
+        _nam.name = [0xFF] * 6
+        for i in range(0, len(mem.name)):
+            try:
+                _nam.name[i] = PUXING_CHARSET.index(mem.name[i])
+            except IndexError:
+                raise Exception("Character `%s' not supported")
+
+def puxing_2r_prep(radio):
+    """Do the Puxing 2R identification dance"""
+    radio.pipe.setTimeout(0.2)
+    radio.pipe.write("PROGRAM\x02")
+    ack = radio.pipe.read(1)
+    if ack != "\x06":
+        raise Exception("Radio is not responding")
+
+    radio.pipe.write(ack)
+    ident = radio.pipe.read(16)
+    print "Radio ident: %s (%i)" % (repr(ident), len(ident))
+
+def puxing_2r_download(radio):
+    """Talk to a Puxing 2R and do a download"""
+    try:
+        puxing_2r_prep(radio)
+        return do_download(radio, 0x0000, 0x0FE0, 0x0010)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+def puxing_2r_upload(radio):
+    """Talk to a Puxing 2R and do an upload"""
+    try:
+        puxing_2r_prep(radio)
+        return do_upload(radio, 0x0000, 0x0FE0, 0x0010)
+    except errors.RadioError:
+        raise
+    except Exception, e:
+        raise errors.RadioError("Failed to communicate with radio: %s" % e)
+
+PUXING_2R_MEM_FORMAT = """
+#seekto 0x0010;
+struct {
+  lbcd freq[4];
+  lbcd offset[4];
+  u8 rx_tone;
+  u8 tx_tone;
+  u8 duplex:2,
+     txdtcsinv:1,
+     rxdtcsinv:1,
+     simplex:1,
+     unknown2:1,
+     iswide:1,
+     ishigh:1;
+  u8 name[5];
+} memory[128];
+"""
+
+PX2R_DUPLEX = ["", "+", "-", ""]
+PX2R_POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.0),
+                     chirp_common.PowerLevel("High", watts=2.0)]
+PX2R_CHARSET = "0123456789- ABCDEFGHIJKLMNOPQRSTUVWXYZ +"
+
+ at directory.register
+class Puxing2RRadio(chirp_common.CloneModeRadio):
+    """Puxing PX-2R"""
+    VENDOR = "Puxing"
+    MODEL = "PX-2R"
+    _memsize = 0x0FE0
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"]
+        rf.valid_modes = ["FM", "NFM"]
+        rf.valid_power_levels = PX2R_POWER_LEVELS
+        rf.valid_bands = [(400000000, 500000000)]
+        rf.valid_characters = PX2R_CHARSET
+        rf.valid_name_length = 5
+        rf.valid_duplexes = ["", "+", "-"]
+        rf.valid_skips = []
+        rf.has_ctone = False
+        rf.has_tuning_step = False
+        rf.has_bank = False
+        rf.memory_bounds = (1, 128)
+        rf.can_odd_split = False
+        return rf
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return (len(filedata) == cls._memsize) and \
+            filedata[-16:] != "IcomCloneFormat3"
+
+    def sync_in(self):
+        self._mmap = puxing_2r_download(self)
+        self.process_mmap()
+
+    def sync_out(self):
+        puxing_2r_upload(self)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(PUXING_2R_MEM_FORMAT, self._mmap)
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number-1]
+
+        mem = chirp_common.Memory()
+        mem.number = number
+        if _mem.get_raw()[0:4] == "\xff\xff\xff\xff":
+            mem.empty = True
+            return mem
+
+        mem.freq = int(_mem.freq) * 10
+        mem.offset = int(_mem.offset) * 10
+        mem.mode = _mem.iswide and "FM" or "NFM"
+        mem.duplex = PX2R_DUPLEX[_mem.duplex]
+        mem.power = PX2R_POWER_LEVELS[_mem.ishigh]
+
+        if _mem.tx_tone >= 0x33:
+            mem.dtcs = chirp_common.DTCS_CODES[_mem.tx_tone - 0x33]
+            mem.tmode = "DTCS"
+            mem.dtcs_polarity = \
+                (_mem.txdtcsinv and "R" or "N") + \
+                (_mem.rxdtcsinv and "R" or "N")
+        elif _mem.tx_tone:
+            mem.rtone = chirp_common.TONES[_mem.tx_tone - 1]
+            mem.tmode = _mem.rx_tone and "TSQL" or "Tone"
+
+        count = 0
+        for i in _mem.name:
+            if i == 0xFF:
+                break
+            try:
+                mem.name += PX2R_CHARSET[i]
+            except Exception:
+                print "Unknown name char %i: 0x%02x (mem %i)" % (count,
+                                                                 i, number)
+                mem.name += " "
+            count += 1
+        mem.name = mem.name.rstrip()
+
+        return mem
+
+    def set_memory(self, mem):
+        _mem = self._memobj.memory[mem.number-1]
+
+        if mem.empty:
+            _mem.set_raw("\xff" * 16)
+            return
+
+        _mem.freq = mem.freq / 10
+        _mem.offset = mem.offset / 10
+        _mem.iswide = mem.mode == "FM"
+        _mem.duplex = PX2R_DUPLEX.index(mem.duplex)
+        _mem.ishigh = mem.power == PX2R_POWER_LEVELS[1]
+
+        if mem.tmode == "DTCS":
+            _mem.tx_tone = chirp_common.DTCS_CODES.index(mem.dtcs) + 0x33
+            _mem.rx_tone = chirp_common.DTCS_CODES.index(mem.dtcs) + 0x33
+            _mem.txdtcsinv = mem.dtcs_polarity[0] == "R" 
+            _mem.rxdtcsinv = mem.dtcs_polarity[1] == "R"
+        elif mem.tmode in ["Tone", "TSQL"]:
+            _mem.tx_tone = chirp_common.TONES.index(mem.rtone) + 1
+            _mem.rx_tone = mem.tmode == "TSQL" and int(_mem.tx_tone) or 0
+        else:
+            _mem.tx_tone = 0
+            _mem.rx_tone = 0
+
+        for i in range(0, 5):
+            try:
+                _mem.name[i] = PX2R_CHARSET.index(mem.name[i])
+            except IndexError:
+                _mem.name[i] = 0xFF
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number-1])
+
diff -r c03ed75f179a -r baf288011d09 chirp/th_uv3r.py
--- /dev/null	gio gen 01 00:00:00 1970 +0000
+++ b/chirp/th_uv3r.py	gio ott 11 17:26:16 2012 +0200
@@ -0,0 +1,268 @@
+# Copyright 2011 Dan Smith <dsmith at danplanet.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import struct
+import time
+import os
+from chirp import util, chirp_common, bitwise, memmap, errors, directory
+from chirp.settings import RadioSetting, RadioSettingGroup, \
+                RadioSettingValueBoolean, RadioSettingValueList
+
+if os.getenv("CHIRP_DEBUG"):
+    DEBUG = True
+else:
+    DEBUG = False
+
+def tyt_uv3r_prep(radio):
+    try:
+        radio.pipe.write("PROGRAMa")
+        ack = radio.pipe.read(1)
+        if ack != "\x06":
+            raise errors.RadioError("Radio did not ACK first command")
+    except:
+        raise errors.RadioError("Unable to communicate with the radio")
+
+def tyt_uv3r_download(radio):
+    tyt_uv3r_prep(radio)
+    return do_download(radio, 0x0000, 0x0910, 0x0010)
+
+def tyt_uv3r_upload(radio):
+    tyt_uv3r_prep(radio)
+    return do_upload(radio, 0x0000, 0x0910, 0x0010)
+
+mem_format = """
+struct memory {
+  ul24 duplex:2,
+       bit:1,
+       iswide:1,
+       bits:2,
+       is625:1,
+       freq:17;
+  ul16 offset;
+  ul16 rx_tone;
+  ul16 tx_tone;
+  u8 unknown;
+  u8 name[6];
+};
+
+#seekto 0x0010;
+struct memory memory[128];
+
+#seekto 0x0870;
+u8 emptyflags[16];
+u8 skipflags[16];
+"""
+
+THUV3R_DUPLEX = ["", "+", "-"]
+THUV3R_CHARSET = "".join([chr(ord("0") + x) for x in range(0, 10)] +
+                         [" -*+"] +
+                         [chr(ord("A") + x) for x in range(0, 26)] +
+                         ["_/"])
+ at directory.register
+class TYTUV3RRadio(chirp_common.CloneModeRadio):
+    VENDOR = "TYT"
+    MODEL = "TH-UV3R"
+    BAUD_RATE = 2400
+    _memsize = 2320
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.has_bank = False
+        rf.has_tuning_step = False
+        rf.has_cross = True
+        rf.memory_bounds = (1, 128)
+        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
+        rf.valid_cross_modes = ["Tone->Tone",
+                                "Tone->DTCS", "DTCS->Tone",
+                                "->Tone", "->DTCS"]
+        rf.valid_skips = []
+        rf.valid_modes = ["FM", "NFM"]
+        rf.valid_name_length = 6
+        rf.valid_characters = THUV3R_CHARSET
+        rf.valid_bands = [(136000000, 470000000)]
+        rf.valid_tuning_steps = [5.0, 6.25, 10.0, 12.5, 25.0, 37.50,
+                                 50.0, 100.0]
+        rf.valid_skips = ["", "S"]
+        return rf
+
+    def sync_in(self):
+        self.pipe.setTimeout(2)
+        self._mmap = tyt_uv3r_download(self)
+        self.process_mmap()
+
+    def sync_out(self):
+        tyt_uv3r_upload(self)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(mem_format, self._mmap)
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number - 1])
+
+    def _decode_tone_value(self, value):
+        if value == 0xFFFF:
+            return "", "N", 0
+        elif value & 0x8000:
+            # FIXME: rev pol
+            pol = value & 0x4000 and "R" or "N"
+            return "DTCS", pol, int("%x" % (value & 0x0FFF))
+        else:
+            return "Tone", "N", int("%x" % value) / 10.0
+
+    def _decode_tone(self, mem, _mem):
+        tx_mode, tpol, tx_tone = self._decode_tone_value(_mem.tx_tone)
+        rx_mode, rpol, rx_tone = self._decode_tone_value(_mem.rx_tone)
+
+        if rx_mode == tx_mode == "":
+            return
+
+        mem.dtcs_polarity = "%s%s" % (tpol, rpol)
+
+        if rx_mode == tx_mode == "DTCS":
+            # Break this for now until we can support this in chirp
+            tx_tone = rx_tone
+
+        if rx_mode in ["", tx_mode] and rx_tone in [0, tx_tone]:
+            mem.tmode = rx_mode == "Tone" and "TSQL" or tx_mode
+            if mem.tmode == "DTCS":
+                mem.dtcs = tx_tone
+            elif mem.tmode == "TSQL":
+                mem.ctone = tx_tone
+            else:
+                mem.rtone = tx_tone
+            return
+
+        mem.cross_mode = "%s->%s" % (tx_mode, rx_mode)
+        mem.tmode = "Cross"
+        if tx_mode == "Tone":
+            mem.rtone = tx_tone
+        elif tx_mode == "DTCS":
+            mem.dtcs = tx_tone
+        if rx_mode == "Tone":
+            mem.ctone = rx_tone
+        elif rx_mode == "DTCS":
+            mem.dtcs = rx_tone # No support for different codes yet
+
+    def _encode_tone(self, mem, _mem):
+        if mem.tmode == "":
+            _mem.tx_tone = _mem.rx_tone = 0xFFFF
+            return
+
+        def _tone(val):
+            return int("%i" % (val * 10), 16)
+        def _dcs(val, pol):
+            polmask = pol == "R" and 0xC000 or 0x8000
+            return int("%i" % (val), 16) | polmask
+
+        rx_tone = tx_tone = 0xFFFF
+
+        if mem.tmode == "Tone":
+            rx_mode = ""
+            tx_mode = "Tone"
+            tx_tone = _tone(mem.rtone)
+        elif mem.tmode == "TSQL":
+            rx_mode = tx_mode = "Tone"
+            rx_tone = tx_tone = _tone(mem.ctone)
+        elif mem.tmode == "DTCS":
+            rx_tone = tx_tone = "DTCS"
+            tx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[0])
+            rx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[1])
+        elif mem.tmode == "Cross":
+            tx_mode, rx_mode = mem.cross_mode.split("->", 1)
+            if tx_mode == "DTCS":
+                tx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[0])
+            elif tx_mode == "Tone":
+                tx_tone = _tone(mem.rtone)
+            if rx_mode == "DTCS":
+                rx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[1])
+            elif rx_mode == "Tone":
+                rx_tone = _tone(mem.ctone)
+
+        _mem.rx_tone = rx_tone
+        _mem.tx_tone = tx_tone            
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number - 1]
+        mem = chirp_common.Memory()
+        mem.number = number
+
+        bit = 1 << ((number - 1) % 8)
+        byte = (number - 1) / 8
+
+        if self._memobj.emptyflags[byte] & bit:
+            mem.empty = True
+            return mem
+
+        mult = _mem.is625 and 6250 or 5000
+        mem.freq = _mem.freq * mult
+        mem.offset = _mem.offset * 5000;
+        mem.duplex = THUV3R_DUPLEX[_mem.duplex]
+        mem.mode = _mem.iswide and "FM" or "NFM"
+        self._decode_tone(mem, _mem)
+        mem.skip = (self._memobj.skipflags[byte] & bit) and "S" or ""
+
+        for char in _mem.name:
+            try:
+                c = THUV3R_CHARSET[char]
+            except:
+                c = ""
+            mem.name += c
+        mem.name = mem.name.rstrip()
+
+        return mem
+
+    def set_memory(self, mem):
+        _mem = self._memobj.memory[mem.number - 1]
+        bit = 1 << ((mem.number - 1) % 8)
+        byte = (mem.number - 1) / 8
+
+        if mem.empty:
+            self._memobj.emptyflags[byte] |= bit
+            _mem.set_raw("\xFF" * 16)
+            return
+
+        self._memobj.emptyflags[byte] &= ~bit
+
+        if chirp_common.is_fractional_step(mem.freq):
+            mult = 6250
+            _mem.is625 = True
+        else:
+            mult = 5000
+            _mem.is625 = False
+        _mem.freq = mem.freq / mult
+        _mem.offset = mem.offset / 5000
+        _mem.duplex = THUV3R_DUPLEX.index(mem.duplex)
+        _mem.iswide = mem.mode == "FM"
+        self._encode_tone(mem, _mem)
+
+        if mem.skip:
+            self._memobj.skipflags[byte] |= bit
+        else:
+            self._memobj.skipflags[byte] &= ~bit
+
+        name = []
+        for char in mem.name.ljust(6):
+            try:
+                c = THUV3R_CHARSET.index(char)
+            except:
+                c = THUV3R_CHARSET.index(" ")
+            name.append(c)
+        _mem.name = name
+        print repr(_mem)
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == 2320
+
diff -r c03ed75f179a -r baf288011d09 chirp/wouxun.py
--- a/chirp/wouxun.py	gio ott 11 16:52:14 2012 +0200
+++ b/chirp/wouxun.py	gio ott 11 17:26:16 2012 +0200
@@ -536,987 +536,3 @@
                 filedata[0x1f77:0x1f7d] == "WELCOM":
             return True
         return False
-
-def _puxing_prep(radio):
-    radio.pipe.write("\x02PROGRA")
-    ack = radio.pipe.read(1)
-    if ack != "\x06":
-        raise Exception("Radio did not ACK first command")
-
-    radio.pipe.write("M\x02")
-    ident = radio.pipe.read(8)
-    if len(ident) != 8:
-        print util.hexprint(ident)
-        raise Exception("Radio did not send identification")
-
-    radio.pipe.write("\x06")
-    if radio.pipe.read(1) != "\x06":
-        raise Exception("Radio did not ACK ident")
-
-def puxing_prep(radio):
-    """Do the Puxing PX-777 identification dance"""
-    for _i in range(0, 10):
-        try:
-            return _puxing_prep(radio)
-        except Exception, e:
-            time.sleep(1)
-
-    raise e
-
-def puxing_download(radio):
-    """Talk to a Puxing PX-777 and do a download"""
-    try:
-        puxing_prep(radio)
-        return do_download(radio, 0x0000, 0x0C60, 0x0008)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-def puxing_upload(radio):
-    """Talk to a Puxing PX-777 and do an upload"""
-    try:
-        puxing_prep(radio)
-        return do_upload(radio, 0x0000, 0x0C40, 0x0008)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-PUXING_CHARSET = list("0123456789") + \
-    [chr(x + ord("A")) for x in range(0, 26)] + \
-    list("-                       ")
-
-PUXING_MEM_FORMAT = """
-#seekto 0x0000;
-struct {
-  lbcd rx_freq[4];
-  lbcd tx_freq[4];
-  lbcd rx_tone[2];
-  lbcd tx_tone[2];
-  u8 _3_unknown_1;
-  u8 _2_unknown_1:2,
-     power_high:1,
-     iswide:1,
-     skip:1,
-     bclo:2,
-     _2_unknown_2:1;
-  u8 _4_unknown1:7,
-     pttid:1;
-  u8 unknown;
-} memory[128];
-
-#seekto 0x080A;
-struct {
-  u8 limits;
-  u8 model;
-} model[1];
-
-#seekto 0x0850;
-struct {
-  u8 name[6];
-  u8 pad[2];
-} names[128];
-"""
-
-# Limits
-#   67- 72: 0xEE
-#  136-174: 0xEF
-#  240-260: 0xF0
-#  350-390: 0xF1
-#  400-430: 0xF2
-#  430-450: 0xF3
-#  450-470: 0xF4
-#  470-490: 0xF5
-#  400-470: 0xF6
-#  460-520: 0xF7
-
-PUXING_MODELS = {
-    328 : 0x38,
-    338 : 0x39,
-    777 : 0x3A,
-}
-
-PUXING_777_BANDS = [
-    ( 67000000,  72000000),
-    (136000000, 174000000),
-    (240000000, 260000000),
-    (350000000, 390000000),
-    (400000000, 430000000),
-    (430000000, 450000000),
-    (450000000, 470000000),
-    (470000000, 490000000),
-    (400000000, 470000000),
-    (460000000, 520000000),
-]
-
- at directory.register
-class Puxing777Radio(KGUVD1PRadio):
-    """Puxing PX-777"""
-    VENDOR = "Puxing"
-    MODEL = "PX-777"
-
-    def sync_in(self):
-        self._mmap = puxing_download(self)
-        self.process_mmap()
-
-    def sync_out(self):
-        puxing_upload(self)
-
-    def get_features(self):
-        rf = chirp_common.RadioFeatures()
-        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"]
-        rf.valid_modes = ["FM", "NFM"]
-        rf.valid_power_levels = POWER_LEVELS
-        rf.valid_characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-        rf.valid_name_length = 6
-        rf.has_ctone = False
-        rf.has_tuning_step = False
-        rf.has_bank = False
-        rf.memory_bounds = (1, 128)
-
-        if not hasattr(self, "_memobj") or self._memobj is None:
-            limit_idx = 1
-        else:
-            limit_idx = self._memobj.model.limits - 0xEE
-        try:
-            rf.valid_bands = [PUXING_777_BANDS[limit_idx]]
-        except IndexError:
-            print "Invalid band index %i (0x%02x)" % \
-                (limit_idx, self._memobj.model.limits)
-            rf.valid_bands = [PUXING_777_BANDS[1]]
-
-        return rf
-
-    def process_mmap(self):
-        self._memobj = bitwise.parse(PUXING_MEM_FORMAT, self._mmap)
-
-    def get_raw_memory(self, number):
-        return repr(self._memobj.memory[number - 1]) + "\r\n" + \
-            repr(self._memobj.names[number - 1])
-
-    @classmethod
-    def match_model(cls, filedata, filename):
-        if len(filedata) > 0x080B and \
-                ord(filedata[0x080B]) != PUXING_MODELS[777]:
-            return False
-        return len(filedata) == 3168
-
-    def get_memory(self, number):
-        _mem = self._memobj.memory[number - 1]
-        _nam = self._memobj.names[number - 1]
-
-        def _is_empty():
-            for i in range(0, 4):
-                if _mem.rx_freq[i].get_raw() != "\xFF":
-                    return False
-            return True
-
-        def _is_no_tone(field):
-            return field[0].get_raw() == "\xFF"
-
-        def _get_dtcs(value):
-            # Upper nibble 0x80 -> DCS, 0xC0 -> Inv. DCS
-            if value > 12000:
-                return "R", value - 12000
-            elif value > 8000:
-                return "N", value - 8000
-            else:
-                raise Exception("Unable to convert DCS value")
-
-        def _do_dtcs(mem, txfield, rxfield):
-            if int(txfield) < 8000 or int(rxfield) < 8000:
-                raise Exception("Split tone not supported")
-
-            if txfield[0].get_raw() == "\xFF":
-                tp, tx = "N", None
-            else:
-                tp, tx = _get_dtcs(int(txfield))
-            
-            if rxfield[0].get_raw() == "\xFF":
-                rp, rx = "N", None
-            else:
-                rp, rx = _get_dtcs(int(rxfield))
-
-            if not rx:
-                rx = tx
-            if not tx:
-                tx = rx
-
-            if tx != rx:
-                raise Exception("Different RX and TX DCS codes not supported")
-
-            mem.dtcs = tx
-            mem.dtcs_polarity = "%s%s" % (tp, rp)
-
-        mem = chirp_common.Memory()
-        mem.number = number
-
-        if _is_empty():
-            mem.empty = True
-            return mem
-
-        mem.freq = int(_mem.rx_freq) * 10
-        mem.offset = (int(_mem.tx_freq) * 10) - mem.freq
-        if mem.offset < 0:
-            mem.duplex = "-"
-        elif mem.offset:
-            mem.duplex = "+"
-        mem.offset = abs(mem.offset)
-        if not _mem.skip:
-            mem.skip = "S"
-        if not _mem.iswide:
-            mem.mode = "NFM"
-
-        if _is_no_tone(_mem.tx_tone):
-            pass # No tone
-        elif int(_mem.tx_tone) > 8000 or \
-                (not _is_no_tone(_mem.rx_tone) and int(_mem.rx_tone) > 8000):
-            mem.tmode = "DTCS"
-            _do_dtcs(mem, _mem.tx_tone, _mem.rx_tone)
-        else:
-            mem.rtone = int(_mem.tx_tone) / 10.0
-            mem.tmode = _is_no_tone(_mem.rx_tone) and "Tone" or "TSQL"
-
-        mem.power = POWER_LEVELS[not _mem.power_high]
-
-        for i in _nam.name:
-            if i == 0xFF:
-                break
-            mem.name += PUXING_CHARSET[i]
-
-        return mem
-
-    def set_memory(self, mem):
-        _mem = self._memobj.memory[mem.number - 1]
-        _nam = self._memobj.names[mem.number - 1]
-
-        if mem.empty:
-            wipe_memory(_mem, "\xFF")
-            return
-
-        _mem.rx_freq = mem.freq / 10
-        if mem.duplex == "+":
-            _mem.tx_freq = (mem.freq / 10) + (mem.offset / 10)
-        elif mem.duplex == "-":
-            _mem.tx_freq = (mem.freq / 10) - (mem.offset / 10)
-        else:
-            _mem.tx_freq = (mem.freq / 10)
-        _mem.skip = mem.skip != "S"
-        _mem.iswide = mem.mode != "NFM"
-
-
-        _mem.rx_tone[0].set_raw("\xFF")
-        _mem.rx_tone[1].set_raw("\xFF")
-        _mem.tx_tone[0].set_raw("\xFF")
-        _mem.tx_tone[1].set_raw("\xFF")
-
-
-        if mem.tmode == "DTCS":
-            _mem.tx_tone = int("%x" % int("%i" % (mem.dtcs), 16))
-            _mem.rx_tone = int("%x" % int("%i" % (mem.dtcs), 16))
-
-            # Argh.  Set the high order two bits to signal DCS or Inv. DCS
-            txm = mem.dtcs_polarity[0] == "N" and 0x80 or 0xC0
-            rxm = mem.dtcs_polarity[1] == "N" and 0x80 or 0xC0
-            _mem.tx_tone[1].set_raw(chr(ord(_mem.tx_tone[1].get_raw()) | txm))
-            _mem.rx_tone[1].set_raw(chr(ord(_mem.rx_tone[1].get_raw()) | rxm))
-
-        elif mem.tmode:
-            _mem.tx_tone = int(mem.rtone * 10)
-            if mem.tmode == "TSQL":
-                _mem.rx_tone = int(_mem.tx_tone)
-
-        if mem.power:
-            _mem.power_high = not POWER_LEVELS.index(mem.power)
-        else:
-            _mem.power_high = True
-
-        # Default to disabling the busy channel lockout
-        # 00 == Close
-        # 01 == Carrier
-        # 10 == QT/DQT
-        _mem.bclo = 0
-
-        _nam.name = [0xFF] * 6
-        for i in range(0, len(mem.name)):
-            try:
-                _nam.name[i] = PUXING_CHARSET.index(mem.name[i])
-            except IndexError:
-                raise Exception("Character `%s' not supported")
-
-def puxing_2r_prep(radio):
-    """Do the Puxing 2R identification dance"""
-    radio.pipe.setTimeout(0.2)
-    radio.pipe.write("PROGRAM\x02")
-    ack = radio.pipe.read(1)
-    if ack != "\x06":
-        raise Exception("Radio is not responding")
-
-    radio.pipe.write(ack)
-    ident = radio.pipe.read(16)
-    print "Radio ident: %s (%i)" % (repr(ident), len(ident))
-
-def puxing_2r_download(radio):
-    """Talk to a Puxing 2R and do a download"""
-    try:
-        puxing_2r_prep(radio)
-        return do_download(radio, 0x0000, 0x0FE0, 0x0010)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-def puxing_2r_upload(radio):
-    """Talk to a Puxing 2R and do an upload"""
-    try:
-        puxing_2r_prep(radio)
-        return do_upload(radio, 0x0000, 0x0FE0, 0x0010)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-PUXING_2R_MEM_FORMAT = """
-#seekto 0x0010;
-struct {
-  lbcd freq[4];
-  lbcd offset[4];
-  u8 rx_tone;
-  u8 tx_tone;
-  u8 duplex:2,
-     txdtcsinv:1,
-     rxdtcsinv:1,
-     simplex:1,
-     unknown2:1,
-     iswide:1,
-     ishigh:1;
-  u8 name[5];
-} memory[128];
-"""
-
-PX2R_DUPLEX = ["", "+", "-", ""]
-PX2R_POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.0),
-                     chirp_common.PowerLevel("High", watts=2.0)]
-PX2R_CHARSET = "0123456789- ABCDEFGHIJKLMNOPQRSTUVWXYZ +"
-
- at directory.register
-class Puxing2RRadio(KGUVD1PRadio):
-    """Puxing PX-2R"""
-    VENDOR = "Puxing"
-    MODEL = "PX-2R"
-    _memsize = 0x0FE0
-
-    def get_features(self):
-        rf = chirp_common.RadioFeatures()
-        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"]
-        rf.valid_modes = ["FM", "NFM"]
-        rf.valid_power_levels = PX2R_POWER_LEVELS
-        rf.valid_bands = [(400000000, 500000000)]
-        rf.valid_characters = PX2R_CHARSET
-        rf.valid_name_length = 5
-        rf.valid_duplexes = ["", "+", "-"]
-        rf.valid_skips = []
-        rf.has_ctone = False
-        rf.has_tuning_step = False
-        rf.has_bank = False
-        rf.memory_bounds = (1, 128)
-        rf.can_odd_split = False
-        return rf
-
-    @classmethod
-    def match_model(cls, filedata, filename):
-        return (len(filedata) == cls._memsize) and \
-            filedata[-16:] != "IcomCloneFormat3"
-
-    def sync_in(self):
-        self._mmap = puxing_2r_download(self)
-        self.process_mmap()
-
-    def sync_out(self):
-        puxing_2r_upload(self)
-
-    def process_mmap(self):
-        self._memobj = bitwise.parse(PUXING_2R_MEM_FORMAT, self._mmap)
-
-    def get_memory(self, number):
-        _mem = self._memobj.memory[number-1]
-
-        mem = chirp_common.Memory()
-        mem.number = number
-        if _mem.get_raw()[0:4] == "\xff\xff\xff\xff":
-            mem.empty = True
-            return mem
-
-        mem.freq = int(_mem.freq) * 10
-        mem.offset = int(_mem.offset) * 10
-        mem.mode = _mem.iswide and "FM" or "NFM"
-        mem.duplex = PX2R_DUPLEX[_mem.duplex]
-        mem.power = PX2R_POWER_LEVELS[_mem.ishigh]
-
-        if _mem.tx_tone >= 0x33:
-            mem.dtcs = chirp_common.DTCS_CODES[_mem.tx_tone - 0x33]
-            mem.tmode = "DTCS"
-            mem.dtcs_polarity = \
-                (_mem.txdtcsinv and "R" or "N") + \
-                (_mem.rxdtcsinv and "R" or "N")
-        elif _mem.tx_tone:
-            mem.rtone = chirp_common.TONES[_mem.tx_tone - 1]
-            mem.tmode = _mem.rx_tone and "TSQL" or "Tone"
-
-        count = 0
-        for i in _mem.name:
-            if i == 0xFF:
-                break
-            try:
-                mem.name += PX2R_CHARSET[i]
-            except Exception:
-                print "Unknown name char %i: 0x%02x (mem %i)" % (count,
-                                                                 i, number)
-                mem.name += " "
-            count += 1
-        mem.name = mem.name.rstrip()
-
-        return mem
-
-    def set_memory(self, mem):
-        _mem = self._memobj.memory[mem.number-1]
-
-        if mem.empty:
-            _mem.set_raw("\xff" * 16)
-            return
-
-        _mem.freq = mem.freq / 10
-        _mem.offset = mem.offset / 10
-        _mem.iswide = mem.mode == "FM"
-        _mem.duplex = PX2R_DUPLEX.index(mem.duplex)
-        _mem.ishigh = mem.power == PX2R_POWER_LEVELS[1]
-
-        if mem.tmode == "DTCS":
-            _mem.tx_tone = chirp_common.DTCS_CODES.index(mem.dtcs) + 0x33
-            _mem.rx_tone = chirp_common.DTCS_CODES.index(mem.dtcs) + 0x33
-            _mem.txdtcsinv = mem.dtcs_polarity[0] == "R" 
-            _mem.rxdtcsinv = mem.dtcs_polarity[1] == "R"
-        elif mem.tmode in ["Tone", "TSQL"]:
-            _mem.tx_tone = chirp_common.TONES.index(mem.rtone) + 1
-            _mem.rx_tone = mem.tmode == "TSQL" and int(_mem.tx_tone) or 0
-        else:
-            _mem.tx_tone = 0
-            _mem.rx_tone = 0
-
-        for i in range(0, 5):
-            try:
-                _mem.name[i] = PX2R_CHARSET.index(mem.name[i])
-            except IndexError:
-                _mem.name[i] = 0xFF
-
-    def get_raw_memory(self, number):
-        return repr(self._memobj.memory[number-1])
-
-def _uv3r_prep(radio):
-    radio.pipe.write("\x05PROGRAM")
-    ack = radio.pipe.read(1)
-    if ack != "\x06":
-        raise errors.RadioError("Radio did not ACK first command")
-
-    radio.pipe.write("\x02")
-    ident = radio.pipe.read(8)
-    if len(ident) != 8:
-        print util.hexprint(ident)
-        raise errors.RadioError("Radio did not send identification")
-
-    radio.pipe.write("\x06")
-    if radio.pipe.read(1) != "\x06":
-        raise errors.RadioError("Radio did not ACK ident")
-
-def uv3r_prep(radio):
-    """Do the UV3R identification dance"""
-    for _i in range(0, 10):
-        try:
-            return _uv3r_prep(radio)
-        except errors.RadioError, e:
-            time.sleep(1)
-
-    raise e
-
-def uv3r_download(radio):
-    """Talk to a UV3R and do a download"""
-    try:
-        uv3r_prep(radio)
-        return do_download(radio, 0x0000, 0x0E40, 0x0010)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-def uv3r_upload(radio):
-    """Talk to a UV3R and do an upload"""
-    try:
-        uv3r_prep(radio)
-        return do_upload(radio, 0x0000, 0x0E40, 0x0010)
-    except errors.RadioError:
-        raise
-    except Exception, e:
-        raise errors.RadioError("Failed to communicate with radio: %s" % e)
-
-UV3R_MEM_FORMAT = """
-#seekto 0x0010;
-struct {
-  lbcd rx_freq[4];
-  u8 rxtone;
-  lbcd offset[4];
-  u8 txtone;
-  u8 ishighpower:1,
-     iswide:1,
-     dtcsinvt:1,
-     unknown1:1,
-     dtcsinvr:1,
-     unknown2:1,
-     duplex:2;
-  u8 unknown;
-  lbcd tx_freq[4];
-} tx_memory[99];
-#seekto 0x0810;
-struct {
-  lbcd rx_freq[4];
-  u8 rxtone;
-  lbcd offset[4];
-  u8 txtone;
-  u8 ishighpower:1,
-     iswide:1,
-     dtcsinvt:1,
-     unknown1:1,
-     dtcsinvr:1,
-     unknown2:1,
-     duplex:2;
-  u8 unknown;
-  lbcd tx_freq[4];
-} rx_memory[99];
-
-#seekto 0x1008;
-struct {
-  u8 unknown[8];
-  u8 name[6];
-  u8 pad[2];
-} names[128];
-"""
-
-UV3R_DUPLEX = ["", "-", "+", ""]
-UV3R_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
-                     chirp_common.PowerLevel("Low", watts=0.50)]
-UV3R_DTCS_POL = ["NN", "NR", "RN", "RR"]
-
- at directory.register
-class UV3RRadio(KGUVD1PRadio):
-    """Baofeng UV-3R"""
-    VENDOR = "Baofeng"
-    MODEL = "UV-3R"
-
-    def get_features(self):
-        rf = chirp_common.RadioFeatures()
-        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
-        rf.valid_modes = ["FM", "NFM"]
-        rf.valid_power_levels = UV3R_POWER_LEVELS
-        rf.valid_bands = [(136000000, 174000000), (400000000, 470000000)]
-        rf.valid_skips = []
-        rf.valid_duplexes = ["", "-", "+", "split"]
-        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
-                                "->Tone", "->DTCS"]
-        rf.has_ctone = True
-        rf.has_cross = True
-        rf.has_tuning_step = False
-        rf.has_bank = False
-        rf.has_name = False
-        rf.can_odd_split = True
-        rf.memory_bounds = (1, 99)
-        return rf
-
-    def sync_in(self):
-        self._mmap = uv3r_download(self)
-        self.process_mmap()
-
-    def sync_out(self):
-        uv3r_upload(self)
-
-    def process_mmap(self):
-        self._memobj = bitwise.parse(UV3R_MEM_FORMAT, self._mmap)
-
-    def get_memory(self, number):
-        _mem = self._memobj.rx_memory[number - 1]
-        mem = chirp_common.Memory()
-        mem.number = number
-
-        if _mem.get_raw()[0] == "\xff":
-            mem.empty = True
-            return mem
-
-        mem.freq = int(_mem.rx_freq) * 10
-        mem.offset = int(_mem.offset) * 10
-        mem.duplex = UV3R_DUPLEX[_mem.duplex]
-        if mem.offset > 60000000:
-            if mem.duplex == "+":
-                mem.offset = mem.freq + mem.offset
-            elif mem.duplex == "-":
-                mem.offset = mem.freq - mem.offset
-            mem.duplex = "split"
-        mem.power = UV3R_POWER_LEVELS[1 - _mem.ishighpower]
-        if not _mem.iswide:
-            mem.mode = "NFM"
-
-        dtcspol = (int(_mem.dtcsinvt) << 1) + _mem.dtcsinvr
-        mem.dtcs_polarity = UV3R_DTCS_POL[dtcspol]
-
-        if _mem.txtone in [0, 0xFF]:
-            txmode = ""
-        elif _mem.txtone < 0x33:
-            mem.rtone = chirp_common.TONES[_mem.txtone - 1]
-            txmode = "Tone"
-        elif _mem.txtone >= 0x33:
-            tcode = chirp_common.DTCS_CODES[_mem.txtone - 0x33]
-            mem.dtcs = tcode
-            txmode = "DTCS"
-        else:
-            print "Bug: tx_mode is %02x" % _mem.txtone
-
-        if _mem.rxtone in [0, 0xFF]:
-            rxmode = ""
-        elif _mem.rxtone < 0x33:
-            mem.ctone = chirp_common.TONES[_mem.rxtone - 1]
-            rxmode = "Tone"
-        elif _mem.rxtone >= 0x33:
-            rcode = chirp_common.DTCS_CODES[_mem.rxtone - 0x33]
-            mem.dtcs = rcode
-            rxmode = "DTCS"
-        else:
-            print "Bug: rx_mode is %02x" % _mem.rxtone
-
-        if txmode == "Tone" and not rxmode:
-            mem.tmode = "Tone"
-        elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
-            mem.tmode = "TSQL"
-        elif txmode == rxmode and txmode == "DTCS":
-            mem.tmode = "DTCS"
-        elif rxmode or txmode:
-            mem.tmode = "Cross"
-            mem.cross_mode = "%s->%s" % (txmode, rxmode)
-
-        return mem
-
-    def _set_tone(self, _mem, which, value, mode):
-        if mode == "Tone":
-            val = chirp_common.TONES.index(value) + 1
-        elif mode == "DTCS":
-            val = chirp_common.DTCS_CODES.index(value) + 0x33
-        elif mode == "":
-            val = 0
-        else:
-            raise errors.RadioError("Internal error: tmode %s" % mode)
-
-        setattr(_mem, which, val)
-
-    def _set_memory(self, mem, _mem):
-        if mem.empty:
-            _mem.set_raw("\xff" * 16)
-            return
-
-        _mem.rx_freq = mem.freq / 10
-        if mem.duplex == "split":
-            diff = mem.freq - mem.offset
-            _mem.offset = abs(diff) / 10
-            _mem.duplex = UV3R_DUPLEX.index(diff < 0 and "+" or "-")
-            for i in range(0, 4):
-                _mem.tx_freq[i].set_raw("\xFF")
-        else:
-            _mem.offset = mem.offset / 10
-            _mem.duplex = UV3R_DUPLEX.index(mem.duplex)
-            _mem.tx_freq = (mem.freq + mem.offset) / 10
-
-        _mem.ishighpower = mem.power == UV3R_POWER_LEVELS[0]
-        _mem.iswide = mem.mode == "FM"
-
-        _mem.dtcsinvt = mem.dtcs_polarity[0] == "R"
-        _mem.dtcsinvr = mem.dtcs_polarity[1] == "R"
-
-        rxtone = txtone = 0
-        rxmode = txmode = ""
-
-        if mem.tmode == "DTCS":
-            rxmode = txmode = "DTCS"
-            rxtone = txtone = mem.dtcs
-        elif mem.tmode and mem.tmode != "Cross":
-            rxtone = txtone = mem.tmode == "Tone" and mem.rtone or mem.ctone
-            txmode = "Tone"
-            rxmode = mem.tmode == "TSQL" and "Tone" or ""
-        elif mem.tmode == "Cross":
-            txmode, rxmode = mem.cross_mode.split("->", 1)
-
-            if txmode == "DTCS":
-                txtone = mem.dtcs
-            elif txmode == "Tone":
-                txtone = mem.rtone
-
-            if rxmode == "DTCS":
-                rxtone = mem.dtcs
-            elif rxmode == "Tone":
-                rxtone = mem.ctone
-
-        self._set_tone(_mem, "txtone", txtone, txmode)
-        self._set_tone(_mem, "rxtone", rxtone, rxmode)
-
-    def set_memory(self, mem):
-        _tmem = self._memobj.tx_memory[mem.number - 1]
-        _rmem = self._memobj.rx_memory[mem.number - 1]
-
-        self._set_memory(mem, _tmem)
-        self._set_memory(mem, _rmem)
-
-    @classmethod
-    def match_model(cls, filedata, filename):
-        return len(filedata) == 3648
-
-    def get_raw_memory(self, number):
-        _rmem = self._memobj.tx_memory[number - 1]
-        _tmem = self._memobj.rx_memory[number - 1]
-        return repr(_rmem) + repr(_tmem)
-
-def tyt_uv3r_prep(radio):
-    try:
-        radio.pipe.write("PROGRAMa")
-        ack = radio.pipe.read(1)
-        if ack != "\x06":
-            raise errors.RadioError("Radio did not ACK first command")
-    except:
-        raise errors.RadioError("Unable to communicate with the radio")
-
-def tyt_uv3r_download(radio):
-    tyt_uv3r_prep(radio)
-    return do_download(radio, 0x0000, 0x0910, 0x0010)
-
-def tyt_uv3r_upload(radio):
-    tyt_uv3r_prep(radio)
-    return do_upload(radio, 0x0000, 0x0910, 0x0010)
-
-mem_format = """
-struct memory {
-  ul24 duplex:2,
-       bit:1,
-       iswide:1,
-       bits:2,
-       is625:1,
-       freq:17;
-  ul16 offset;
-  ul16 rx_tone;
-  ul16 tx_tone;
-  u8 unknown;
-  u8 name[6];
-};
-
-#seekto 0x0010;
-struct memory memory[128];
-
-#seekto 0x0870;
-u8 emptyflags[16];
-u8 skipflags[16];
-"""
-
-THUV3R_DUPLEX = ["", "+", "-"]
-THUV3R_CHARSET = "".join([chr(ord("0") + x) for x in range(0, 10)] +
-                         [" -*+"] +
-                         [chr(ord("A") + x) for x in range(0, 26)] +
-                         ["_/"])
- at directory.register
-class TYTUV3RRadio(KGUVD1PRadio):
-    VENDOR = "TYT"
-    MODEL = "TH-UV3R"
-    BAUD_RATE = 2400
-    _memsize = 2320
-
-    def get_features(self):
-        rf = chirp_common.RadioFeatures()
-        rf.has_bank = False
-        rf.has_tuning_step = False
-        rf.has_cross = True
-        rf.memory_bounds = (1, 128)
-        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
-        rf.valid_cross_modes = ["Tone->Tone",
-                                "Tone->DTCS", "DTCS->Tone",
-                                "->Tone", "->DTCS"]
-        rf.valid_skips = []
-        rf.valid_modes = ["FM", "NFM"]
-        rf.valid_name_length = 6
-        rf.valid_characters = THUV3R_CHARSET
-        rf.valid_bands = [(136000000, 470000000)]
-        rf.valid_tuning_steps = [5.0, 6.25, 10.0, 12.5, 25.0, 37.50,
-                                 50.0, 100.0]
-        rf.valid_skips = ["", "S"]
-        return rf
-
-    def sync_in(self):
-        self.pipe.setTimeout(2)
-        self._mmap = tyt_uv3r_download(self)
-        self.process_mmap()
-
-    def sync_out(self):
-        tyt_uv3r_upload(self)
-
-    def process_mmap(self):
-        self._memobj = bitwise.parse(mem_format, self._mmap)
-
-    def get_raw_memory(self, number):
-        return repr(self._memobj.memory[number - 1])
-
-    def _decode_tone_value(self, value):
-        if value == 0xFFFF:
-            return "", "N", 0
-        elif value & 0x8000:
-            # FIXME: rev pol
-            pol = value & 0x4000 and "R" or "N"
-            return "DTCS", pol, int("%x" % (value & 0x0FFF))
-        else:
-            return "Tone", "N", int("%x" % value) / 10.0
-
-    def _decode_tone(self, mem, _mem):
-        tx_mode, tpol, tx_tone = self._decode_tone_value(_mem.tx_tone)
-        rx_mode, rpol, rx_tone = self._decode_tone_value(_mem.rx_tone)
-
-        if rx_mode == tx_mode == "":
-            return
-
-        mem.dtcs_polarity = "%s%s" % (tpol, rpol)
-
-        if rx_mode == tx_mode == "DTCS":
-            # Break this for now until we can support this in chirp
-            tx_tone = rx_tone
-
-        if rx_mode in ["", tx_mode] and rx_tone in [0, tx_tone]:
-            mem.tmode = rx_mode == "Tone" and "TSQL" or tx_mode
-            if mem.tmode == "DTCS":
-                mem.dtcs = tx_tone
-            elif mem.tmode == "TSQL":
-                mem.ctone = tx_tone
-            else:
-                mem.rtone = tx_tone
-            return
-
-        mem.cross_mode = "%s->%s" % (tx_mode, rx_mode)
-        mem.tmode = "Cross"
-        if tx_mode == "Tone":
-            mem.rtone = tx_tone
-        elif tx_mode == "DTCS":
-            mem.dtcs = tx_tone
-        if rx_mode == "Tone":
-            mem.ctone = rx_tone
-        elif rx_mode == "DTCS":
-            mem.dtcs = rx_tone # No support for different codes yet
-
-    def _encode_tone(self, mem, _mem):
-        if mem.tmode == "":
-            _mem.tx_tone = _mem.rx_tone = 0xFFFF
-            return
-
-        def _tone(val):
-            return int("%i" % (val * 10), 16)
-        def _dcs(val, pol):
-            polmask = pol == "R" and 0xC000 or 0x8000
-            return int("%i" % (val), 16) | polmask
-
-        rx_tone = tx_tone = 0xFFFF
-
-        if mem.tmode == "Tone":
-            rx_mode = ""
-            tx_mode = "Tone"
-            tx_tone = _tone(mem.rtone)
-        elif mem.tmode == "TSQL":
-            rx_mode = tx_mode = "Tone"
-            rx_tone = tx_tone = _tone(mem.ctone)
-        elif mem.tmode == "DTCS":
-            rx_tone = tx_tone = "DTCS"
-            tx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[0])
-            rx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[1])
-        elif mem.tmode == "Cross":
-            tx_mode, rx_mode = mem.cross_mode.split("->", 1)
-            if tx_mode == "DTCS":
-                tx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[0])
-            elif tx_mode == "Tone":
-                tx_tone = _tone(mem.rtone)
-            if rx_mode == "DTCS":
-                rx_tone = _dcs(mem.dtcs, mem.dtcs_polarity[1])
-            elif rx_mode == "Tone":
-                rx_tone = _tone(mem.ctone)
-
-        _mem.rx_tone = rx_tone
-        _mem.tx_tone = tx_tone            
-
-    def get_memory(self, number):
-        _mem = self._memobj.memory[number - 1]
-        mem = chirp_common.Memory()
-        mem.number = number
-
-        bit = 1 << ((number - 1) % 8)
-        byte = (number - 1) / 8
-
-        if self._memobj.emptyflags[byte] & bit:
-            mem.empty = True
-            return mem
-
-        mult = _mem.is625 and 6250 or 5000
-        mem.freq = _mem.freq * mult
-        mem.offset = _mem.offset * 5000;
-        mem.duplex = THUV3R_DUPLEX[_mem.duplex]
-        mem.mode = _mem.iswide and "FM" or "NFM"
-        self._decode_tone(mem, _mem)
-        mem.skip = (self._memobj.skipflags[byte] & bit) and "S" or ""
-
-        for char in _mem.name:
-            try:
-                c = THUV3R_CHARSET[char]
-            except:
-                c = ""
-            mem.name += c
-        mem.name = mem.name.rstrip()
-
-        return mem
-
-    def set_memory(self, mem):
-        _mem = self._memobj.memory[mem.number - 1]
-        bit = 1 << ((mem.number - 1) % 8)
-        byte = (mem.number - 1) / 8
-
-        if mem.empty:
-            self._memobj.emptyflags[byte] |= bit
-            _mem.set_raw("\xFF" * 16)
-            return
-
-        self._memobj.emptyflags[byte] &= ~bit
-
-        if chirp_common.is_fractional_step(mem.freq):
-            mult = 6250
-            _mem.is625 = True
-        else:
-            mult = 5000
-            _mem.is625 = False
-        _mem.freq = mem.freq / mult
-        _mem.offset = mem.offset / 5000
-        _mem.duplex = THUV3R_DUPLEX.index(mem.duplex)
-        _mem.iswide = mem.mode == "FM"
-        self._encode_tone(mem, _mem)
-
-        if mem.skip:
-            self._memobj.skipflags[byte] |= bit
-        else:
-            self._memobj.skipflags[byte] &= ~bit
-
-        name = []
-        for char in mem.name.ljust(6):
-            try:
-                c = THUV3R_CHARSET.index(char)
-            except:
-                c = THUV3R_CHARSET.index(" ")
-            name.append(c)
-        _mem.name = name
-        print repr(_mem)
-
-    @classmethod
-    def match_model(cls, filedata, filename):
-        return len(filedata) == 2320
-



More information about the chirp_devel mailing list