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