Developers
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
April 2015
- 14 participants
- 26 discussions
[chirp_devel] [PATCH] [RFC] Abstract Bank and BankModel to MemoryMapping and MappingModel
by Dan Smith 24 Aug '16
by Dan Smith 24 Aug '16
24 Aug '16
# HG changeset patch
# User Dan Smith <dsmith(a)danplanet.com>
# Date 1364678018 25200
# Node ID bf1c92a33bfd4c730dcc49095ff9a737b30c33c0
# Parent ede5a4ccfd6efbaed4883c86d93be92509fde8da
[RFC] Abstract Bank and BankModel to MemoryMapping and MappingModel
This is mostly just a search-and-replace for the above names, but makes
way for supporting things like scan lists that behave exactly the same
way.
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/chirp_common.py
--- a/chirp/chirp_common.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/chirp_common.py Sat Mar 30 14:13:38 2013 -0700
@@ -543,8 +543,8 @@
except Exception:
self.dv_code = 0
-class Bank:
- """Base class for a radio's Bank"""
+class MemoryMapping(object):
+ """Base class for a memory mapping"""
def __init__(self, model, index, name):
self._model = model
self._index = index
@@ -554,76 +554,84 @@
return self.get_name()
def __repr__(self):
- return "Bank-%s" % self._index
+ return "%s-%s" % (self.__class__.__name__, self._index)
def get_name(self):
- """Returns the static or user-adjustable bank name"""
+ """Returns the mapping name"""
return self._name
def get_index(self):
- """Returns the immutable bank index (string or int)"""
+ """Returns the immutable index (string or int)"""
return self._index
def __eq__(self, other):
return self.get_index() == other.get_index()
+class MappingModel(object):
+ """Base class for a memory mapping model"""
+
+ def __init__(self, radio):
+ self._radio = radio
+
+ def get_num_mappings(self):
+ """Returns the number of mappings in the model (should be
+ callable without consulting the radio"""
+ raise NotImplementedError()
+
+ def get_mappings(self):
+ """Return a list of mappings"""
+ raise NotImplementedError()
+
+ def add_memory_to_mapping(self, memory, mapping):
+ """Add @memory to @mapping."""
+ raise NotImplementedError()
+
+ def remove_memory_from_mapping(self, memory, mapping):
+ """Remove @memory from @mapping.
+ Shall raise exception if @memory is not in @bank"""
+ raise NotImplementedError()
+
+ def get_mapping_memories(self, mapping):
+ """Return a list of memories in @mapping"""
+ raise NotImplementedError()
+
+ def get_memory_mappings(self, memory):
+ """Return a list of mappings that @memory is in"""
+ raise NotImplementedError()
+
+class Bank(MemoryMapping):
+ """Base class for a radio's Bank"""
+ pass
+
class NamedBank(Bank):
"""A bank that can have a name"""
def set_name(self, name):
"""Changes the user-adjustable bank name"""
self._name = name
-class BankModel:
+class BankModel(MappingModel):
"""A bank model where one memory is in zero or one banks at any point"""
- def __init__(self, radio):
- self._radio = radio
+ pass
- def get_num_banks(self):
- """Returns the number of banks (should be callable without
- consulting the radio"""
+class MappingModelIndexInterface:
+ """Interface for mappings with index capabilities"""
+ def get_index_bounds(self):
+ """Returns a tuple (lo,hi) of the min and max mapping indices"""
raise Exception("Not implemented")
- def get_banks(self):
- """Return a list of banks"""
+ def get_memory_index(self, memory, mapping):
+ """Returns the index of @memory in @mapping"""
raise Exception("Not implemented")
- def add_memory_to_bank(self, memory, bank):
- """Add @memory to @bank."""
+ def set_memory_index(self, memory, mapping, index):
+ """Sets the index of @memory in @mapping to @index"""
raise Exception("Not implemented")
- def remove_memory_from_bank(self, memory, bank):
- """Remove @memory from @bank.
- Shall raise exception if @memory is not in @bank."""
- raise Exception("Not implemented")
-
- def get_bank_memories(self, bank):
- """Return a list of memories in @bank"""
- raise Exception("Not implemented")
-
- def get_memory_banks(self, memory):
- """Returns a list of the banks that @memory is in"""
- raise Exception("Not implemented")
-
-class BankIndexInterface:
- """Interface for banks with index capabilities"""
- def get_index_bounds(self):
- """Returns a tuple (lo,hi) of the minimum and maximum bank indices"""
- raise Exception("Not implemented")
-
- def get_memory_index(self, memory, bank):
- """Returns the index of @memory in @bank"""
- raise Exception("Not implemented")
-
- def set_memory_index(self, memory, bank, index):
- """Sets the index of @memory in @bank to @index"""
- raise Exception("Not implemented")
-
- def get_next_bank_index(self, bank):
- """Returns the next available bank index in @bank, or raises
+ def get_next_mapping_index(self, mapping):
+ """Returns the next available mapping index in @mapping, or raises
Exception if full"""
raise Exception("Not implemented")
-
class MTOBankModel(BankModel):
"""A bank model where one memory can be in multiple banks at once """
pass
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/ft7800.py
--- a/chirp/ft7800.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/ft7800.py Sat Mar 30 14:13:38 2013 -0700
@@ -336,7 +336,7 @@
class FT7800BankModel(chirp_common.BankModel):
"""Yaesu FT-7800/7900 bank model"""
def __init__(self, radio):
- chirp_common.BankModel.__init__(self, radio)
+ super(FT7800BankModel, self).__init__(radio)
self.__b2m_cache = defaultdict(list)
self.__m2b_cache = defaultdict(list)
@@ -344,24 +344,24 @@
if self.__b2m_cache:
return
- for bank in self.get_banks():
+ for bank in self.get_mappings():
self.__b2m_cache[bank.index] = self._get_bank_memories(bank)
for memnum in self.__b2m_cache[bank.index]:
self.__m2b_cache[memnum].append(bank.index)
- def get_num_banks(self):
+ def get_num_mappings(self):
return 20
- def get_banks(self):
+ def get_mappings(self):
banks = []
- for i in range(0, self.get_num_banks()):
+ for i in range(0, self.get_num_mappings()):
bank = chirp_common.Bank(self, "%i" % i, "BANK-%i" % (i + 1))
bank.index = i
banks.append(bank)
return banks
- def add_memory_to_bank(self, memory, bank):
+ def add_memory_to_mapping(self, memory, bank):
self.__precache()
index = memory.number - 1
@@ -371,7 +371,7 @@
self.__m2b_cache[memory.number].append(bank.index)
self.__b2m_cache[bank.index].append(memory.number)
- def remove_memory_from_bank(self, memory, bank):
+ def remove_memory_from_mapping(self, memory, bank):
self.__precache()
index = memory.number - 1
@@ -395,16 +395,16 @@
memories.append(i + 1)
return memories
- def get_bank_memories(self, bank):
+ def get_mapping_memories(self, bank):
self.__precache()
return [self._radio.get_memory(n)
for n in self.__b2m_cache[bank.index]]
- def get_memory_banks(self, memory):
+ def get_memory_mappings(self, memory):
self.__precache()
- _banks = self.get_banks()
+ _banks = self.get_mappings()
return [_banks[b] for b in self.__m2b_cache[memory.number]]
@directory.register
@@ -473,7 +473,7 @@
"""
class FT8800BankModel(FT7800BankModel):
- def get_num_banks(self):
+ def get_num_mappings(self):
return 10
@directory.register
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/icf.py
--- a/chirp/icf.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/icf.py Sat Mar 30 14:13:38 2013 -0700
@@ -485,10 +485,10 @@
central implementation can, with a few icom-specific radio interfaces
serve most/all of them"""
- def get_num_banks(self):
+ def get_num_mappings(self):
return self._radio._num_banks
- def get_banks(self):
+ def get_mappings(self):
banks = []
for i in range(0, self._radio._num_banks):
@@ -498,31 +498,32 @@
banks.append(bank)
return banks
- def add_memory_to_bank(self, memory, bank):
+ def add_memory_to_mapping(self, memory, bank):
self._radio._set_bank(memory.number, bank.index)
- def remove_memory_from_bank(self, memory, bank):
+ def remove_memory_from_mapping(self, memory, bank):
if self._radio._get_bank(memory.number) != bank.index:
raise Exception("Memory %i not in bank %s. Cannot remove." % \
(memory.number, bank))
self._radio._set_bank(memory.number, None)
- def get_bank_memories(self, bank):
+ def get_bank_mappings(self, bank):
memories = []
for i in range(*self._radio.get_features().memory_bounds):
if self._radio._get_bank(i) == bank.index:
memories.append(self._radio.get_memory(i))
return memories
- def get_memory_banks(self, memory):
+ def get_memory_mappings(self, memory):
index = self._radio._get_bank(memory.number)
if index is None:
return []
else:
- return [self.get_banks()[index]]
+ return [self.get_mappings()[index]]
-class IcomIndexedBankModel(IcomBankModel, chirp_common.BankIndexInterface):
+class IcomIndexedBankModel(IcomBankModel,
+ chirp_common.MappingModelIndexInterface):
"""Generic bank model for Icom radios with indexed banks"""
def get_index_bounds(self):
return self._radio._bank_index_bounds
@@ -531,7 +532,7 @@
return self._radio._get_bank_index(memory.number)
def set_memory_index(self, memory, bank, index):
- if bank not in self.get_memory_banks(memory):
+ if bank not in self.get_memory_mappings(memory):
raise Exception("Memory %i is not in bank %s" % (memory.number,
bank))
@@ -539,7 +540,7 @@
raise Exception("Invalid index")
self._radio._set_bank_index(memory.number, index)
- def get_next_bank_index(self, bank):
+ def get_next_mapping_index(self, bank):
indexes = []
for i in range(*self._radio.get_features().memory_bounds):
if self._radio._get_bank(i) == bank.index:
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/vx3.py
--- a/chirp/vx3.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/vx3.py Sat Mar 30 14:13:38 2013 -0700
@@ -110,10 +110,10 @@
class VX3BankModel(chirp_common.BankModel):
"""A VX-3 bank model"""
- def get_num_banks(self):
+ def get_num_mappings(self):
return 24
- def get_banks(self):
+ def get_mappings(self):
_banks = self._radio._memobj.bank_names
banks = []
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/vx5.py
--- a/chirp/vx5.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/vx5.py Sat Mar 30 14:13:38 2013 -0700
@@ -82,18 +82,18 @@
chirp_common.PowerLevel("L1", watts=0.05)]
class VX5BankModel(chirp_common.BankModel):
- def get_num_banks(self):
+ def get_num_mappings(self):
return 5
- def get_banks(self):
+ def get_mappings(self):
banks = []
- for i in range(0, self.get_num_banks()):
+ for i in range(0, self.get_num_mappings()):
bank = chirp_common.Bank(self, "%i" % (i+1), "MG%i" % (i+1))
bank.index = i
banks.append(bank)
return banks
- def add_memory_to_bank(self, memory, bank):
+ def add_memory_to_mapping(self, memory, bank):
_members = self._radio._memobj.bank_groups[bank.index].members
_bank_used = self._radio._memobj.bank_used[bank.index]
for i in range(0, len(_members)):
@@ -107,7 +107,7 @@
return True
raise Exception(_("{bank} is full").format(bank=bank))
- def remove_memory_from_bank(self, memory, bank):
+ def remove_memory_from_mapping(self, memory, bank):
_members = self._radio._memobj.bank_groups[bank.index].members
_bank_used = self._radio._memobj.bank_used[bank.index]
@@ -128,7 +128,7 @@
if not remaining_members:
_bank_used.current_member = 0xFF
- def get_bank_memories(self, bank):
+ def get_mapping_memories(self, bank):
memories = []
_members = self._radio._memobj.bank_groups[bank.index].members
@@ -143,10 +143,11 @@
memories.append(self._radio.get_memory(member.channel+1))
return memories
- def get_memory_banks(self, memory):
+ def get_memory_mappings(self, memory):
banks = []
- for bank in self.get_banks():
- if memory.number in [x.number for x in self.get_bank_memories(bank)]:
+ for bank in self.get_mappings():
+ if memory.number in [x.number for x in
+ self.get_mapping_memories(bank)]:
banks.append(bank)
return banks
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/vx7.py
--- a/chirp/vx7.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/vx7.py Sat Mar 30 14:13:38 2013 -0700
@@ -103,18 +103,18 @@
class VX7BankModel(chirp_common.BankModel):
"""A VX-7 Bank model"""
- def get_num_banks(self):
+ def get_num_mappings(self):
return 9
- def get_banks(self):
+ def get_mappings(self):
banks = []
- for i in range(0, self.get_num_banks()):
+ for i in range(0, self.get_num_mappings()):
bank = chirp_common.Bank(self, "%i" % (i+1), "MG%i" % (i+1))
bank.index = i
banks.append(bank)
return banks
- def add_memory_to_bank(self, memory, bank):
+ def add_memory_to_mapping(self, memory, bank):
_members = self._radio._memobj.bank_members[bank.index]
_bank_used = self._radio._memobj.bank_used[bank.index]
for i in range(0, 48):
@@ -123,7 +123,7 @@
_bank_used.in_use = 0x0000
break
- def remove_memory_from_bank(self, memory, bank):
+ def remove_memory_from_mapping(self, memory, bank):
_members = self._radio._memobj.bank_members[bank.index].members
_bank_used = self._radio._memobj.bank_used[bank.index]
@@ -143,7 +143,7 @@
if not remaining_members:
_bank_used.in_use = 0xFFFF
- def get_bank_memories(self, bank):
+ def get_mapping_memories(self, bank):
memories = []
_members = self._radio._memobj.bank_members[bank.index].members
@@ -158,11 +158,11 @@
memories.append(self._radio.get_memory(number+1))
return memories
- def get_memory_banks(self, memory):
+ def get_memory_mappings(self, memory):
banks = []
- for bank in self.get_banks():
+ for bank in self.get_mappings():
if memory.number in [x.number for x in
- self.get_bank_memories(bank)]:
+ self.get_mapping_memories(bank)]:
banks.append(bank)
return banks
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirp/vx8.py
--- a/chirp/vx8.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirp/vx8.py Sat Mar 30 14:13:38 2013 -0700
@@ -151,10 +151,10 @@
class VX8BankModel(chirp_common.BankModel):
"""A VX-8 bank model"""
- def get_num_banks(self):
+ def get_num_mappings(self):
return 24
- def get_banks(self):
+ def get_mappings(self):
banks = []
_banks = self._radio._memobj.bank_info
@@ -174,7 +174,7 @@
flags = self._radio._memobj.flag
# Find a suitable bank and MR for VFO A and B.
- for bank in self.get_banks():
+ for bank in self.get_mappings():
bank_used = self._radio._memobj.bank_used[bank.index]
if bank_used != 0xFFFF:
members = self._radio._memobj.bank_members[bank.index]
@@ -213,7 +213,7 @@
vfo_bak.mr_index = vfo.mr_index
vfo_bak.bank_enable = vfo.bank_enable
- def add_memory_to_bank(self, memory, bank):
+ def add_memory_to_mapping(self, memory, bank):
_members = self._radio._memobj.bank_members[bank.index]
_bank_used = self._radio._memobj.bank_used[bank.index]
for i in range(0, 100):
@@ -224,7 +224,7 @@
self.update_vfo()
- def remove_memory_from_bank(self, memory, bank):
+ def remove_memory_from_mapping(self, memory, bank):
_members = self._radio._memobj.bank_members[bank.index]
_bank_used = self._radio._memobj.bank_used[bank.index]
@@ -246,7 +246,7 @@
self.update_vfo()
- def get_bank_memories(self, bank):
+ def get_mapping_memories(self, bank):
memories = []
_members = self._radio._memobj.bank_members[bank.index]
_bank_used = self._radio._memobj.bank_used[bank.index]
@@ -260,11 +260,11 @@
return memories
- def get_memory_banks(self, memory):
+ def get_memory_mappings(self, memory):
banks = []
- for bank in self.get_banks():
+ for bank in self.get_mappings():
if memory.number in \
- [x.number for x in self.get_bank_memories(bank)]:
+ [x.number for x in self.get_mapping_memories(bank)]:
banks.append(bank)
return banks
diff -r ede5a4ccfd6e -r bf1c92a33bfd chirpui/bankedit.py
--- a/chirpui/bankedit.py Sat Mar 30 13:51:51 2013 -0700
+++ b/chirpui/bankedit.py Sat Mar 30 14:13:38 2013 -0700
@@ -22,68 +22,70 @@
from chirp import chirp_common
from chirpui import common, miscwidgets
-class BankNamesJob(common.RadioJob):
- def __init__(self, bm, editor, cb):
+class MappingNamesJob(common.RadioJob):
+ def __init__(self, model, editor, cb):
common.RadioJob.__init__(self, cb, None)
- self.__bm = bm
+ self.__model = model
self.__editor = editor
def execute(self, radio):
- self.__editor.banks = []
+ self.__editor.mappings = []
- banks = self.__bm.get_banks()
- for bank in banks:
- self.__editor.banks.append((bank, bank.get_name()))
+ mappings = self.__model.get_mappings()
+ for mapping in mappings:
+ self.__editor.mappings.append((mapping, mapping.get_name()))
gobject.idle_add(self.cb, *self.cb_args)
-class BankNameEditor(common.Editor):
+class MappingNameEditor(common.Editor):
+ TYPE = _("Mapping")
+
def refresh(self):
- def got_banks():
+ def got_mappings():
self._keys = []
- for bank, name in self.banks:
- self._keys.append(bank.get_index())
- self.listw.set_item(bank.get_index(),
- bank.get_index(),
+ for mapping, name in self.mappings:
+ self._keys.append(mapping.get_index())
+ self.listw.set_item(mapping.get_index(),
+ mapping.get_index(),
name)
- self.listw.connect("item-set", self.bank_changed)
+ self.listw.connect("item-set", self.mapping_changed)
- job = BankNamesJob(self._bm, self, got_banks)
- job.set_desc(_("Retrieving bank information"))
+ job = MappingNamesJob(self._model, self, got_mappings)
+ job.set_desc(_("Retrieving %s information") % self.TYPE)
self.rthread.submit(job)
- def get_bank_list(self):
- banks = []
+ def get_mapping_list(self):
+ mappings = []
keys = self.listw.get_keys()
for key in keys:
- banks.append(self.listw.get_item(key)[2])
+ mappings.append(self.listw.get_item(key)[2])
- return banks
-
- def bank_changed(self, listw, key):
+ return mappings
+
+ def mapping_changed(self, listw, key):
def cb(*args):
self.emit("changed")
name = self.listw.get_item(key)[2]
- bank, oldname = self.banks[self._keys.index(key)]
+ mapping, oldname = self.mappings[self._keys.index(key)]
def trigger_changed(*args):
self.emit("changed")
job = common.RadioJob(trigger_changed, "set_name", name)
- job.set_target(bank)
- job.set_desc(_("Setting name on bank"))
+ job.set_target(mapping)
+ job.set_desc(_("Setting name on %s") % self.TYPE.lower())
self.rthread.submit(job)
return True
- def __init__(self, rthread):
- super(BankNameEditor, self).__init__(rthread)
- self._bm = rthread.radio.get_bank_model()
+ def __init__(self, rthread, model):
+ super(MappingNameEditor, self).__init__(rthread)
+ self._model = model
types = [(gobject.TYPE_STRING, "key"),
- (gobject.TYPE_STRING, _("Bank")),
+ (gobject.TYPE_STRING, self.TYPE),
(gobject.TYPE_STRING, _("Name"))]
self.listw = miscwidgets.KeyedListWidget(types)
@@ -92,7 +94,7 @@
self.listw.set_sort_column(1, -1)
self.listw.show()
- self.banks = []
+ self.mappings = []
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -108,51 +110,61 @@
self.refresh()
self._loaded = True
-class MemoryBanksJob(common.RadioJob):
- def __init__(self, bm, cb, number):
+class BankNameEditor(MappingNameEditor):
+ TYPE = _("Bank")
+
+ def __init__(self, rthread):
+ model = rthread.radio.get_bank_model()
+ super(BankNameEditor, self).__init__(rthread, model)
+
+class MemoryMappingsJob(common.RadioJob):
+ def __init__(self, model, cb, number):
common.RadioJob.__init__(self, cb, None)
- self.__bm = bm
+ self.__model = model
self.__number = number
def execute(self, radio):
mem = radio.get_memory(self.__number)
if mem.empty:
- banks = []
+ mappings = []
indexes = []
else:
- banks = self.__bm.get_memory_banks(mem)
+ mappings = self.__model.get_memory_mappings(mem)
indexes = []
- if isinstance(self.__bm, chirp_common.BankIndexInterface):
- for bank in banks:
- indexes.append(self.__bm.get_memory_index(mem, bank))
- self.cb(mem, banks, indexes, *self.cb_args)
+ if isinstance(self.__model,
+ chirp_common.MappingModelIndexInterface):
+ for mapping in mappings:
+ indexes.append(self.__model.get_memory_index(mem, mapping))
+ self.cb(mem, mappings, indexes, *self.cb_args)
-class BankMembershipEditor(common.Editor):
+class MappingMembershipEditor(common.Editor):
+ TYPE = _("Mapping")
+
def _number_to_path(self, number):
return (number - self._rf.memory_bounds[0],)
- def _get_next_bank_index(self, bank):
- # NB: Only works for one-to-one bank models right now!
+ def _get_next_mapping_index(self, mapping):
+ # NB: Only works for one-to-one models right now!
iter = self._store.get_iter_first()
indexes = []
- ncols = len(self._cols) + len(self.banks)
+ ncols = len(self._cols) + len(self.mappings)
while iter:
vals = self._store.get(iter, *tuple([n for n in range(0, ncols)]))
loc = vals[self.C_LOC]
index = vals[self.C_INDEX]
- banks = vals[self.C_BANKS:]
- if True in banks and banks.index(True) == bank:
+ mappings = vals[self.C_MAPPINGS:]
+ if True in mappings and mappings.index(True) == mapping:
indexes.append(index)
iter = self._store.iter_next(iter)
- index_bounds = self._bm.get_index_bounds()
+ index_bounds = self._model.get_index_bounds()
num_indexes = index_bounds[1] - index_bounds[0]
indexes.sort()
for i in range(0, num_indexes):
if i not in indexes:
return i + index_bounds[0] # In case not zero-origin index
- return 0 # If the bank is full, just wrap around!
+ return 0 # If the mapping is full, just wrap around!
def _toggled_cb(self, rend, path, colnum):
try:
@@ -164,56 +176,60 @@
if not self._store.get(iter, self.C_FILLED)[0]:
return
- # The bank index is the column number, minus the 3 label columns
- bank, name = self.banks[colnum - len(self._cols)]
+ # The mapping index is the column number, minus the 3 label columns
+ mapping, name = self.mappings[colnum - len(self._cols)]
loc, = self._store.get(self._store.get_iter(path), self.C_LOC)
+ is_indexed = isinstance(self._model,
+ chirp_common.MappingModelIndexInterface)
+
if rend.get_active():
# Changing from True to False
- fn = "remove_memory_from_bank"
+ fn = "remove_memory_from_mapping"
index = None
else:
# Changing from False to True
- fn = "add_memory_to_bank"
- if self._rf.has_bank_index:
- index = self._get_next_bank_index(colnum - len(self._cols))
+ fn = "add_memory_to_mapping"
+ if is_indexed:
+ index = self._get_next_mapping_index(colnum - len(self._cols))
else:
index = None
def do_refresh_memory(*args):
- # Step 2: Update our notion of the memory's bank information
+ # Step 2: Update our notion of the memory's mapping information
self.refresh_memory(loc)
- def do_bank_index(result, memory):
+ def do_mapping_index(result, memory):
if isinstance(result, Exception):
- common.show_error("Failed to add {mem} to bank: {err}"
+ common.show_error("Failed to add {mem} to mapping: {err}"
.format(mem=memory.number,
err=str(result)),
parent=self.editorset.parent_window)
return
self.emit("changed")
- # Step 3: Set the memory's bank index (maybe)
- if not self._rf.has_bank_index or index is None:
+ # Step 3: Set the memory's mapping index (maybe)
+ if not is_indexed or index is None:
return do_refresh_memory()
job = common.RadioJob(do_refresh_memory,
- "set_memory_index", memory, bank, index)
- job.set_target(self._bm)
- job.set_desc(_("Updating bank index "
- "for memory {num}").format(num=memory.number))
+ "set_memory_index", memory, mapping, index)
+ job.set_target(self._model)
+ job.set_desc(_("Updating {type} index "
+ "for memory {num}").format(type=self.TYPE,
+ num=memory.number))
self.rthread.submit(job)
- def do_bank_adjustment(memory):
- # Step 1: Do the bank add/remove
- job = common.RadioJob(do_bank_index, fn, memory, bank)
- job.set_target(self._bm)
+ def do_mapping_adjustment(memory):
+ # Step 1: Do the mapping add/remove
+ job = common.RadioJob(do_mapping_index, fn, memory, mapping)
+ job.set_target(self._model)
job.set_cb_args(memory)
- job.set_desc(_("Updating bank information "
+ job.set_desc(_("Updating mapping information "
"for memory {num}").format(num=memory.number))
self.rthread.submit(job)
# Step 0: Fetch the memory
- job = common.RadioJob(do_bank_adjustment, "get_memory", loc)
+ job = common.RadioJob(do_mapping_adjustment, "get_memory", loc)
job.set_desc(_("Getting memory {num}").format(num=loc))
self.rthread.submit(job)
@@ -223,36 +239,37 @@
def refresh_memory(*args):
self.refresh_memory(loc)
- def set_index(banks, memory):
+ def set_index(mappings, memory):
self.emit("changed")
# Step 2: Set the index
job = common.RadioJob(refresh_memory, "set_memory_index",
- memory, banks[0], int(new))
- job.set_target(self._bm)
+ memory, mappings[0], int(new))
+ job.set_target(self._model)
job.set_desc(_("Setting index "
"for memory {num}").format(num=memory.number))
self.rthread.submit(job)
- def get_bank(memory):
- # Step 1: Get the first/only bank
- job = common.RadioJob(set_index, "get_memory_banks", memory)
+ def get_mapping(memory):
+ # Step 1: Get the first/only mapping
+ job = common.RadioJob(set_index, "get_memory_mappings", memory)
job.set_cb_args(memory)
- job.set_target(self._bm)
- job.set_desc(_("Getting bank for "
- "memory {num}").format(num=memory.number))
+ job.set_target(self._model)
+ job.set_desc(_("Getting {type} for "
+ "memory {num}").format(type=self.TYPE,
+ num=memory.number))
self.rthread.submit(job)
# Step 0: Get the memory
- job = common.RadioJob(get_bank, "get_memory", loc)
+ job = common.RadioJob(get_mapping, "get_memory", loc)
job.set_desc(_("Getting memory {num}").format(num=loc))
self.rthread.submit(job)
- def __init__(self, rthread, editorset):
- super(BankMembershipEditor, self).__init__(rthread)
+ def __init__(self, rthread, editorset, model):
+ super(MappingMembershipEditor, self).__init__(rthread)
self.editorset = editorset
self._rf = rthread.radio.get_features()
- self._bm = rthread.radio.get_bank_model()
+ self._model = model
self._view_cols = [
(_("Loc"), TYPE_INT, gtk.CellRendererText, ),
@@ -270,19 +287,22 @@
self.C_FREQ = 2
self.C_NAME = 3
self.C_INDEX = 4
- self.C_BANKS = 5 # and beyond
+ self.C_MAPPINGS = 5 # and beyond
cols = list(self._cols)
self._index_cache = []
- for i in range(0, self._bm.get_num_banks()):
- label = "Bank %i" % (i+1)
+ for i in range(0, self._model.get_num_mappings()):
+ label = "%s %i" % (self.TYPE, (i+1))
cols.append((label, TYPE_BOOLEAN, gtk.CellRendererToggle))
self._store = gtk.ListStore(*tuple([y for x,y,z in cols]))
self._view = gtk.TreeView(self._store)
+ is_indexed = isinstance(self._model,
+ chirp_common.MappingModelIndexInterface)
+
colnum = 0
for label, dtype, rtype in cols:
if not rtype:
@@ -305,7 +325,7 @@
elif colnum == self.C_INDEX:
rend.set_property("editable", True)
rend.connect("edited", self._index_edited_cb)
- col.set_visible(self._rf.has_bank_index)
+ col.set_visible(is_indexed)
colnum += 1
# A non-rendered column to absorb extra space in the row
@@ -329,7 +349,7 @@
self._loaded = False
def refresh_memory(self, number):
- def got_mem(memory, banks, indexes):
+ def got_mem(memory, mappings, indexes):
iter = self._store.get_iter(self._number_to_path(memory.number))
row = [self.C_FILLED, not memory.empty,
self.C_LOC, memory.number,
@@ -338,29 +358,30 @@
# Hack for only one index right now
self.C_INDEX, indexes and indexes[0] or 0,
]
- for i in range(0, len(self.banks)):
+ for i in range(0, len(self.mappings)):
row.append(i + len(self._cols))
- row.append(self.banks[i][0] in banks)
+ row.append(self.mappings[i][0] in mappings)
self._store.set(iter, *tuple(row))
if memory.number == self._rf.memory_bounds[1] - 1:
- print "Got all bank info in %s" % (time.time() - self._start)
+ print "Got all %s info in %s" % (self.TYPE,
+ (time.time() - self._start))
- job = MemoryBanksJob(self._bm, got_mem, number)
- job.set_desc(_("Getting bank information "
- "for memory {num}").format(num=number))
+ job = MemoryMappingsJob(self._model, got_mem, number)
+ job.set_desc(_("Getting {type} information "
+ "for memory {num}").format(type=self.TYPE, num=number))
self.rthread.submit(job)
def refresh_all_memories(self):
for i in range(*self._rf.memory_bounds):
self.refresh_memory(i)
- def refresh_banks(self, and_memories=False):
- def got_banks():
+ def refresh_mappings(self, and_memories=False):
+ def got_mappings():
for i in range(len(self._cols) - len(self._view_cols) - 1,
- len(self.banks)):
+ len(self.mappings)):
col = self._view.get_column(i + len(self._view_cols))
- bank, name = self.banks[i]
+ mapping, name = self.mappings[i]
if name:
col.set_title(name)
else:
@@ -368,8 +389,8 @@
if and_memories:
self.refresh_all_memories()
- job = BankNamesJob(self._bm, self, got_banks)
- job.set_desc(_("Getting bank information"))
+ job = MappingNamesJob(self._model, self, got_mappings)
+ job.set_desc(_("Getting %s information") % self.TYPE)
self.rthread.submit(job)
def focus(self):
@@ -378,7 +399,7 @@
return
self._start = time.time()
- self.refresh_banks(True)
+ self.refresh_mappings(True)
self._loaded = True
@@ -387,5 +408,15 @@
if self.is_focused():
self.refresh_all_memories()
+ def mappings_changed(self):
+ self.refresh_mappings()
+
+class BankMembershipEditor(MappingMembershipEditor):
+ TYPE = _("Bank")
+
+ def __init__(self, rthread, editorset):
+ model = rthread.radio.get_bank_model()
+ super(BankMembershipEditor, self).__init__(rthread, editorset, model)
+
def banks_changed(self):
- self.refresh_banks()
+ self.mappings_changed()
2
3
[chirp_devel] [PATCH] [Th9000] Adding devices file for new radio TYT TH9000D models #1035
by David Fannin 02 May '15
by David Fannin 02 May '15
02 May '15
# HG changeset patch
# User David Fannin <dfannin(a)sushisoft.com>
# Date 1430203203 25200
# Mon Apr 27 23:40:03 2015 -0700
# Node ID 237bb7557e557c689aa8d1863c40f2388b596974
# Parent 31a7494c324a347634c30cc3c9bded2ce83eeb21
[Th9000] Adding devices file for new radio TYT TH9000D models #1035
diff -r 31a7494c324a -r 237bb7557e55 chirp/drivers/th9000.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/th9000.py Mon Apr 27 23:40:03 2015 -0700
@@ -0,0 +1,839 @@
+# Copyright 2015 David Fannin KK6DF <kk6df(a)arrl.org>
+#
+# 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 os
+import struct
+import time
+import logging
+
+from chirp import bitwise
+from chirp import chirp_common
+from chirp import directory
+from chirp import errors
+from chirp import memmap
+from chirp import util
+from chirp.settings import RadioSettingGroup, RadioSetting, RadioSettings, \
+ RadioSettingValueList, RadioSettingValueString, RadioSettingValueBoolean, \
+ RadioSettingValueInteger, RadioSettingValueString, \
+ RadioSettingValueFloat, InvalidValueError
+
+LOG = logging.getLogger(__name__)
+
+#
+# Chirp Driver for TYT TH-9000D (models: 2M (144 Mhz), 1.25M (220 Mhz) and 70cm (440 Mhz) radios)
+#
+# Version 1.0
+#
+# Note: Features not working:
+# - DCS , Cross Signaling
+# - Skip channels
+#
+# Global Parameters
+#
+MMAPSIZE = 16384
+TONES = [62.5] + list(chirp_common.TONES)
+TMODES = ['','Tone','DTCS']
+DUPLEXES = ['','err','-','+'] # index 2 not used
+MODES = ['WFM','FM','NFM'] # 25k, 20k,15k bw
+TUNING_STEPS=[ 5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0 ] # index 0-9
+POWER_LEVELS=[chirp_common.PowerLevel("High", watts=65),
+ chirp_common.PowerLevel("Mid", watts=25),
+ chirp_common.PowerLevel("Low", watts=10)]
+
+CROSS_MODES = chirp_common.CROSS_MODES
+
+APO_LIST = [ "Off","30 min","1 hr","2 hrs" ]
+BGCOLOR_LIST = ["Blue","Orange","Purple"]
+BGBRIGHT_LIST = ["%s" % x for x in range(1,32)]
+SQUELCH_LIST = ["Off"] + ["Level %s" % x for x in range(1,20)]
+TIMEOUT_LIST = ["Off"] + ["%s min" % x for x in range(1,30)]
+TXPWR_LIST = ["60W","25W"] # maximum power for Hi setting
+TBSTFREQ_LIST = ["1750Hz","2100Hz","1000Hz","1450Hz"]
+BEEP_LIST = ["Off","On"]
+
+SETTING_LISTS = {
+ "auto_power_off": APO_LIST,
+ "bg_color" : BGCOLOR_LIST,
+ "bg_brightness" : BGBRIGHT_LIST,
+ "squelch" : SQUELCH_LIST,
+ "timeout_timer" : TIMEOUT_LIST,
+ "choose_tx_power": TXPWR_LIST,
+ "tbst_freq" : TBSTFREQ_LIST,
+ "voice_prompt" : BEEP_LIST
+}
+
+MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+ u8 unknown0000[16];
+ char idhdr[16];
+ u8 unknown0001[16];
+} fidhdr;
+"""
+#Overall Memory Map:
+#
+# Memory Map (Range 0x0100-3FF0, step 0x10):
+#
+# Field Start End Size
+# (hex) (hex) (hex)
+#
+# 1 Channel Set Flag 0100 011F 20
+# 2 Channel Skip Flag 0120 013F 20
+# 3 Blank/Unknown 0140 01EF B0
+# 4 Unknown 01F0 01FF 10
+# 5 TX/RX Range 0200 020F 10
+# 6 Bootup Passwd 0210 021F 10
+# 7 Options, Radio 0220 023F 20
+# 8 Unknown 0240 019F
+# 8B Startup Label 03E0 03E7 07
+# 9 Channel Bank 2000 38FF 1900
+# Channel 000 2000 201F 20
+# Channel 001 2020 202F 20
+# ...
+# Channel 199 38E0 38FF 20
+# 10 Blank/Unknown 3900 3FFF 6FF 14592 16383 1792
+# Total Map Size 16128 (2^8 = 16384)
+#
+# TH9000/220 memory map
+# section: 1 and 2: Channel Set/Skip Flags
+#
+# Channel Set (starts 0x100) : Channel Set bit is value 0 if a memory location in the channel bank is active.
+# Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
+#
+# Both flag maps are a total 24 bytes in length, aligned on 32 byte records.
+# bit = 0 channel set/no skip, 1 is channel not set/skip
+#
+# to index a channel:
+# cbyte = channel / 8 ;
+# cbit = channel % 8 ;
+# setflag = csetflag[cbyte].c[cbit] ;
+# skipflag = cskipflag[cbyte].c[cbit] ;
+#
+# channel range is 0-199, range is 32 bytes (last 7 unknown)
+#
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0100;
+struct {
+ bit c[8];
+} csetflag[32];
+
+struct {
+ u8 unknown0100[7];
+} ropt0100;
+
+#seekto 0x0120;
+struct {
+ bit c[8];
+} cskipflag[32];
+
+struct {
+ u8 unknown0120[7];
+} ropt0120;
+"""
+# TH9000 memory map
+# section: 5 TX/RX Range
+# used to set the TX/RX range of the radio (e.g. 222-228Mhz for 220 meter)
+# possible to set range for tx/rx
+#
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0200;
+struct {
+ bbcd txrangelow[4];
+ bbcd txrangehi[4];
+ bbcd rxrangelow[4];
+ bbcd rxrangehi[4];
+} freqrange;
+"""
+# TH9000 memory map
+# section: 6 bootup_passwd
+# used to set bootup passwd (see boot_passwd checkbox option)
+#
+# options - bootup password
+#
+# bytes:bit type description
+# ---------------------------------------------------------------------------
+# 6 u8 bootup_passwd[6] bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
+# 10 u8 unknown;
+#
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0210;
+struct {
+ u8 bootup_passwd[6];
+ u8 unknown2010[10];
+} ropt0210;
+"""
+# TH9000/220 memory map
+# section: 7 Radio Options
+# used to set a number of radio options
+#
+# bytes:bit type description
+# ---------------------------------------------------------------------------
+# 1 u8 display_mode display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
+# 1 u8 vfo_mr; vfo_mr , 0=vfo, mr=1
+# 1 u8 unknown;
+# 1 u8 squelch; squelch level, range 0-19, hex for menu
+# 1 u8 unknown[2];
+# 1 u8 channel_lock; if display_mode[channel] selected, then lock=1,no lock =0
+# 1 u8 unknown;
+# 1 u8 bg_brightness ; background brightness, range 0-21, hex, menu index
+# 1 u8 unknown;
+# 1 u8 bg_color ; bg color, menu index, blue 0 , orange 1, purple 2
+# 1 u8 tbst_freq ; tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz
+# 1 u8 timeout_timer; timeout timer, hex, value = minutes, 0= no timeout
+# 1 u8 unknown;
+# 1 u8 auto_power_off; auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
+# 1 u8 voice_prompt; voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
+#
+# description of function setup options, starting at 0x0230
+#
+# bytes:bit type description
+# ---------------------------------------------------------------------------
+# 1 u8 // 0
+# :4 unknown:6
+# :1 elim_sql_tail:1 eliminate squelsh tail when no ctcss checkbox (1=checked)
+# :1 sql_key_function "squelch off" 1 , "squelch momentary off" 0 , menu index
+# 2 u8 unknown[2] /1-2
+# 1 u8 // 3
+# :4 unknown:4
+# :1 inhibit_init_ops:1 //bit 5
+# :1 unknownD:1
+# :1 inhibit_setup_bg_chk:1 //bit 7
+# :1 unknown:1
+# 1 u8 tail_elim_type menu , (off=0,120=1,180=2), // 4
+# 1 u8 choose_tx_power menu , (60w=0,25w=1) // 5
+# 2 u8 unknown[2]; // 6-7
+# 1 u8 bootup_passwd_flag checkbox 1=on, 0=off // 8
+# 7 u8 unknown[7]; // 9-F
+#
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0220;
+struct {
+ u8 display_mode;
+ u8 vfo_mr;
+ u8 unknown0220A;
+ u8 squelch;
+ u8 unknown0220B[2];
+ u8 channel_lock;
+ u8 unknown0220C;
+ u8 bg_brightness;
+ u8 unknown0220D;
+ u8 bg_color;
+ u8 tbst_freq;
+ u8 timeout_timer;
+ u8 unknown0220E;
+ u8 auto_power_off;
+ u8 voice_prompt;
+ u8 unknown0230A:6,
+ elim_sql_tail:1,
+ sql_key_function:1;
+ u8 unknown0230B[2];
+ u8 unknown0230C:4,
+ inhibit_init_ops:1,
+ unknown0230D:1,
+ inhibit_setup_bg_chk:1,
+ unknown0230E:1;
+ u8 tail_elim_type;
+ u8 choose_tx_power;
+ u8 unknown0230F[2];
+ u8 bootup_passwd_flag;
+ u8 unknown0230G[7];
+} settings;
+"""
+# TH9000 memory map
+# section: 8B Startup Label
+#
+# bytes:bit type description
+# ---------------------------------------------------------------------------
+# 7 char start_label[7] label displayed at startup (usually your call sign)
+#
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x03E0;
+struct {
+ char startname[7];
+} slabel;
+"""
+# TH9000/220 memory map
+# section: 9 Channel Bank
+# description of channel bank (200 channels , range 0-199)
+# Each 32 Byte (0x20 hex) record:
+# bytes:bit type description
+# ---------------------------------------------------------------------------
+# 4 bbcd freq[4] receive frequency in packed binary coded decimal
+# 4 bbcd offset[4] transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
+# 1 u8
+# :4 unknown:4
+# :4 tuning_step:4 tuning step, menu index value from 0-9
+# 5,6.25,8.33,10,12.5,15,20,25,30,50
+# 1 u8
+# :4 unknown:4 not yet decoded, used for DCS coding?
+# :2 channel_width:2 channel spacing, menu index value from 0-3
+# 25,20,12.5
+# :1 reverse:1 reverse flag, 0=off, 1=on (reverses tx and rx freqs)
+# :1 txoff:1 transmitt off flag, 0=transmit , 1=do not transmit
+# 1 u8
+# :1 talkaround:1 talkaround flag, 0=off, 1=on (bypasses repeater)
+# :1 compander:1 compander flag, 0=off, 1=on (turns on/off voice compander option)
+# :2 unknown:2
+# :2 power:2 tx power setting, value range 0-2, 0=hi,1=med,2=lo
+# :2 duplex:2 duplex settings, 0=simplex,2= minus(-) offset, 3= plus (+) offset (see offset field)
+#
+# 1 u8
+# :4 unknown:4
+# :2 rxtmode:2 rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS (ctcss tone in field rxtone)
+# :2 txtmode:2 tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS (ctcss tone in field txtone)
+# 1 u8
+# :2 unknown:2
+# :6 txtone:6 tx ctcss tone, menu index
+# 1 u8
+# :2 unknown:2
+# :6 rxtone:6 rx ctcss tone, menu index
+# 1 u8 txcode ?, not used for ctcss
+# 1 u8 rxcode ?, not used for ctcss
+# 3 u8 unknown[3]
+# 7 char name[7] 7 byte char string for channel name
+# 1 u8
+# :6 unknown:6,
+# :2 busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy (lock out tx if channel busy)
+# 4 u8 unknownI[4];
+# 1 u8
+# :7 unknown:7
+# :1 scrambler:1 scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
+#
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x2000;
+struct {
+ bbcd freq[4];
+ bbcd offset[4];
+ u8 unknown2000A:4,
+ tuning_step:4;
+ u8 unknown2000B:4,
+ channel_width:2,
+ reverse:1,
+ txoff:1;
+ u8 talkaround:1,
+ compander:1,
+ unknown2000C:2,
+ power:2,
+ duplex:2;
+ u8 unknown2000D:4,
+ rxtmode:2,
+ txtmode:2;
+ u8 unknown2000E:2,
+ txtone:6;
+ u8 unknown2000F:2,
+ rxtone:6;
+ u8 txcode;
+ u8 rxcode;
+ u8 unknown2000G[3];
+ char name[7];
+ u8 unknown2000H:6,
+ busychannellockout:2;
+ u8 unknown2000I[4];
+ u8 unknown2000J:7,
+ scrambler:1;
+} memory[200] ;
+"""
+
+def _echo_write(radio, data):
+ try:
+ radio.pipe.write(data)
+ radio.pipe.read(len(data))
+ except Exception, e:
+ LOG.error("Error writing to radio: %s" % e)
+ raise errors.RadioError("Unable to write to radio")
+
+
+def _checksum(data):
+ cs = 0
+ for byte in data:
+ cs += ord(byte)
+ return cs % 256
+
+def _read(radio, length):
+ try:
+ data = radio.pipe.read(length)
+ except Exception, e:
+ LOG.error( "Error reading from radio: %s" % e)
+ raise errors.RadioError("Unable to read from radio")
+
+ if len(data) != length:
+ LOG.error( "Short read from radio (%i, expected %i)" % (len(data),
+ length))
+ LOG.debug(util.hexprint(data))
+ raise errors.RadioError("Short read from radio")
+ return data
+
+
+
+def _ident(radio):
+ radio.pipe.setTimeout(1)
+ _echo_write(radio,"PROGRAM")
+ response = radio.pipe.read(3)
+ if response != "QX\06":
+ LOG.debug( "Response was :\n%s" % util.hexprint(response))
+ raise errors.RadioError("Unsupported model")
+ _echo_write(radio, "\x02")
+ response = radio.pipe.read(16)
+ LOG.debug(util.hexprint(response))
+ if response[1:8] != "TH-9000":
+ LOG.error( "Looking for:\n%s" % util.hexprint("TH-9000"))
+ LOG.error( "Response was:\n%s" % util.hexprint(response))
+ raise errors.RadioError("Unsupported model")
+
+def _send(radio, cmd, addr, length, data=None):
+ frame = struct.pack(">cHb", cmd, addr, length)
+ if data:
+ frame += data
+ frame += chr(_checksum(frame[1:]))
+ frame += "\x06"
+ _echo_write(radio, frame)
+ LOG.debug("Sent:\n%s" % util.hexprint(frame))
+ if data:
+ result = radio.pipe.read(1)
+ if result != "\x06":
+ LOG.debug( "Ack was: %s" % repr(result))
+ raise errors.RadioError("Radio did not accept block at %04x" % addr)
+ return
+ result = _read(radio, length + 6)
+ LOG.debug("Got:\n%s" % util.hexprint(result))
+ header = result[0:4]
+ data = result[4:-2]
+ ack = result[-1]
+ if ack != "\x06":
+ LOG.debug("Ack was: %s" % repr(ack))
+ raise errors.RadioError("Radio NAK'd block at %04x" % addr)
+ _cmd, _addr, _length = struct.unpack(">cHb", header)
+ if _addr != addr or _length != _length:
+ LOG.debug( "Expected/Received:")
+ LOG.debug(" Length: %02x/%02x" % (length, _length))
+ LOG.debug( " Addr: %04x/%04x" % (addr, _addr))
+ raise errors.RadioError("Radio send unexpected block")
+ cs = _checksum(result[1:-2])
+ if cs != ord(result[-2]):
+ LOG.debug( "Calculated: %02x" % cs)
+ LOG.debug( "Actual: %02x" % ord(result[-2]))
+ raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
+ return data
+
+
+def _finish(radio):
+ endframe = "\x45\x4E\x44"
+ _echo_write(radio, endframe)
+ result = radio.pipe.read(1)
+ if result != "\x06":
+ LOG.error( "Got:\n%s" % util.hexprint(result))
+ raise errors.RadioError("Radio did not finish cleanly")
+
+def do_download(radio):
+
+ _ident(radio)
+
+ _memobj = None
+ data = ""
+
+ for start,end in radio._ranges:
+ for addr in range(start,end,0x10):
+ block = _send(radio,'R',addr,0x10)
+ data += block
+ status = chirp_common.Status()
+ status.cur = len(data)
+ status.max = end
+ status.msg = "Downloading from radio"
+ radio.status_fn(status)
+
+ _finish(radio)
+
+ return memmap.MemoryMap(data)
+
+def do_upload(radio):
+
+ _ident(radio)
+
+ for start,end in radio._ranges:
+ for addr in range(start,end,0x10):
+ if addr < 0x0100:
+ continue
+ block = radio._mmap[addr:addr+0x10]
+ _send(radio,'W',addr,len(block),block)
+ status = chirp_common.Status()
+ status.cur = addr
+ status.max = end
+ status.msg = "Uploading to Radio"
+ radio.status_fn(status)
+
+ _finish(radio)
+
+
+
+#
+# The base class, extended for use with other models
+#
+(a)directory.register
+class Th9000Radio(chirp_common.CloneModeRadio,
+ chirp_common.ExperimentalRadio):
+ """TYT TH-9000"""
+ VENDOR = "TYT"
+ MODEL = "TH9000 Base"
+ BAUD_RATE = 9600
+ valid_freq = [(900000000, 999000000)]
+
+
+ _memsize = MMAPSIZE
+ _ranges = [(0x0000,0x4000)]
+
+ @classmethod
+ def get_prompts(cls):
+ rp = chirp_common.RadioPrompts()
+ rp.experimental = ("The TYT TH-9000 driver is an beta version."
+ "Proceed with Caution and backup your data")
+ return rp
+
+ def get_features(self):
+ rf = chirp_common.RadioFeatures()
+ rf.has_settings = True
+ rf.has_bank = False
+ rf.has_cross = True
+ rf.has_tuning_step = False
+ rf.has_rx_dtcs = True
+ rf.valid_skips = ["","S"]
+ rf.memory_bounds = (0, 199)
+ rf.valid_name_length = 7
+ rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "-"
+ rf.valid_modes = MODES
+ rf.valid_tmodes = chirp_common.TONE_MODES
+ rf.valid_cross_modes = CROSS_MODES
+ rf.valid_power_levels = POWER_LEVELS
+ rf.valid_dtcs_codes = chirp_common.ALL_DTCS_CODES
+ rf.valid_bands = self.valid_freq
+ return rf
+
+ # Do a download of the radio from the serial port
+ def sync_in(self):
+ self._mmap = do_download(self)
+ self.process_mmap()
+
+ # Do an upload of the radio to the serial port
+ def sync_out(self):
+ do_upload(self)
+
+ def process_mmap(self):
+ self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+
+ # Return a raw representation of the memory object, which
+ # is very helpful for development
+ def get_raw_memory(self, number):
+ return repr(self._memobj.memory[number])
+
+ # not working yet
+ def _get_dcs_index(self, _mem,which):
+ base = getattr(_mem, '%scode' % which)
+ extra = getattr(_mem, '%sdcsextra' % which)
+ return (int(extra) << 8) | int(base)
+
+ def _set_dcs_index(self, _mem, which, index):
+ base = getattr(_mem, '%scode' % which)
+ extra = getattr(_mem, '%sdcsextra' % which)
+ base.set_value(index & 0xFF)
+ extra.set_value(index >> 8)
+
+
+ # Extract a high-level memory object from the low-level memory map
+ # This is called to populate a memory in the UI
+ def get_memory(self, number):
+ # Get a low-level memory object mapped to the image
+ _mem = self._memobj.memory[number]
+
+ # get flag info
+ cbyte = number / 8 ;
+ cbit = 7 - (number % 8) ;
+ setflag = self._memobj.csetflag[cbyte].c[cbit];
+ skipflag = self._memobj.cskipflag[cbyte].c[cbit];
+
+ mem = chirp_common.Memory()
+
+ mem.number = number # Set the memory number
+
+ if setflag == 1:
+ mem.empty = True
+ return mem
+
+ mem.freq = int(_mem.freq) * 100
+ mem.offset = int(_mem.offset) * 100
+ mem.name = str(_mem.name).rstrip() # Set the alpha tag
+ mem.duplex = DUPLEXES[_mem.duplex]
+ mem.mode = MODES[_mem.channel_width]
+ mem.power = POWER_LEVELS[_mem.power]
+
+ rxtone = txtone = None
+
+
+ rxmode = TMODES[_mem.rxtmode]
+ txmode = TMODES[_mem.txtmode]
+
+
+ rxpol = txpol = ""
+
+ # doesn't work
+ if rxmode == "Tone":
+ rxpol = ""
+ rxtone = TONES[_mem.rxtone]
+ elif rxmode == "DTCS":
+ rxpol = "N"
+ rxtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'rx')]
+
+ if txmode == "Tone":
+ txpol = ""
+ txtone = TONES[_mem.txtone]
+ elif txmode == "DTCS":
+ txpol = "N"
+ txtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'tx')]
+
+
+ chirp_common.split_tone_decode(mem,
+ (txmode, txtone, txpol),
+ (rxmode, rxtone, rxpol))
+
+ mem.skip = "S" if skipflag == 1 else ""
+
+
+ # We'll consider any blank (i.e. 0MHz frequency) to be empty
+ if mem.freq == 0:
+ mem.empty = True
+
+ return mem
+
+ # Store details about a high-level memory to the memory map
+ # This is called when a user edits a memory in the UI
+ def set_memory(self, mem):
+ # Get a low-level memory object mapped to the image
+
+ _mem = self._memobj.memory[mem.number]
+
+ cbyte = mem.number / 8
+ cbit = 7 - (mem.number % 8)
+
+ if mem.empty:
+ self._memobj.csetflag[cbyte].c[cbit] = 1
+ self._memobj.cskipflag[cbyte].c[cbit] = 1
+ return
+
+ self._memobj.csetflag[cbyte].c[cbit] = 0
+ self._memobj.cskipflag[cbyte].c[cbit] = 1 if (mem.skip == "S") else 0
+
+ _mem.set_raw("\x00" * 32)
+
+ _mem.freq = mem.freq / 100 # Convert to low-level frequency
+ _mem.offset = mem.offset / 100 # Convert to low-level frequency
+
+ _mem.name = mem.name.ljust(7)[:7] # Store the alpha tag
+ _mem.duplex = DUPLEXES.index(mem.duplex)
+
+
+ try:
+ _mem.channel_width = MODES.index(mem.mode)
+ except ValueError:
+ _mem.channel_width = 0
+
+ ((txmode, txtone, txpol),
+ (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
+
+ _mem.txtmode = TMODES.index(txmode)
+ _mem.rxtmode = TMODES.index(rxmode)
+
+ if txmode == "Tone":
+ _mem.txtone = TONES.index(txtone)
+ elif txmode == "DTCS":
+ self._set_dcs_index(_mem,'tx',chirp_common.ALL_DTCS_CODES.index(txtone))
+
+ if rxmode == "Tone":
+ _mem.rxtone = TONES.index(rxtone)
+ elif rxmode == "DTCS":
+ self._set_dcs_index(_mem, 'rx', chirp_common.ALL_DTCS_CODES.index(rxtone))
+
+ #_mem.txinv = txpol == "N"
+ #_mem.rxinv = rxpol == "N"
+
+
+ if mem.power:
+ _mem.power = POWER_LEVELS.index(mem.power)
+ else:
+ _mem.power = 0
+
+ def _get_settings(self):
+ _settings = self._memobj.settings
+ _freqrange = self._memobj.freqrange
+ _slabel = self._memobj.slabel
+
+ basic = RadioSettingGroup("basic","Global Settings")
+ freqrange = RadioSettingGroup("freqrange","Frequency Ranges")
+ top = RadioSettingGroup("top","All Settings",basic,freqrange)
+ settings = RadioSettings(top)
+
+ def _filter(name):
+ filtered = ""
+ for char in str(name):
+ if char in chirp_common.CHARSET_ASCII:
+ filtered += char
+ else:
+ filtered += ""
+ return filtered
+
+ val = RadioSettingValueString(0,7,_filter(_slabel.startname))
+ rs = RadioSetting("startname","Startup Label",val)
+ basic.append(rs)
+
+ rs = RadioSetting("bg_color","LCD Color",
+ RadioSettingValueList(BGCOLOR_LIST, BGCOLOR_LIST[_settings.bg_color]))
+ basic.append(rs)
+
+ rs = RadioSetting("bg_brightness","LCD Brightness",
+ RadioSettingValueList(BGBRIGHT_LIST, BGBRIGHT_LIST[_settings.bg_brightness]))
+ basic.append(rs)
+
+ rs = RadioSetting("squelch","Squelch Level",
+ RadioSettingValueList(SQUELCH_LIST, SQUELCH_LIST[_settings.squelch]))
+ basic.append(rs)
+
+ rs = RadioSetting("timeout_timer","Timeout Timer (TOT)",
+ RadioSettingValueList(TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout_timer]))
+ basic.append(rs)
+
+ rs = RadioSetting("auto_power_off","Auto Power Off (APO)",
+ RadioSettingValueList(APO_LIST, APO_LIST[_settings.auto_power_off]))
+ basic.append(rs)
+
+ rs = RadioSetting("voice_prompt","Beep Prompt",
+ RadioSettingValueList(BEEP_LIST, BEEP_LIST[_settings.voice_prompt]))
+ basic.append(rs)
+
+ rs = RadioSetting("tbst_freq","Tone Burst Frequency",
+ RadioSettingValueList(TBSTFREQ_LIST, TBSTFREQ_LIST[_settings.tbst_freq]))
+ basic.append(rs)
+
+ rs = RadioSetting("choose_tx_power","Max Level of TX Power",
+ RadioSettingValueList(TXPWR_LIST, TXPWR_LIST[_settings.choose_tx_power]))
+ basic.append(rs)
+
+ (flow,fhigh) = self.valid_freq[0]
+ flow /= 1000
+ fhigh /= 1000
+ fmidrange = (fhigh- flow)/2
+
+ rs = RadioSetting("txrangelow","TX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
+ flow + fmidrange,
+ int(_freqrange.txrangelow)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("txrangehi","TX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
+ fhigh,
+ int(_freqrange.txrangehi)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("rxrangelow","RX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
+ flow+fmidrange,
+ int(_freqrange.rxrangelow)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("rxrangehi","RX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
+ fhigh,
+ int(_freqrange.rxrangehi)/10))
+ freqrange.append(rs)
+
+ return settings
+
+ def get_settings(self):
+ try:
+ return self._get_settings()
+ except:
+ import traceback
+ LOG.error( "failed to parse settings")
+ traceback.print_exc()
+ return None
+
+ def set_settings(self,settings):
+ _settings = self._memobj.settings
+ for element in settings:
+ if not isinstance(element,RadioSetting):
+ self.set_settings(element)
+ continue
+ else:
+ try:
+ name = element.get_name()
+
+ if name in ["txrangelow","txrangehi","rxrangelow","rxrangehi"]:
+ LOG.debug( "setting %s = %s" % (name,int(element.value)*10))
+ setattr(self._memobj.freqrange,name,int(element.value)*10)
+ continue
+
+ if name in ["startname"]:
+ LOG.debug( "setting %s = %s" % (name, element.value))
+ setattr(self._memobj.slabel,name,element.value)
+ continue
+
+ obj = _settings
+ setting = element.get_name()
+
+ if element.has_apply_callback():
+ LOG.debug( "using apply callback")
+ element.run_apply_callback()
+ else:
+ 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):
+ if MMAPSIZE == len(filedata):
+ (flow,fhigh) = cls.valid_freq[0]
+ flow /= 1000000
+ fhigh /= 1000000
+
+ txmin=ord(filedata[0x200])*100 + (ord(filedata[0x201])>>4)*10 + ord(filedata[0x201])%16
+ txmax=ord(filedata[0x204])*100 + (ord(filedata[0x205])>>4)*10 + ord(filedata[0x205])%16
+ rxmin=ord(filedata[0x208])*100 + (ord(filedata[0x209])>>4)*10 + ord(filedata[0x209])%16
+ rxmax=ord(filedata[0x20C])*100 + (ord(filedata[0x20D])>>4)*10 + ord(filedata[0x20D])%16
+
+ if ( rxmin >= flow and rxmax <= fhigh and txmin >= flow and txmax <= fhigh ):
+ return True
+
+ return False
+
+(a)directory.register
+class Th9000220Radio(Th9000Radio):
+ """TYT TH-9000 220"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_220"
+ BAUD_RATE = 9600
+ valid_freq = [(220000000, 260000000)]
+
+(a)directory.register
+class Th9000144Radio(Th9000220Radio):
+ """TYT TH-9000 144"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_144"
+ BAUD_RATE = 9600
+ valid_freq = [(136000000, 174000000)]
+
+(a)directory.register
+class Th9000440Radio(Th9000220Radio):
+ """TYT TH-9000 440"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_440"
+ BAUD_RATE = 9600
+ valid_freq = [(400000000, 490000000)]
2
2
02 May '15
Hi All,
This weekend I accidentally discovered that the UV-B5/B6 radios can be
programmed with any frequency split across the band and not just the
69.995 MHz maximum offset when programming from the keypad. In
addition to that, the radio can also be successfully programmed for
cross-band operation.
The following code currently limits CHIRP to programming these radios
with maximun offset of 69.995 MHz in a single band with no checking to
make sure that the offset doesn't put the TX frequency out of the
working band of the radio.
def validate_memory(self, mem):
msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
if (mem.duplex == "split" and abs(mem.freq - mem.offset) > 69995000) \
or (mem.duplex in ["+", "-"] and mem.offset > 69995000):
msgs.append(chirp_common.ValidationError(
"Max split is 69.995MHz"))
return msgs
What I propose to do is change the code to something like the
following. It allows programming any split within the VHF or UHF band
and any cross-band pair of frequencies. If the user programs an Offset
or Split frequency that puts the TX frequency outside of the band
limits, an error is displayed.
def validate_memory(self, mem):
msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
valid = False
for lo, hi in VALID_BANDS:
if mem.duplex == "split":
freq=chirp_common.format_freq(mem.offset)
_msg = "Offset %s is out of supported range" % freq
if lo <= mem.offset < hi:
valid = True
break
elif mem.duplex == "-":
freq=chirp_common.format_freq(mem.offset)
_msg = "Offset %s is out of supported range" % freq
if lo <= mem.freq - mem.offset < hi:
valid = True
break
elif mem.duplex == "+":
freq=chirp_common.format_freq(mem.offset)
_msg = "Offset %s is out of supported range" % freq
if lo <= mem.freq + mem.offset < hi:
valid = True
break
elif not mem.duplex in ["+", "-", "split"]:
_msg = " "
if lo <= mem.freq < hi:
valid = True
break
if not valid:
msgs.append(chirp_common.ValidationError(_msg))
return msgs
My question is...
Is there a more tidy way to code this or should I go ahead and submit
something like this?
Thanks,
Jim KC9HI
3
9
30 Apr '15
# HG changeset patch
# User Jim Unroe <rock.unroe(a)gmail.com>
# Date 1430437240 14400
# Node ID a0d812f186dff963cdbdd8d0bbee029aa1585a2a
# Parent 510510df3272f0e6e745c88d1a69046ad07769b7
[BF-F8HP] Add Support for New Firmware String
Add detection for F5R3407 firmware.
related to #2551
diff -r 510510df3272 -r a0d812f186df chirp/drivers/uv5r.py
--- a/chirp/drivers/uv5r.py Thu Apr 30 12:35:58 2015 +0200
+++ b/chirp/drivers/uv5r.py Thu Apr 30 19:40:40 2015 -0400
@@ -284,7 +284,7 @@
BASETYPE_BJ55 = ["BJ55"] # needed for for the Baojie UV-55 in bjuv55.py
BASETYPE_UV6 = ["BF1"]
BASETYPE_KT980HP = ["BFP3V3 B"]
-BASETYPE_F8HP = ["BFP3V3 F", "N5R-3", "N5R3"]
+BASETYPE_F8HP = ["BFP3V3 F", "N5R-3", "N5R3", "F5R3"]
BASETYPE_LIST = BASETYPE_UV5R + BASETYPE_F11 + BASETYPE_UV82 + \
BASETYPE_BJ55 + BASETYPE_UV6 + BASETYPE_KT980HP + \
BASETYPE_F8HP
1
0
[chirp_devel] [PATCH] This is the latest experimential version for the TYT-TH9000D radio driver
by David Fannin 28 Apr '15
by David Fannin 28 Apr '15
28 Apr '15
# HG changeset patch
# User David Fannin <dfannin(a)sushisoft.com>
# Date 1429419742 25200
# Sat Apr 18 22:02:22 2015 -0700
# Node ID d5eee1cd54180bd2ae07f86d06ab660935c14190
# Parent 31a7494c324a347634c30cc3c9bded2ce83eeb21
This is the latest experimential version for the TYT-TH9000D radio driver.
This patch covers the 3 existing TH9000 models - 2meters, 220 and 440 radios in a single class file.
All previous features are currently working for this patch, with the exception of automatic file identifcation. The TH radios do not have a identifer that can safely distingush between the radios, this feature is still TBD. You will need to manually select the correct model when you open a radio image file.
diff -r 31a7494c324a -r d5eee1cd5418 chirp/drivers/th9000.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/drivers/th9000.py Sat Apr 18 22:02:22 2015 -0700
@@ -0,0 +1,882 @@
+# Copyright 2012 Dan Smith <dsmith(a)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 os
+import struct
+import time
+import logging
+
+from chirp import bitwise
+from chirp import chirp_common
+from chirp import directory
+from chirp import errors
+from chirp import memmap
+from chirp import util
+from chirp.settings import RadioSettingGroup, RadioSetting, RadioSettings, \
+ RadioSettingValueList, RadioSettingValueString, RadioSettingValueBoolean, \
+ RadioSettingValueInteger, RadioSettingValueString, \
+ RadioSettingValueFloat, InvalidValueError
+
+#from chirp.settings import RadioSetting, RadioSettingGroup, \
+# RadioSettingValueInteger, RadioSettingValueList, \
+# RadioSettingValueBoolean, RadioSettingValueString, \
+# RadioSettingValueFloat, InvalidValueError
+
+LOG = logging.getLogger(__name__)
+
+#
+# Chirp Driver for TYT TH-9000D Radio (2 meter, 1.25 and 70cm radios)
+# by David Fannin <dfannin(a)sushisoft.com>, KK6DF
+#
+# Version 0.5 (Experimental - Known Bugs and Issues)
+# Use for development purposes only!
+# Features working:
+# - single class file for 3 radio types - 144, 220 and 440 mhz models)
+# - Download from Radio
+# - Display Memories (only None, Tone, TSQL signalling supported)
+# - Save image file
+# - memory map decoded (about 90%)
+# - Upload to radio
+# - Modification of memories
+# - feature settings
+# - added Startup ID label
+#
+# Features not working:
+# - DCS , Cross Signaling
+# - Skip channels
+
+
+#
+# Global Parameters
+#
+MMAPSIZE = 16128
+TONES = [62.5] + list(chirp_common.TONES)
+TMODES = ['','Tone','DTCS']
+DUPLEXES = ['','err','-','+'] # index 2 not used
+MODES = ['WFM','FM','NFM'] # 25k, 20k,15k bw
+TUNING_STEPS=[ 5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0 ] # index 0-9
+POWER_LEVELS=[chirp_common.PowerLevel("High", watts=65),
+ chirp_common.PowerLevel("Mid", watts=25),
+ chirp_common.PowerLevel("Low", watts=10)]
+
+CROSS_MODES = chirp_common.CROSS_MODES
+
+APO_LIST = [ "Off","30 min","1 hr","2 hrs" ]
+BGCOLOR_LIST = ["Blue","Orange","Purple"]
+BGBRIGHT_LIST = ["%s" % x for x in range(1,32)]
+SQUELCH_LIST = ["Off"] + ["Level %s" % x for x in range(1,20)]
+TIMEOUT_LIST = ["Off"] + ["%s min" % x for x in range(1,30)]
+TXPWR_LIST = ["60W","25W"] # maximum power for Hi setting
+TBSTFREQ_LIST = ["1750Hz","2100Hz","1000Hz","1450Hz"]
+BEEP_LIST = ["Off","On"]
+
+SETTING_LISTS = {
+ "auto_power_off": APO_LIST,
+ "bg_color" : BGCOLOR_LIST,
+ "bg_brightness" : BGBRIGHT_LIST,
+ "squelch" : SQUELCH_LIST,
+ "timeout_timer" : TIMEOUT_LIST,
+ "choose_tx_power": TXPWR_LIST,
+ "tbst_freq" : TBSTFREQ_LIST,
+ "voice_prompt" : BEEP_LIST
+}
+
+
+#
+#
+#
+
+MEM_FORMAT = """
+#seekto 0x0000;
+struct {
+ u8 unknown0000[16];
+ char idhdr[16];
+ u8 unknown0001[16];
+} fidhdr;
+"""
+
+"""
+Overall Memory Map:
+
+ Memory Map (Range 0x0100-3FF0, step 0x10):
+
+ Field Start End Size
+ (hex) (hex) (hex)
+
+ 1 Channel Set Flag 0100 011F 20
+ 2 Channel Skip Flag 0120 013F 20
+ 3 Blank/Unknown 0140 01EF B0
+ 4 Unknown 01F0 01FF 10
+ 5 TX/RX Range 0200 020F 10
+ 6 Bootup Passwd 0210 021F 10
+ 7 Options, Radio 0220 023F 20
+ 8 Unknown 0240 019F
+ 8B Startup Label 03E0 03E7 07
+ 9 Channel Bank 2000 38FF 1900
+ Channel 000 2000 201F 20
+ Channel 001 2020 202F 20
+ ...
+ Channel 199 38E0 38FF 20
+ 10 Blank/Unknown 3900 3FFF 6FF 14592 16383 1792
+ Total Map Size 16128 (2^8 = 16384)
+
+
+"""
+
+"""
+ TH9000/220 memory map
+ section: 1 and 2: Channel Set/Skip Flags
+
+ Channel Set (starts 0x100) : Channel Set bit is value 0 if a memory location in the channel bank is active.
+ Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
+
+ Both flag maps are a total 24 bytes in length, aligned on 32 byte records.
+ bit = 0 channel set/no skip, 1 is channel not set/skip
+
+ to index a channel:
+ cbyte = channel / 8 ;
+ cbit = channel % 8 ;
+ setflag = csetflag[cbyte].c[cbit] ;
+ skipflag = cskipflag[cbyte].c[cbit] ;
+
+ channel range is 0-199, range is 32 bytes (last 7 unknown)
+"""
+
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0100;
+struct {
+ bit c[8];
+} csetflag[32];
+
+struct {
+ u8 unknown0100[7];
+} ropt0100;
+
+#seekto 0x0120;
+struct {
+ bit c[8];
+} cskipflag[32];
+
+struct {
+ u8 unknown0120[7];
+} ropt0120;
+"""
+
+"""
+ TH9000/220 memory map
+ section: 5 TX/RX Range
+ used to set the TX/RX range of the radio (e.g. 222-228Mhz for 220 meter)
+ possible to set range to 220-260Mhz for tx/rx
+
+"""
+
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0200;
+struct {
+ bbcd txrangelow[4];
+ bbcd txrangehi[4];
+ bbcd rxrangelow[4];
+ bbcd rxrangehi[4];
+} freqrange;
+"""
+
+"""
+ TH9000/220 memory map
+ section: 6 bootup_passwd
+ used to set bootup passwd (see boot_passwd checkbox option)
+
+ options - bootup password
+
+ bytes:bit type description
+ ---------------------------------------------------------------------------
+ 6 u8 bootup_passwd[6] bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
+ 10 u8 unknown;
+
+"""
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0210;
+struct {
+ u8 bootup_passwd[6];
+ u8 unknown2010[10];
+} ropt0210;
+"""
+
+"""
+ TH9000/220 memory map
+ section: 7 Radio Options
+ used to set a number of radio options
+
+ bytes:bit type description
+ ---------------------------------------------------------------------------
+ 1 u8 display_mode display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
+ 1 u8 vfo_mr; vfo_mr , 0=vfo, mr=1
+ 1 u8 unknown;
+ 1 u8 squelch; squelch level, range 0-19, hex for menu
+ 1 u8 unknown[2];
+ 1 u8 channel_lock; if display_mode[channel] selected, then lock=1,no lock =0
+ 1 u8 unknown;
+ 1 u8 bg_brightness ; background brightness, range 0-21, hex, menu index
+ 1 u8 unknown;
+ 1 u8 bg_color ; bg color, menu index, blue 0 , orange 1, purple 2
+ 1 u8 tbst_freq ; tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz
+ 1 u8 timeout_timer; timeout timer, hex, value = minutes, 0= no timeout
+ 1 u8 unknown;
+ 1 u8 auto_power_off; auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
+ 1 u8 voice_prompt; voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
+
+ description of function setup options, starting at 0x0230
+
+ bytes:bit type description
+ ---------------------------------------------------------------------------
+ 1 u8 // 0
+ :4 unknown:6
+ :1 elim_sql_tail:1 eliminate squelsh tail when no ctcss checkbox (1=checked)
+ :1 sql_key_function "squelch off" 1 , "squelch momentary off" 0 , menu index
+ 2 u8 unknown[2] /1-2
+ 1 u8 // 3
+ :4 unknown:4
+ :1 inhibit_init_ops:1 //bit 5
+ :1 unknownD:1
+ :1 inhibit_setup_bg_chk:1 //bit 7
+ :1 unknown:1
+ 1 u8 tail_elim_type menu , (off=0,120=1,180=2), // 4
+ 1 u8 choose_tx_power menu , (60w=0,25w=1) // 5
+ 2 u8 unknown[2]; // 6-7
+ 1 u8 bootup_passwd_flag checkbox 1=on, 0=off // 8
+ 7 u8 unknown[7]; // 9-F
+
+"""
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x0220;
+struct {
+ u8 display_mode;
+ u8 vfo_mr;
+ u8 unknown0220A;
+ u8 squelch;
+ u8 unknown0220B[2];
+ u8 channel_lock;
+ u8 unknown0220C;
+ u8 bg_brightness;
+ u8 unknown0220D;
+ u8 bg_color;
+ u8 tbst_freq;
+ u8 timeout_timer;
+ u8 unknown0220E;
+ u8 auto_power_off;
+ u8 voice_prompt;
+ u8 unknown0230A:6,
+ elim_sql_tail:1,
+ sql_key_function:1;
+ u8 unknown0230B[2];
+ u8 unknown0230C:4,
+ inhibit_init_ops:1,
+ unknown0230D:1,
+ inhibit_setup_bg_chk:1,
+ unknown0230E:1;
+ u8 tail_elim_type;
+ u8 choose_tx_power;
+ u8 unknown0230F[2];
+ u8 bootup_passwd_flag;
+ u8 unknown0230G[7];
+} settings;
+"""
+
+"""
+ TH9000/220 memory map
+ section: 8B Startup Label
+
+ bytes:bit type description
+ ---------------------------------------------------------------------------
+ 7 char start_label[7] label displayed at startup (usually your call sign)
+"""
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x03E0;
+struct {
+ char startname[7];
+} slabel;
+"""
+
+"""
+ TH9000/220 memory map
+ section: 9 Channel Bank
+ description of channel bank (200 channels , range 0-199)
+ Each 32 Byte (0x20 hex) record:
+ bytes:bit type description
+ ---------------------------------------------------------------------------
+ 4 bbcd freq[4] receive frequency in packed binary coded decimal
+ 4 bbcd offset[4] transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
+ 1 u8
+ :4 unknown:4
+ :4 tuning_step:4 tuning step, menu index value from 0-9
+ 5,6.25,8.33,10,12.5,15,20,25,30,50
+ 1 u8
+ :4 unknown:4 not yet decoded, used for DCS coding?
+ :2 channel_width:2 channel spacing, menu index value from 0-3
+ 25,20,12.5
+ :1 reverse:1 reverse flag, 0=off, 1=on (reverses tx and rx freqs)
+ :1 txoff:1 transmitt off flag, 0=transmit , 1=do not transmit
+ 1 u8
+ :1 talkaround:1 talkaround flag, 0=off, 1=on (bypasses repeater)
+ :1 compander:1 compander flag, 0=off, 1=on (turns on/off voice compander option)
+ :2 unknown:2
+ :2 power:2 tx power setting, value range 0-2, 0=hi,1=med,2=lo
+ :2 duplex:2 duplex settings, 0=simplex,2= minus(-) offset, 3= plus (+) offset (see offset field)
+
+ 1 u8
+ :4 unknown:4
+ :2 rxtmode:2 rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS (ctcss tone in field rxtone)
+ :2 txtmode:2 tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS (ctcss tone in field txtone)
+ 1 u8
+ :2 unknown:2
+ :6 txtone:6 tx ctcss tone, menu index
+ 1 u8
+ :2 unknown:2
+ :6 rxtone:6 rx ctcss tone, menu index
+ 1 u8 txcode ?, not used for ctcss
+ 1 u8 rxcode ?, not used for ctcss
+ 3 u8 unknown[3]
+ 7 char name[7] 7 byte char string for channel name
+ 1 u8
+ :6 unknown:6,
+ :2 busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy (lock out tx if channel busy)
+ 4 u8 unknownI[4];
+ 1 u8
+ :7 unknown:7
+ :1 scrambler:1 scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
+"""
+
+
+
+MEM_FORMAT = MEM_FORMAT + """
+#seekto 0x2000;
+struct {
+ bbcd freq[4];
+ bbcd offset[4];
+ u8 unknown2000A:4,
+ tuning_step:4;
+ u8 unknown2000B:4,
+ channel_width:2,
+ reverse:1,
+ txoff:1;
+ u8 talkaround:1,
+ compander:1,
+ unknown2000C:2,
+ power:2,
+ duplex:2;
+ u8 unknown2000D:4,
+ rxtmode:2,
+ txtmode:2;
+ u8 unknown2000E:2,
+ txtone:6;
+ u8 unknown2000F:2,
+ rxtone:6;
+ u8 txcode;
+ u8 rxcode;
+ u8 unknown2000G[3];
+ char name[7];
+ u8 unknown2000H:6,
+ busychannellockout:2;
+ u8 unknown2000I[4];
+ u8 unknown2000J:7,
+ scrambler:1;
+} memory[200] ;
+"""
+
+
+def _echo_write(radio, data):
+ try:
+ radio.pipe.write(data)
+ radio.pipe.read(len(data))
+ except Exception, e:
+ LOG.error("Error writing to radio: %s" % e)
+ raise errors.RadioError("Unable to write to radio")
+
+
+def _checksum(data):
+ cs = 0
+ for byte in data:
+ cs += ord(byte)
+ return cs % 256
+
+def _read(radio, length):
+ try:
+ data = radio.pipe.read(length)
+ except Exception, e:
+ LOG.error( "Error reading from radio: %s" % e)
+ raise errors.RadioError("Unable to read from radio")
+
+ if len(data) != length:
+ LOG.error( "Short read from radio (%i, expected %i)" % (len(data),
+ length))
+ LOG.debug(util.hexprint(data))
+ raise errors.RadioError("Short read from radio")
+ return data
+
+
+
+def _ident(radio):
+ radio.pipe.setTimeout(1)
+ _echo_write(radio,"PROGRAM")
+ response = radio.pipe.read(3)
+ if response != "QX\06":
+ LOG.debug( "Response was :\n%s" % util.hexprint(response))
+ raise errors.RadioError("Unsupported model")
+ _echo_write(radio, "\x02")
+ response = radio.pipe.read(16)
+ LOG.debug(util.hexprint(response))
+ if response[1:8] != "TH-9000":
+ LOG.error( "Looking for:\n%s" % util.hexprint("TH-9000"))
+ LOG.error( "Response was:\n%s" % util.hexprint(response))
+ raise errors.RadioError("Unsupported model")
+
+def _send(radio, cmd, addr, length, data=None):
+ frame = struct.pack(">cHb", cmd, addr, length)
+ if data:
+ frame += data
+ frame += chr(_checksum(frame[1:]))
+ frame += "\x06"
+ _echo_write(radio, frame)
+ LOG.debug("Sent:\n%s" % util.hexprint(frame))
+ if data:
+ result = radio.pipe.read(1)
+ if result != "\x06":
+ LOG.debug( "Ack was: %s" % repr(result))
+ raise errors.RadioError("Radio did not accept block at %04x" % addr)
+ return
+ result = _read(radio, length + 6)
+ LOG.debug("Got:\n%s" % util.hexprint(result))
+ header = result[0:4]
+ data = result[4:-2]
+ ack = result[-1]
+ if ack != "\x06":
+ LOG.debug("Ack was: %s" % repr(ack))
+ raise errors.RadioError("Radio NAK'd block at %04x" % addr)
+ _cmd, _addr, _length = struct.unpack(">cHb", header)
+ if _addr != addr or _length != _length:
+ LOG.debug( "Expected/Received:")
+ LOG.debug(" Length: %02x/%02x" % (length, _length))
+ LOG.debug( " Addr: %04x/%04x" % (addr, _addr))
+ raise errors.RadioError("Radio send unexpected block")
+ cs = _checksum(result[1:-2])
+ if cs != ord(result[-2]):
+ LOG.debug( "Calculated: %02x" % cs)
+ LOG.debug( "Actual: %02x" % ord(result[-2]))
+ raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
+ return data
+
+
+def _finish(radio):
+ endframe = "\x45\x4E\x44"
+ _echo_write(radio, endframe)
+ result = radio.pipe.read(1)
+ if result != "\x06":
+ LOG.error( "Got:\n%s" % util.hexprint(result))
+ raise errors.RadioError("Radio did not finish cleanly")
+
+def do_download(radio):
+
+ _ident(radio)
+
+ _memobj = None
+ data = ""
+
+ for start,end in radio._ranges:
+ for addr in range(start,end,0x10):
+ block = _send(radio,'R',addr,0x10)
+ data += block
+ status = chirp_common.Status()
+ status.cur = len(data)
+ status.max = end
+ status.msg = "Downloading from radio"
+ radio.status_fn(status)
+
+ _finish(radio)
+
+ return memmap.MemoryMap(data)
+
+def do_upload(radio):
+
+ _ident(radio)
+
+ for start,end in radio._ranges:
+ for addr in range(start,end,0x10):
+ if addr < 0x0100:
+ continue
+ block = radio._mmap[addr:addr+0x10]
+ _send(radio,'W',addr,len(block),block)
+ status = chirp_common.Status()
+ status.cur = addr
+ status.max = end
+ status.msg = "Uploading to Radio"
+ radio.status_fn(status)
+
+ _finish(radio)
+
+
+
+(a)directory.register
+class Th9000Radio(chirp_common.CloneModeRadio,
+ chirp_common.ExperimentalRadio):
+ """TYT TH-9000"""
+ VENDOR = "TYT"
+ MODEL = "TH9000"
+ BAUD_RATE = 9600
+ _file_ident = "TH9000 XXX"
+ valid_freq = [(220000000, 260000000)]
+
+
+ _memsize = MMAPSIZE
+ _ranges = [(0x0000,0x4000)]
+
+ @classmethod
+ def get_prompts(cls):
+ rp = chirp_common.RadioPrompts()
+ rp.experimental = ("The TYT TH-9000 driver is an alpha version."
+ "Use only for testing and development"
+ "Proceed with Caution and backup your data"
+ "as you may lose it using this driver!")
+ return rp
+
+ def get_features(self):
+ rf = chirp_common.RadioFeatures()
+ rf.has_settings = True
+ rf.has_bank = False
+ rf.has_cross = True
+ rf.has_tuning_step = False
+ rf.has_rx_dtcs = True
+ rf.valid_skips = ["","S"]
+ rf.memory_bounds = (0, 199)
+ rf.valid_name_length = 7
+ rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "-"
+ rf.valid_modes = MODES
+ rf.valid_tmodes = chirp_common.TONE_MODES
+ rf.valid_cross_modes = CROSS_MODES
+ rf.valid_power_levels = POWER_LEVELS
+ rf.valid_dtcs_codes = chirp_common.ALL_DTCS_CODES
+ rf.valid_bands = self.valid_freq
+ return rf
+
+ # Do a download of the radio from the serial port
+ def sync_in(self):
+ self._mmap = do_download(self)
+ self.process_mmap()
+
+ # Do an upload of the radio to the serial port
+ def sync_out(self):
+ do_upload(self)
+
+ def process_mmap(self):
+ self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+
+ # Return a raw representation of the memory object, which
+ # is very helpful for development
+ def get_raw_memory(self, number):
+ return repr(self._memobj.memory[number])
+
+
+ # not working
+ def _get_dcs_index(self, _mem,which):
+ base = getattr(_mem, '%scode' % which)
+ extra = getattr(_mem, '%sdcsextra' % which)
+ return (int(extra) << 8) | int(base)
+
+ def _set_dcs_index(self, _mem, which, index):
+ base = getattr(_mem, '%scode' % which)
+ extra = getattr(_mem, '%sdcsextra' % which)
+ base.set_value(index & 0xFF)
+ extra.set_value(index >> 8)
+
+
+ # Extract a high-level memory object from the low-level memory map
+ # This is called to populate a memory in the UI
+ def get_memory(self, number):
+ # Get a low-level memory object mapped to the image
+ _mem = self._memobj.memory[number]
+
+ # get flag info
+ cbyte = number / 8 ;
+ cbit = 7 - (number % 8) ;
+ setflag = self._memobj.csetflag[cbyte].c[cbit];
+ skipflag = self._memobj.cskipflag[cbyte].c[cbit];
+
+ mem = chirp_common.Memory()
+
+ mem.number = number # Set the memory number
+
+ if setflag == 1:
+ mem.empty = True
+ return mem
+
+ mem.freq = int(_mem.freq) * 100
+ mem.offset = int(_mem.offset) * 100
+ mem.name = str(_mem.name).rstrip() # Set the alpha tag
+ mem.duplex = DUPLEXES[_mem.duplex]
+ mem.mode = MODES[_mem.channel_width]
+ mem.power = POWER_LEVELS[_mem.power]
+
+ rxtone = txtone = None
+
+
+ rxmode = TMODES[_mem.rxtmode]
+ txmode = TMODES[_mem.txtmode]
+
+
+ rxpol = txpol = ""
+
+ # doesn't work
+ if rxmode == "Tone":
+ rxpol = ""
+ rxtone = TONES[_mem.rxtone]
+ elif rxmode == "DTCS":
+ rxpol = "N"
+ rxtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'rx')]
+
+ if txmode == "Tone":
+ txpol = ""
+ txtone = TONES[_mem.txtone]
+ elif txmode == "DTCS":
+ txpol = "N"
+ txtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'tx')]
+
+
+ chirp_common.split_tone_decode(mem,
+ (txmode, txtone, txpol),
+ (rxmode, rxtone, rxpol))
+
+ mem.skip = "S" if skipflag == 1 else ""
+
+
+ # We'll consider any blank (i.e. 0MHz frequency) to be empty
+ if mem.freq == 0:
+ mem.empty = True
+
+ return mem
+
+ # Store details about a high-level memory to the memory map
+ # This is called when a user edits a memory in the UI
+ def set_memory(self, mem):
+ # Get a low-level memory object mapped to the image
+
+ _mem = self._memobj.memory[mem.number]
+
+ cbyte = mem.number / 8
+ cbit = 7 - (mem.number % 8)
+
+ if mem.empty:
+ self._memobj.csetflag[cbyte].c[cbit] = 1
+ self._memobj.cskipflag[cbyte].c[cbit] = 1
+ return
+
+ self._memobj.csetflag[cbyte].c[cbit] = 0
+ self._memobj.cskipflag[cbyte].c[cbit] = 1 if (mem.skip == "S") else 0
+
+ _mem.set_raw("\x00" * 32)
+
+ _mem.freq = mem.freq / 100 # Convert to low-level frequency
+ _mem.offset = mem.offset / 100 # Convert to low-level frequency
+
+ _mem.name = mem.name.ljust(7)[:7] # Store the alpha tag
+ _mem.duplex = DUPLEXES.index(mem.duplex)
+
+
+ try:
+ _mem.channel_width = MODES.index(mem.mode)
+ except ValueError:
+ _mem.channel_width = 0
+
+ ((txmode, txtone, txpol),
+ (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
+
+ _mem.txtmode = TMODES.index(txmode)
+ _mem.rxtmode = TMODES.index(rxmode)
+
+ if txmode == "Tone":
+ _mem.txtone = TONES.index(txtone)
+ elif txmode == "DTCS":
+ self._set_dcs_index(_mem,'tx',chirp_common.ALL_DTCS_CODES.index(txtone))
+
+ if rxmode == "Tone":
+ _mem.rxtone = TONES.index(rxtone)
+ elif rxmode == "DTCS":
+ self._set_dcs_index(_mem, 'rx', chirp_common.ALL_DTCS_CODES.index(rxtone))
+
+ #_mem.txinv = txpol == "N"
+ #_mem.rxinv = rxpol == "N"
+
+
+ if mem.power:
+ _mem.power = POWER_LEVELS.index(mem.power)
+ else:
+ _mem.power = 0
+
+ def _get_settings(self):
+ _settings = self._memobj.settings
+ _freqrange = self._memobj.freqrange
+ _slabel = self._memobj.slabel
+
+ basic = RadioSettingGroup("basic","Global Settings")
+ freqrange = RadioSettingGroup("freqrange","Frequency Ranges")
+ top = RadioSettingGroup("top","All Settings",basic,freqrange)
+ settings = RadioSettings(top)
+
+ def _filter(name):
+ filtered = ""
+ for char in str(name):
+ if char in chirp_common.CHARSET_ASCII:
+ filtered += char
+ else:
+ filtered += ""
+ return filtered
+
+ val = RadioSettingValueString(0,7,_filter(_slabel.startname))
+ rs = RadioSetting("startname","Startup Label",val)
+ basic.append(rs)
+
+ rs = RadioSetting("bg_color","LCD Color",
+ RadioSettingValueList(BGCOLOR_LIST, BGCOLOR_LIST[_settings.bg_color]))
+ basic.append(rs)
+
+ rs = RadioSetting("bg_brightness","LCD Brightness",
+ RadioSettingValueList(BGBRIGHT_LIST, BGBRIGHT_LIST[_settings.bg_brightness]))
+ basic.append(rs)
+
+ rs = RadioSetting("squelch","Squelch Level",
+ RadioSettingValueList(SQUELCH_LIST, SQUELCH_LIST[_settings.squelch]))
+ basic.append(rs)
+
+ rs = RadioSetting("timeout_timer","Timeout Timer (TOT)",
+ RadioSettingValueList(TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout_timer]))
+ basic.append(rs)
+
+ rs = RadioSetting("auto_power_off","Auto Power Off (APO)",
+ RadioSettingValueList(APO_LIST, APO_LIST[_settings.auto_power_off]))
+ basic.append(rs)
+
+ rs = RadioSetting("voice_prompt","Beep Prompt",
+ RadioSettingValueList(BEEP_LIST, BEEP_LIST[_settings.voice_prompt]))
+ basic.append(rs)
+
+ rs = RadioSetting("tbst_freq","Tone Burst Frequency",
+ RadioSettingValueList(TBSTFREQ_LIST, TBSTFREQ_LIST[_settings.tbst_freq]))
+ basic.append(rs)
+
+ rs = RadioSetting("choose_tx_power","Max Level of TX Power",
+ RadioSettingValueList(TXPWR_LIST, TXPWR_LIST[_settings.choose_tx_power]))
+ basic.append(rs)
+
+ (flow,fhigh) = self.valid_freq[0]
+ flow /= 1000
+ fhigh /= 1000
+ fmidrange = (fhigh- flow)/2000
+
+ rs = RadioSetting("txrangelow","TX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
+ flow + fmidrange,
+ int(_freqrange.txrangelow)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("txrangehi","TX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
+ fhigh,
+ int(_freqrange.txrangehi)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("rxrangelow","RX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
+ flow+fmidrange,
+ int(_freqrange.rxrangelow)/10))
+ freqrange.append(rs)
+
+ rs = RadioSetting("rxrangehi","RX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
+ fhigh,
+ int(_freqrange.rxrangehi)/10))
+ freqrange.append(rs)
+
+ return settings
+
+ def get_settings(self):
+ try:
+ return self._get_settings()
+ except:
+ import traceback
+ LOG.error( "failed to parse settings")
+ traceback.print_exc()
+ return None
+
+ def set_settings(self,settings):
+ _settings = self._memobj.settings
+ for element in settings:
+ if not isinstance(element,RadioSetting):
+ self.set_settings(element)
+ continue
+ else:
+ try:
+ name = element.get_name()
+
+ if name in ["txrangelow","txrangehi","rxrangelow","rxrangehi"]:
+ LOG.debug( "setting %s = %s" % (name,int(element.value)*10))
+ setattr(self._memobj.freqrange,name,int(element.value)*10)
+ continue
+
+ if name in ["startname"]:
+ LOG.debug( "setting %s = %s" % (name, element.value))
+ setattr(self._memobj.slabel,name,element.value)
+ continue
+
+ obj = _settings
+ setting = element.get_name()
+
+ if element.has_apply_callback():
+ LOG.debug( "using apply callback")
+ element.run_apply_callback()
+ else:
+ 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):
+ return cls._file_ident in filedata[16:27]
+
+(a)directory.register
+class Th9000220Radio(Th9000Radio):
+ """TYT TH-9000 220"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_220"
+ BAUD_RATE = 9600
+ _file_ident = "TH9000 220"
+ valid_freq = [(220000000, 260000000)]
+
+(a)directory.register
+class Th9000144Radio(Th9000220Radio):
+ """TYT TH-9000 144"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_144"
+ BAUD_RATE = 9600
+ _file_ident = "TH9000 144"
+ valid_freq = [(138000000, 152000000)]
+
+(a)directory.register
+class Th9000440Radio(Th9000220Radio):
+ """TYT TH-9000 440"""
+ VENDOR = "TYT"
+ MODEL = "TH9000_440"
+ BAUD_RATE = 9600
+ _file_ident = "TH9000 440"
+ valid_freq = [(400000000, 450000000)]
3
7
[chirp_devel] [ft2900] Fix checksum calc for Euro version of FT-2900/FT-1900 (#2501)
by Richard Cochran 27 Apr '15
by Richard Cochran 27 Apr '15
27 Apr '15
# HG changeset patch
# User Richard Cochran <ag6qr(a)sonic.net>
# Date 1430192545 25200
# Mon Apr 27 20:42:25 2015 -0700
# Node ID cb249df8d5744091c373d0a887ac3b148f1e65a1
# Parent 673eef6f99ad71ba4efc1a3a067068d91210b2d4
[ft2900] Fix checksum calc for Euro version of FT-2900/FT-1900 (#2501)
This patch fixes the checksum calculation for the European versions
of the FT-2900/FT-1900 radio. The US and Euro versions have different
IDBLOCK strings. It turns out that the radio's checksum algorithm
includes the IDBLOCK data as it calculates the checksum, but the
original CHIRP ft2900 driver code hadn't taken this into account.
The differences in the IDBLOCK strings were causing checksum
mismatch.
diff -r 673eef6f99ad -r cb249df8d574 chirp/drivers/ft2900.py
--- a/chirp/drivers/ft2900.py Tue Apr 07 21:20:31 2015 -0700
+++ b/chirp/drivers/ft2900.py Mon Apr 27 20:42:25 2015 -0700
@@ -35,7 +35,7 @@
LOG.debug("got echo\n%s\n" % util.hexprint(echo))
ACK = "\x06"
-INITIAL_CHECKSUM = 73
+INITIAL_CHECKSUM = 0
def _download(radio):
@@ -100,9 +100,12 @@
# compute checksum
cs = INITIAL_CHECKSUM
+ for byte in radio.IDBLOCK:
+ cs += ord(byte)
for byte in data:
cs += ord(byte)
LOG.debug("calculated checksum is %x\n" % (cs & 0xff))
+ LOG.debug("Radio sent checksum is %x\n" % ord(chunk[0]))
if (cs & 0xff) != ord(chunk[0]):
raise Exception("Failed checksum on read.")
@@ -133,6 +136,8 @@
block = 0
cs = INITIAL_CHECKSUM
+ for byte in radio.IDBLOCK:
+ cs += ord(byte)
while block < (radio.get_memsize() / 32):
data = radio.get_mmap()[block*32:(block+1)*32]
1
0
25 Apr '15
# HG changeset patch
# User Jim Unroe <rock.unroe(a)gmail.com>
# Date 1430007394 14400
# Node ID 485dce3430559079f3e4c0c66e0935b19b719408
# Parent 31a7494c324a347634c30cc3c9bded2ce83eeb21
[termn8r] Add Anytone TERMN-8R Support #2437
Add most "Function Setup" settings that are common to
the OBLTR-8R and TERMN-8R radios.
related to #2437
diff -r 31a7494c324a -r 485dce343055 chirp/drivers/anytone_ht.py
--- a/chirp/drivers/anytone_ht.py Tue Apr 14 20:58:13 2015 -0400
+++ b/chirp/drivers/anytone_ht.py Sat Apr 25 20:16:34 2015 -0400
@@ -98,13 +98,127 @@
#seekto 0x0220;
struct {
- u8 unknown1:6,
+ u8 unknown220:6,
display:2;
- u8 unknown2[19];
- u8 unknown3:3,
+ u8 unknown221:7,
+ upvfomr:1;
+ u8 unknown222:7,
+ dnvfomr:1;
+ u8 unknown223:7,
+ fmvfomr:1;
+ u8 upmrch;
+ u8 dnmrch;
+ u8 unknown226:1,
+ fmmrch:7;
+ u8 unknown227;
+ u8 unknown228:7,
+ fastscano:1; // obltr-8r only
+ u8 unknown229:6,
+ pause:2;
+ u8 unknown22A:5,
+ stop:3;
+ u8 unknown22B:6,
+ backlight:2;
+ u8 unknown22C:6,
+ color:2;
+ u8 unknown22D:6,
+ vdisplay:2;
+ u8 unknown22E;
+ u8 unknown22F:5,
+ pf1key:3;
+ u8 beep:1,
+ alarmoff:1,
+ main:1,
+ radio:1,
+ unknown230:1,
+ allband:1,
+ elimtail:1,
+ monikey:1;
+ u8 fastscan:1, // termn-8r only
+ keylock:1,
+ unknown231:2,
+ lwenable:1,
+ swenable:1,
+ fmenable:1,
+ amenable:1;
+ u8 unknown232:3,
+ tot:5;
+ u8 unknown233:7,
+ amvfomr:1;
+ u8 unknown234:3,
apo:5;
+ u8 unknown235:5,
+ pf2key:3; // keylock for obltr-8r
+ u8 unknown236;
+ u8 unknown237:4,
+ save:4;
+ u8 unknown238:5,
+ tbst:3;
+ u8 unknown239:4,
+ voxlevel:4;
+ u8 unknown23A:3,
+ voxdelay:5;
+ u8 unknown23B:5,
+ tail:3;
+ u8 unknown23C;
+ u8 unknown23D:1,
+ ammrch:7;
+ u8 unknown23E:5,
+ vvolume:3;
+ u8 unknown23F:5,
+ fmam:3;
+ u8 unknown240:4,
+ upmrbank:4;
+ u8 unknown241:7,
+ upwork:1;
+ u8 unknown242:7,
+ uplink:1;
+ u8 unknown243:4,
+ dnmrbank:4;
+ u8 unknown244:7,
+ dnwork:1;
+ u8 unknown245:7,
+ downlink:1;
+ u8 unknown246:7,
+ banklink1:1;
+ u8 unknown247:7,
+ banklink2:1;
+ u8 unknown248:7,
+ banklink3:1;
+ u8 unknown249:7,
+ banklink4:1;
+ u8 unknown24A:7,
+ banklink5:1;
+ u8 unknown24B:7,
+ banklink6:1;
+ u8 unknown24C:7,
+ banklink7:1;
+ u8 unknown24D:7,
+ banklink8:1;
+ u8 unknown24E:7,
+ banklink9:1;
+ u8 unknown24F:7,
+ banklink0:1;
+ u8 unknown250:6,
+ noaa:2;
+ u8 unknown251:5,
+ noaach:3;
+ u8 unknown252:6,
+ part95:2;
+ u8 unknown253:3,
+ gmrs:5;
+ u8 unknown254:5,
+ murs:3;
+ u8 unknown255:5,
+ amsql:3;
} settings;
+#seekto 0x0246;
+struct {
+ u8 unused:7,
+ bank:1;
+} banklink[10];
+
#seekto 0x03E0;
struct {
char line1[6];
@@ -250,18 +364,43 @@
_finish(radio)
-APO = ['Off', '30 Min', '1 Hour', '2 Hours']
+APO = ['Off', '30 Minutes', '1 Hour', '2 Hours']
+BACKLIGHT = ['Off', 'On', 'Auto']
BCLO = ['Off', 'Repeater', 'Busy']
CHARSET = chirp_common.CHARSET_ASCII
-DISPLAY = ['Frequency', 'Channel', 'Name']
+COLOR = ['Blue', 'Orange', 'Purple']
+DISPLAY = ['Frequency', 'N/A', 'Name']
DUPLEXES = ['', 'N/A', '-', '+', 'split', 'off']
-MODES = ["FM", "NFM"]
+GMRS = ['GMRS %s' % x for x in range(1, 8)] + \
+ ['GMRS %s' % x for x in range(15, 23)] + \
+ ['GMRS Repeater %s' % x for x in range(15, 23)]
+MAIN = ['Up', 'Down']
+MODES = ['FM', 'NFM']
+MONI = ['Squelch Off Momentarily', 'Squelch Off']
+MRBANK = ['Bank %s' % x for x in range(1, 10)] + ['Bank 0']
+MURS = ['MURS %s' % x for x in range(1, 6)]
+NOAA = ['Weather Off', 'Weather On', 'Weather Alerts']
+NOAACH = ['WX %s' % x for x in range(1, 8)]
+PART95 = ['Normal(Part 90)', 'GMRS(Part 95A)', 'MURS(Part 95J)']
+PAUSE = ['%s Seconds (TO)' % x for x in range(5, 20, 5)] + ['2 Seconds (CO)']
+PFKEYT = ['Off', 'VOLT', 'CALL', 'FHSS', 'SUB PTT', 'ALARM', 'MONI']
+PFKEYO = ['Off', 'VOLT', 'CALL', 'SUB PTT', 'ALARM', 'MONI']
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5),
chirp_common.PowerLevel("Mid", watts=2),
chirp_common.PowerLevel("Low", watts=1)]
+SAVE = ['Off', '1:2', '1:3', '1:5', '1:8', 'Auto']
SQUELCH = ['%s' % x for x in range(0, 10)]
+STOP = ['%s Seconds' % x for x in range(0, 4)] + ['Manual']
+TAIL = ['Off', '120 Degree', '180 Degree', '240 Degree']
+TBST = ['Off', '1750 Hz', '2100 Hz', '1000 Hz', '1450 Hz']
TMODES = ['', 'Tone', 'DTCS', '']
TONES = [62.5] + list(chirp_common.TONES)
+TOT = ['Off'] + ['%s Seconds' % x for x in range(10, 280, 10)]
+VDISPLAY = ['Frequency/Channel', 'Battery Voltage', 'Off']
+VFOMR = ["VFO", "MR"]
+VOXLEVEL = ['Off'] + ['%s' % x for x in range(1, 11)]
+VOXDELAY = ['%.1f Seconds' % (0.1 * x) for x in range(5, 31)]
+WORKMODE = ["Channel", "Bank"]
@directory.register
@@ -511,11 +650,225 @@
DISPLAY[_settings.display]))
cfg_grp.append(rs)
+ rs = RadioSetting("upvfomr", "Up VFO/MR",
+ RadioSettingValueList(VFOMR,
+ VFOMR[_settings.upvfomr]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("dnvfomr", "Down VFO/MR",
+ RadioSettingValueList(VFOMR,
+ VFOMR[_settings.dnvfomr]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("upwork", "Up Work Mode",
+ RadioSettingValueList(WORKMODE,
+ WORKMODE[_settings.upwork]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("upmrbank", "Up MR Bank",
+ RadioSettingValueList(MRBANK,
+ MRBANK[_settings.upmrbank]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("upmrch", "Up MR Channel",
+ RadioSettingValueInteger(0, 200, _settings.upmrch))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("dnwork", "Down Work Mode",
+ RadioSettingValueList(WORKMODE,
+ WORKMODE[_settings.dnwork]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("dnmrbank", "Down MR Bank",
+ RadioSettingValueList(MRBANK,
+ MRBANK[_settings.dnmrbank]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("dnmrch", "Down MR Channel",
+ RadioSettingValueInteger(0, 200, _settings.dnmrch))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("main", "Main",
+ RadioSettingValueList(MAIN,
+ MAIN[_settings.main]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("pause", "Scan Pause Time",
+ RadioSettingValueList(PAUSE,
+ PAUSE[_settings.pause]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("stop", "Function Keys Stop Time",
+ RadioSettingValueList(STOP,
+ STOP[_settings.stop]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("backlight", "Backlight",
+ RadioSettingValueList(BACKLIGHT,
+ BACKLIGHT[_settings.backlight]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("color", "Backlight Color",
+ RadioSettingValueList(COLOR,
+ COLOR[_settings.color]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("vdisplay", "Vice-Machine Display",
+ RadioSettingValueList(VDISPLAY,
+ VDISPLAY[_settings.vdisplay]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("voxlevel", "Vox Level",
+ RadioSettingValueList(VOXLEVEL,
+ VOXLEVEL[_settings.voxlevel]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("voxdelay", "Vox Delay",
+ RadioSettingValueList(VOXDELAY,
+ VOXDELAY[_settings.voxdelay]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("tot", "Time Out Timer",
+ RadioSettingValueList(TOT,
+ TOT[_settings.tot]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("tbst", "Tone Burst",
+ RadioSettingValueList(TBST,
+ TBST[_settings.tbst]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("monikey", "MONI Key Function",
+ RadioSettingValueList(MONI,
+ MONI[_settings.monikey]))
+ cfg_grp.append(rs)
+
+ if self.MODEL == "TERMN-8R":
+ rs = RadioSetting("pf1key", "PF1 Key Function",
+ RadioSettingValueList(PFKEYT,
+ PFKEYT[_settings.pf1key]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("pf2key", "PF2 Key Function",
+ RadioSettingValueList(PFKEYT,
+ PFKEYT[_settings.pf2key]))
+ cfg_grp.append(rs)
+
+ if self.MODEL == "OBLTR-8R":
+ rs = RadioSetting("pf1key", "PF1 Key Function",
+ RadioSettingValueList(PFKEYO,
+ PFKEYO[_settings.pf1key]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("fmam", "PF2 Key Function",
+ RadioSettingValueList(PFKEYO,
+ PFKEYO[_settings.fmam]))
+ cfg_grp.append(rs)
+
rs = RadioSetting("apo", "Automatic Power Off",
RadioSettingValueList(APO,
APO[_settings.apo]))
cfg_grp.append(rs)
+ rs = RadioSetting("save", "Power Save",
+ RadioSettingValueList(SAVE,
+ SAVE[_settings.save]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("tail", "Tail Eliminator Type",
+ RadioSettingValueList(TAIL,
+ TAIL[_settings.tail]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("fmvfomr", "FM VFO/MR",
+ RadioSettingValueList(VFOMR,
+ VFOMR[_settings.fmvfomr]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("fmmrch", "FM MR Channel",
+ RadioSettingValueInteger(0, 100, _settings.fmmrch))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("noaa", "NOAA",
+ RadioSettingValueList(NOAA,
+ NOAA[_settings.noaa]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("noaach", "NOAA Channel",
+ RadioSettingValueList(NOAACH,
+ NOAACH[_settings.noaach]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("part95", "PART95",
+ RadioSettingValueList(PART95,
+ PART95[_settings.part95]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("gmrs", "GMRS",
+ RadioSettingValueList(GMRS,
+ GMRS[_settings.gmrs]))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("murs", "MURS",
+ RadioSettingValueList(MURS,
+ MURS[_settings.murs]))
+ cfg_grp.append(rs)
+
+ for i in range(0, 9):
+ val = self._memobj.banklink[i].bank
+ rs = RadioSetting("banklink/%i.bank" % i,
+ "Bank Link %i" % (i + 1),
+ RadioSettingValueBoolean(val))
+ cfg_grp.append(rs)
+
+ val = self._memobj.banklink[9].bank
+ rs = RadioSetting("banklink/9.bank", "Bank Link 0",
+ RadioSettingValueBoolean(val))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("allband", "All Band",
+ RadioSettingValueBoolean(_settings.allband))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("alarmoff", "Alarm Function Off",
+ RadioSettingValueBoolean(_settings.alarmoff))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("beep", "Beep",
+ RadioSettingValueBoolean(_settings.beep))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("radio", "Radio",
+ RadioSettingValueBoolean(_settings.radio))
+ cfg_grp.append(rs)
+
+ if self.MODEL == "TERMN-8R":
+ rs = RadioSetting("keylock", "Keylock",
+ RadioSettingValueBoolean(_settings.keylock))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("fastscan", "Fast Scan",
+ RadioSettingValueBoolean(_settings.fastscan))
+ cfg_grp.append(rs)
+
+ if self.MODEL == "OBLTR-8R":
+ # "pf2key" is used for OBLTR-8R "keylock"
+ rs = RadioSetting("pf2key", "Keylock",
+ RadioSettingValueBoolean(_settings.pf2key))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("fastscano", "Fast Scan",
+ RadioSettingValueBoolean(_settings.fastscano))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("uplink", "Up Bank Link Select",
+ RadioSettingValueBoolean(_settings.uplink))
+ cfg_grp.append(rs)
+
+ rs = RadioSetting("downlink", "Down Bank Link Select",
+ RadioSettingValueBoolean(_settings.downlink))
+ cfg_grp.append(rs)
+
#
# OEM info
#
1
0
23 Apr '15
# HG changeset patch
# User Chris Fosnight <chris.fosnight(a)gmail.com>
# Date 1429845173 25200
# Node ID 1b8e689eb0425cf9d83d385e45b78042795a7cba
# Parent 31a7494c324a347634c30cc3c9bded2ce83eeb21
[ft60] Corrects minor typos in UI. Fixes #2535
Changes "CTCSS/DSC/DTMF Settings" to "CTCSS/DCS/DTMF Settings".
Changes Miscellaneous Settings "Bush Channel Lock-Out" to
"Busy Channel Lock-Out".
#2535
diff -r 31a7494c324a -r 1b8e689eb042 chirp/drivers/ft60.py
--- a/chirp/drivers/ft60.py Tue Apr 14 20:58:13 2015 -0400
+++ b/chirp/drivers/ft60.py Thu Apr 23 20:12:53 2015 -0700
@@ -414,7 +414,7 @@
_settings = self._memobj.settings
repeater = RadioSettingGroup("repeater", "Repeater Settings")
- ctcss = RadioSettingGroup("ctcss", "CTCSS/DSC/DTMF Settings")
+ ctcss = RadioSettingGroup("ctcss", "CTCSS/DCS/DTMF Settings")
arts = RadioSettingGroup("arts", "ARTS Settings")
scan = RadioSettingGroup("scan", "Scan Settings")
power = RadioSettingGroup("power", "Power Saver Settings")
@@ -458,7 +458,7 @@
# BCLO
opts = ["OFF", "ON"]
misc.append(RadioSetting(
- "bclo", "Bush Channel Lock-Out",
+ "bclo", "Busy Channel Lock-Out",
RadioSettingValueList(opts, opts[_settings.bclo])))
# BEEP
1
0
Tested changes:
[Jim Unroe <rock.unroe(a)gmail.com>] [termn8r] Add Anytone TERMN-8R Support #2437
Add an "extra" setting to get started
- Squelch (this is a per channel setting)
releated to #2437
Full log:
Started by an SCM change
Building in workspace /var/lib/jenkins/jobs/chirp-test/workspace
[workspace] $ hg showconfig paths.default
[workspace] $ hg pull --rev default
[workspace] $ hg update --clean --rev default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[workspace] $ hg log --rev . --template {node}
[workspace] $ hg log --rev . --template {rev}
[workspace] $ hg log --rev 1406ad325647e30ca51af56adf21cac08a113a92
[workspace] $ hg log --template "<changeset node='{node}' author='{author|xmlescape}' rev='{rev}' date='{date}'><msg>{desc|xmlescape}</msg><added>{file_adds|stringify|xmlescape}</added><deleted>{file_dels|stringify|xmlescape}</deleted><files>{files|stringify|xmlescape}</files><parents>{parents}</parents></changeset>\n" --rev default:0 --follow --prune 1406ad325647e30ca51af56adf21cac08a113a92
No emails were triggered.
[workspace] $ /bin/sh -xe /tmp/hudson3008256353882603673.sh
+ ./run_all_tests.sh
test_bit_array (tests.unit.test_bitwise.TestBitType) ... ok
test_bit_array_fail (tests.unit.test_bitwise.TestBitType) ... ok
test_bitfield_u16 (tests.unit.test_bitwise.TestBitfieldTypes) ... ok
test_bitfield_u24 (tests.unit.test_bitwise.TestBitfieldTypes) ... ok
test_bitfield_u8 (tests.unit.test_bitwise.TestBitfieldTypes) ... ok
test_bitfield_ul16 (tests.unit.test_bitwise.TestBitfieldTypes) ... ok
test_bitfield_ul24 (tests.unit.test_bitwise.TestBitfieldTypes) ... ok
test_bbcd (tests.unit.test_bitwise.TestBitwiseBCDTypes) ... ok
test_bbcd_array (tests.unit.test_bitwise.TestBitwiseBCDTypes) ... ok
test_lbcd (tests.unit.test_bitwise.TestBitwiseBCDTypes) ... ok
test_lbcd_array (tests.unit.test_bitwise.TestBitwiseBCDTypes) ... ok
test_int_array (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_u16 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_u24 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_u32 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_u8 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_ul16 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_ul24 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_type_ul32 (tests.unit.test_bitwise.TestBitwiseBaseIntTypes) ... ok
test_char (tests.unit.test_bitwise.TestBitwiseCharTypes) ... ok
test_string (tests.unit.test_bitwise.TestBitwiseCharTypes) ... ok
test_string_invalid_chars (tests.unit.test_bitwise.TestBitwiseCharTypes) ... ok
test_string_wrong_length (tests.unit.test_bitwise.TestBitwiseCharTypes) ... ok
test_comment_cppstyle (tests.unit.test_bitwise.TestBitwiseComments) ... ok
test_comment_inline_cppstyle (tests.unit.test_bitwise.TestBitwiseComments) ... ok
test_missing_semicolon (tests.unit.test_bitwise.TestBitwiseErrors) ... ok
test_seek (tests.unit.test_bitwise.TestBitwiseSeek) ... ok
test_seekto (tests.unit.test_bitwise.TestBitwiseSeek) ... ok
test_struct_one_element (tests.unit.test_bitwise.TestBitwiseStructTypes) ... ok
test_struct_two_elements (tests.unit.test_bitwise.TestBitwiseStructTypes) ... ok
test_struct_writes (tests.unit.test_bitwise.TestBitwiseStructTypes) ... ok
split_tone_encode_test_cross_dtcs_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_cross_none_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_cross_none_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_cross_tone_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_cross_tone_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_none (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
split_tone_encode_test_tsql (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_dtcs_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_dtcs_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_none_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_none_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_tone_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_cross_tone_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_dtcs (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_none (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_tone (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_split_tone_decode_tsql (tests.unit.test_chirp_common.TestSplitTone) ... ok
test_fix_rounded_step_250 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_fix_rounded_step_500 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_fix_rounded_step_750 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_is_12_5 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_is_2_5 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_is_5_0 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_is_6_25 (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_is_fractional_step (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_required_step (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_required_step_fail (tests.unit.test_chirp_common.TestStepFunctions) ... ok
test_format_freq (tests.unit.test_chirp_common.TestUtilityFunctions) ... ok
test_parse_freq_bad (tests.unit.test_chirp_common.TestUtilityFunctions) ... ok
test_parse_freq_decimal (tests.unit.test_chirp_common.TestUtilityFunctions) ... ok
test_parse_freq_whitespace (tests.unit.test_chirp_common.TestUtilityFunctions) ... ok
test_parse_freq_whole (tests.unit.test_chirp_common.TestUtilityFunctions) ... ok
test_ensure_has_calls_almost_full (tests.unit.test_import_logic.DstarTests) ... ok
test_ensure_has_calls_empty (tests.unit.test_import_logic.DstarTests) ... ok
test_ensure_has_calls_partial (tests.unit.test_import_logic.DstarTests) ... ok
test_ensure_has_calls_rptcall_full1 (tests.unit.test_import_logic.DstarTests) ... ok
test_ensure_has_calls_rptcall_full2 (tests.unit.test_import_logic.DstarTests) ... ok
test_ensure_has_calls_urcall_full (tests.unit.test_import_logic.DstarTests) ... ok
test_import_bank (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_dtcs_diffA_dtcs (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_dtcs_diffB_dtcs (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_duplex_negative (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_duplex_too_big_vhf (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_duplex_uhf (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_duplex_vhf (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mem (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mem_with_errors (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mem_with_warnings (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mode_invalid (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mode_valid_am (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_mode_valid_fm (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_name (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_power_closest (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_power_no_dst (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_power_no_src (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_power_same (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_tone_diffA_tsql (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_import_tone_diffB_tsql (tests.unit.test_import_logic.ImportFieldTests) ... ok
test_mapping (tests.unit.test_mappingmodel.TestBaseBank) ... ok
test_mapping_eq (tests.unit.test_mappingmodel.TestBaseBank) ... ok
test_base_class (tests.unit.test_mappingmodel.TestBaseBankModel) ... ok
test_get_name (tests.unit.test_mappingmodel.TestBaseBankModel) ... ok
test_mapping (tests.unit.test_mappingmodel.TestBaseMapping) ... ok
test_mapping_eq (tests.unit.test_mappingmodel.TestBaseMapping) ... ok
test_base_class (tests.unit.test_mappingmodel.TestBaseMappingModel) ... ok
test_get_name (tests.unit.test_mappingmodel.TestBaseMappingModel) ... ok
test_base_class (tests.unit.test_mappingmodel.TestBaseMappingModelIndexInterface) ... ok
test_add_memory_to_mapping (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_get_mapping_memories (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_get_mappings (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_get_memory_mappings (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_get_num_mappings (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_remove_memory_from_mapping (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_remove_memory_from_mapping_no_bank (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_remove_memory_from_mapping_wrong_bank (tests.unit.test_mappingmodel.TestIcomBankModel) ... ok
test_icom_bank (tests.unit.test_mappingmodel.TestIcomBanks) ... ok
test_mapping (tests.unit.test_mappingmodel.TestIcomBanks) ... ok
test_mapping_eq (tests.unit.test_mappingmodel.TestIcomBanks) ... ok
test_add_memory_to_mapping (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_index_bounds (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_mapping_memories (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_mappings (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_memory_index (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_memory_mappings (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_next_mapping_index (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_get_num_mappings (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_remove_memory_from_mapping (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_remove_memory_from_mapping_no_bank (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_remove_memory_from_mapping_wrong_bank (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_set_memory_index (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_set_memory_index_bad_bank (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_set_memory_index_bad_index (tests.unit.test_mappingmodel.TestIcomIndexedBankModel) ... ok
test_auto_tone_mode_cross (tests.unit.test_memedit_edits.TestEdits) ... ok
test_auto_tone_mode_dtcs (tests.unit.test_memedit_edits.TestEdits) ... ok
test_auto_tone_mode_dtcs_pol (tests.unit.test_memedit_edits.TestEdits) ... ok
test_auto_tone_mode_dtcs_rx (tests.unit.test_memedit_edits.TestEdits) ... ok
test_auto_tone_mode_tone (tests.unit.test_memedit_edits.TestEdits) ... ok
test_auto_tone_mode_tsql (tests.unit.test_memedit_edits.TestEdits) ... ok
test_init (tests.unit.test_platform.Win32PlatformTest) ... ok
test_serial_ports_bad_portnames (tests.unit.test_platform.Win32PlatformTest) ... ok
test_serial_ports_sorted (tests.unit.test_platform.Win32PlatformTest) ... ok
test_apply_callback (tests.unit.test_settings.TestSettingContainers) ... ok
test_radio_setting (tests.unit.test_settings.TestSettingContainers) ... ok
test_radio_setting_group (tests.unit.test_settings.TestSettingContainers) ... ok
test_radio_setting_multi (tests.unit.test_settings.TestSettingContainers) ... ok
test_changed (tests.unit.test_settings.TestSettingValues) ... ok
test_radio_setting_value_boolean (tests.unit.test_settings.TestSettingValues) ... ok
test_radio_setting_value_float (tests.unit.test_settings.TestSettingValues) ... ok
test_radio_setting_value_integer (tests.unit.test_settings.TestSettingValues) ... ok
test_radio_setting_value_list (tests.unit.test_settings.TestSettingValues) ... ok
test_radio_setting_value_string (tests.unit.test_settings.TestSettingValues) ... ok
test_validate_callback (tests.unit.test_settings.TestSettingValues) ... ok
test_delete_hole_with_all (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
test_delete_hole_with_all_full (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
test_delete_hole_with_hole (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
test_delete_hole_without_hole (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
test_insert_hole_with_space (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
test_insert_hole_without_space (tests.unit.test_shiftdialog.ShiftDialogTest) ... ok
----------------------------------------------------------------------
Ran 151 tests in 0.097s
OK
Patch 'tip' is OK
Checking for PEP8 regressions...
real 0m11.396s
user 0m9.804s
sys 0m0.199s
================================================
Tests OK
Email was triggered for: Success
Sending email for trigger: Success
1
0
Is there a design reason why RadioSettingValueList() has to be used
for the "mem.extra" settings instead of RadioSettingValueInteger()? Or
is this a bug?
Jim KC9HI
2
1