[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